I have a problem with my code.
I send a number but the format does not working.
My code is the next.
class SetValueClass extends HelperClass {
SetValueClass() : super();
String transformValue({
required double number,
bool? pasa,
}) {
number = 27126677097.04;
SuffixOptions suffixOptions = getSuffixObject(number);
if (pasa != null && pasa) {
print(number);
print(suffixOptions.name);
print(suffixOptions.format);
print(suffixOptions.number);
print(suffixOptions.suffix);
}
final numberFormat = NumberFormat.currency(
locale: 'es_CO',
// customPattern: '',
symbol: suffixOptions.suffix,
);
numberFormat.maximumFractionDigits = suffixOptions.format['maximumFractionDigits']!;
numberFormat.minimumFractionDigits = suffixOptions.format['minimumFractionDigits']!;
return numberFormat.format(number);
}
}
The final result i want, is parse 27126677097.04 to 27,127 MM, but y received this...
27.126.677.097,04 MM
The symbol is working successfully, but, ¿how can i compact this number and return 27,127 MM?
Note: The locale es_CO is the code of Colombia.
Thanks!
After some research, I realized that the es_CO did not work the way I needed it to, so I decided to go with the following solution.
Create a function.
String _defineValue({required number}) {
/**
* Valor en el rango de cuatrillones.
* (C)
*/
if (number > 1e+24) {
return "${(number / 1e+24).toStringAsFixed(2)} C";
}
/**
* Valor en el rango de miles de trillones.
* (MT)
*/
if (number > 1e+21) {
return "${(number / 1e+21).toStringAsFixed(3)} MT";
}
/**
* Valor en el rango de trillones.
* (T)
*/
if (number > 1e+18) {
return "${(number / 1e+18).toStringAsFixed(2)} T";
}
/**
* Valor en el rango de miles de billones.
* (MB)
*/
if (number > 1e+15) {
return "${(number / 1e+15).toStringAsFixed(3)} MB";
}
/**
* Valor en el rango de billones.
* (MB)
*/
if (number > 1e+12) {
return "${(number / 1e+12).toStringAsFixed(2)} B";
}
/**
* Valor en el rango de miles de millones.
* (MM)
*/
if (number > 1e+9) {
return "${(number / 1e+9).toStringAsFixed(3)} MM";
}
/**
* Valor en el rango de millones.
* (M)
*/
if (number > 1e+6) {
return "${(number / 1e+6).toStringAsFixed(2)} M";
}
/**
* Valor en el rango de inferior
*/
return "${(number / 1).toStringAsFixed(0)}";
}
This function gets a number and return a String with the logic required in my case.
Related
I'm trying to run a really old Scala code for my game but I couldn't run it due to some of the libraries are expired so they don't work.
import collection.mutable
import scala.concurrent.ops._
/**
* Put numbers in a NxN board
* from 1 to N*N
* scala SayiYerlestirmece N
*/
object SayiYerlestirmece {
private var current : Int = 0;
def main(args: Array[String]) {
var size = 5;
//if board size is given use.
if (args.nonEmpty){
size = Integer.parseInt(args(0));
}
var i = 0;
for (x <- 0 until size ){
for(y <- 0 until size){
//run every initial states in parallel.
spawn{
val arr = new Array[Int](size * size);
arr(i) = 1;
//create initial states
val initialList :List[Model] = List(new Model(arr,size,x,y));
solve(initialList);
}
i+=1;
}
}
}
/**
* solve problem recursively
* #param modelList - next states
*/
def solve(modelList: List[Model]){
modelList.foreach(p => {
if (p.completed){
current+=1;
println(p);
println(current);
}
solve(p.nextStepList());
});
}
}
/**
*
* #param data - current state of board
* #param size - size of board 5x5 10x10 etc
* #param x - current x position on the board
* #param y - current y position on the board
*/
class Model(data:Array[Int], size:Int, x:Int, y:Int) {
/**
* convert multi dimensional x,y index to one dimensional index.
* #param size - size of board
* #param x - current x position
* #param y - current y position
* #return - corresponding array index
*/
def xy2Idx(size:Int, x:Int, y:Int): Int = {
if ( x < 0 || y < 0 || x >= size || y >= size)
-1
else
size * x + y;
}
/**
* convert one dimensional array index to multi dimensional x,y index
* #param size
* #param idx
* #return
*/
def idx2Xy(size:Int, idx:Int):(Int,Int) = {
return (idx/size,idx%size);
}
/**
* Checks whether game is completed or not
* #return true if is game completed else false
*/
def completed() :Boolean = { data(xy2Idx(size,x,y)) == size * size };
/**
* Position of next available empty cells.
* #return - list of index of empty cells.
*/
def nextStepIdxList():List[Int] = {
return mutable.MutableList(
xy2Idx(size,x+3,y),
xy2Idx(size,x-3,y),
xy2Idx(size,x,y+3),
xy2Idx(size,x,y-3),
xy2Idx(size,x+2,y+2),
xy2Idx(size,x-2,y+2),
xy2Idx(size,x+2,y-2),
xy2Idx(size,x-2,y-2)
).filter(p => p > -1 && data(p) == 0).toList; //filter out of bounds and non-empty cells
}
/**
* Next states of board. These are derived from indexes
* which are returned by nextStepIdxList() function.
* #return - Next possible states of the board
*/
def nextStepList():List[Model] = {
var modelList = mutable.MutableList[Model]()
nextStepIdxList().foreach( p => {
val newArr = data.clone();
newArr(p) = data(xy2Idx(size,x,y)) + 1;
modelList += new Model(newArr,size,idx2Xy(size,p)._1, idx2Xy(size,p)._2);
});
return modelList.sortWith(_.nextStepSize() < _.nextStepSize()).toList; // sorts board states by least next step size
}
/**
* Available cell count at next step.
* This value is used to determine next move.
* #return - Available empty cell count
*/
def nextStepSize():Int = {
return nextStepIdxList().size;
}
override def toString(): String = {
val sb = new StringBuilder();
data.indices.foreach(p =>{
if (p % size == 0)
sb.append("\n");
else
sb.append(",");
sb.append(data(p));
});
return sb.toString();
}
}
When I run it it says import scala.concurrent.ops._ is not working when I delete it, it can't find spawn. When I added another version of spawn it doesn't work. How can I run this code?
scala.concurrent.ops._ have been deprecated in favour of Future by Futures, promises and execution contexts #200 in Scala 2.10.0 in year 2012 and removed entirely in 2.11.0. Try replacing
spawn {
// expressions
}
with
import concurrent.ExecutionContext.Implicits.global
Future {
// expressions
}
As a side note, concurrency and mutable collections are usually an unsafe combination due to race conditions etc.
I have succesfully changed the aspectRatioConfig for a html5smartimage component to replace the default "Free crop" with a custom aspect ratio, however I'm struggling to find a way to select my custom aspect ratio as the default selection when cropping images. Essentially, I want to default to my custom aspect ratio rather than have the user select this from the list of aspect ratios (in this case there is only one value). Is this possible? Can anyone assist?
here is my code to replace the free crop, with my custom config, I was thinking that setting the checked value of the available aspect ratios (in initComponent below) would trigger the selected event, but it doesn't appear too. How can I default the selected aspect ratio ? Or perhaps trigger the selected event ?
(function($, undefined) {
/**
* Determine the scaling ratio the image component might have applied
*
* #param data contains `imgWidth` and `imgHeight` keys
* #returns {number} is the scaling ratio, 1 if no scaling was applied.
*/
function getScalingRatio(data) {
var scaleRatio = 1;
if (data.imgWidth > 1280 || data.imgHeight > 1280) {
if (data.imgWidth > 1280) {
scaleRatio = 1280 / data.imgWidth;
} else {
scaleRatio = 1280 / data.imgHeight;
}
}
return scaleRatio;
}
/**
* Wrapper for the HTML5 aspect ratio aware
*
* #type {void|*}
*/
window.CQTG = $.extend(window.CQTG || {}, {
Html5SmartImageWrapper: CQ.Ext.extend(CQ.Ext.Panel, {
/**
* Initialize panel
*
* #param originalConfig
*/
constructor: function (originalConfig) {
// initialize the container so we get some useful information
CQTG.Html5SmartImageWrapper.superclass.constructor.call(this, $.extend({}, originalConfig, {
layout: 'fit',
bodyStyle: 'padding: 0;',
items: {
title: null,
header: false,
frameHeader: false,
border: false
}
}));
// find the data path for this container instance
originalConfig.imagePath = this.findParentByType('dialog').responseScope.path;
// get rid of panel and replace with smart image
this.removeAll();
// add add html5 smart image to the container
this.add(new CQTG.Html5SmartImage(originalConfig));
}
}),
Html5SmartImage: CQ.Ext.extend(CQ.html5.form.SmartImage, {
/**
* Initialize data-members
*
* #param config
*/
constructor: function (config) {
config = config || {};
var aInfo = this.getAspectRatioString(config.imagePath);
// setup some default values
config = CQ.Util.applyDefaults(config, {
"cropConfig": {
"aspectRatios": {
"newsholeCrop": {
"text": "Newshole crop",
"value": aInfo.aspectRatio
}
}
}
}
);
CQTG.Html5SmartImage.superclass.constructor.call(this, config);
// first tool is the crop (rest has been disabled)
this.imageToolDefs[0].cropMinWidth = aInfo.minWidth;
this.imageToolDefs[0].cropMinHeight = aInfo.minHeight;
},
/**
* Retrieve the bounding box and concoct a aspect ratio based on it
*
* There is some additional magic here because the image component uses a rendition,
* not the original and bases all its pixel calculations on it without scaling it back
* up. That is why the scaleRatio is determined
*/
getAspectRatioString: function (cmpPath) {
var
boundingBoxLocation = cmpPath.substring(0, cmpPath.lastIndexOf('/')),
data = CQ.Util.eval(boundingBoxLocation + ".crop.json"),
roundX = Math.ceil(data.aspectX * 100),
roundY = Math.ceil(data.aspectY * 100),
scaleRatio = getScalingRatio(data)
;
return {
aspectRatio: (roundX + "," + roundY),
minWidth: Math.ceil(data.minCropWidth * scaleRatio),
minHeight: Math.ceil(data.minCropHeight * scaleRatio)
};
},
initComponent: function () {
CQTG.Html5SmartImage.superclass.initComponent.call(this);
var cropTool = null;
CQ.Ext.each(this.imageToolDefs, function(tool){
if(tool.toolId == "smartimageCrop"){
cropTool = tool;
}
});
var userInterface = cropTool.userInterface;
this.on("loadimage", function(){
var aRatios = userInterface.aspectRatioMenu.findByType("menucheckitem");
if(!aRatios[0].checked || !aRatios[0].initialConfig.checked){
aRatios[0].checked = true;
aRatios[0].initialConfig.checked = true;
}
});
}
})
});
CQ.Ext.reg("ffx.html5smartimage", CQTG.Html5SmartImageWrapper);
})($CQ);
Sorted this out myself.....I actually wasn't too far off when I posted this initially. I just needed to find the correct event to trigger the aspectRatio update. Turns out to be I needed to override smartImage.js toolClicked() method and reset the aspectRatio via setAspectRatioUI, then re-trigger onRatioChanged
(function($, undefined) {
/**
* Determine the scaling ratio the image component might have applied
*
* #param data contains `imgWidth` and `imgHeight` keys
* #returns {number} is the scaling ratio, 1 if no scaling was applied.
*/
function getScalingRatio(data) {
var scaleRatio = 1;
if (data.imgWidth > 1280 || data.imgHeight > 1280) {
if (data.imgWidth > 1280) {
scaleRatio = 1280 / data.imgWidth;
} else {
scaleRatio = 1280 / data.imgHeight;
}
}
return scaleRatio;
}
/**
* Wrapper for the HTML5 aspect ratio aware
*
* #type {void|*}
*/
window.CQTG = $.extend(window.CQTG || {}, {
Html5SmartImageWrapper: CQ.Ext.extend(CQ.Ext.Panel, {
/**
* Initialize panel
*
* #param originalConfig
*/
constructor: function (originalConfig) {
// initialize the container so we get some useful information
CQTG.Html5SmartImageWrapper.superclass.constructor.call(this, $.extend({}, originalConfig, {
layout: 'fit',
bodyStyle: 'padding: 0;',
items: {
title: null,
header: false,
frameHeader: false,
border: false
}
}));
// find the data path for this container instance
originalConfig.imagePath = this.findParentByType('dialog').responseScope.path;
// get rid of panel and replace with smart image
this.removeAll();
// add add html5 smart image to the container
this.add(new CQTG.Html5SmartImage(originalConfig));
}
}),
Html5SmartImage: CQ.Ext.extend(CQ.html5.form.SmartImage, {
/**
* Initialize data-members
*
* #param config
*/
constructor: function (config) {
config = config || {};
var aInfo = this.getAspectRatioString(config.imagePath);
// setup some default values
config = CQ.Util.applyDefaults(config, {
"cropConfig": {
"aspectRatios": {
"newsholeCrop": {
"text": "Newshole crop",
"value": aInfo.aspectRatio,
"checked": true
}
}
}
}
);
CQTG.Html5SmartImage.superclass.constructor.call(this, config);
// first tool is the crop (rest has been disabled)
this.imageToolDefs[0].cropMinWidth = aInfo.minWidth;
this.imageToolDefs[0].cropMinHeight = aInfo.minHeight;
},
/**
* Retrieve the bounding box and concoct a aspect ratio based on it
*
* There is some additional magic here because the image component uses a rendition,
* not the original and bases all its pixel calculations on it without scaling it back
* up. That is why the scaleRatio is determined
*/
getAspectRatioString: function (cmpPath) {
var
boundingBoxLocation = cmpPath.substring(0, cmpPath.lastIndexOf('/')),
data = CQ.Util.eval(boundingBoxLocation + ".crop.json"),
roundX = Math.ceil(data.aspectX * 100),
roundY = Math.ceil(data.aspectY * 100),
scaleRatio = getScalingRatio(data)
;
return {
aspectRatio: (roundX + "," + roundY),
minWidth: Math.ceil(data.minCropWidth * scaleRatio),
minHeight: Math.ceil(data.minCropHeight * scaleRatio)
};
},
/**
* override smartImage toolClicked and set default cropTool to "Newshole crop"
*/
toolClicked: function(cropTool) {
cropTool = null;
CQ.Ext.each(this.imageToolDefs, function(tool){
if(tool.toolId == "smartimageCrop"){
cropTool = tool;
}
});
CQTG.Html5SmartImage.superclass.toolClicked.call(this, cropTool);
var userInterface = cropTool.userInterface;
var aRatios = userInterface.aspectRatioMenu.findByType("menucheckitem");
if(!aRatios[0].checked){
aRatios[0].checked = true;
}
userInterface.setAspectRatioUI(aRatios[0].value);
userInterface.onRatioChanged(aRatios[0].value, aRatios[0].text);
}
})
});
CQ.Ext.reg("ffx.html5smartimage", CQTG.Html5SmartImageWrapper);
})($CQ);
Is there a way to have JSDOC to display variables inside of functions without using the #namespace annotation before the function?
I have made a small test object, but it will only display the inner variable k for the last function, which is declared as a namespace. I thought using the #memberof! annotation and exact #name annotation would display the inner variable in the second method below called diff. I am running jsdoc3 on the command with the options --access "all" --explain.
Here is the code for test object:
(function(){
"use strict";
/**
* #namespace
* #type {{}}
*/
window.myFunctions = {};
/**
* Return the sum
* #param {number} i
* #param {number} j
* #return {number}
*/
window.myFunctions.sum = function(i, j) {
/** #type {number} */
var k = i + j;
return k;
};
/**
* Return the difference i - j
* #param {number} i
* #param {number} j
* #return {number}
*/
window.myFunctions.diff = function(i, j) {
/**
* #inner
* #memberof! window.myFunctions.diff
* #type {number}
* #name window.myFunctions.diff~k
*/
var k = i - j;
return k;
};
/**
* Return the product
* #namespace
* #param {number} i
* #param {number} j
* #return {number}
*/
window.myFunctions.multiply = function(i, j) {
/** #type {number} */
var k = i * j;
return k;
}
});
i noticed your code is within an IIFE, you should read this issue
You need to use the #module and #exports tags.
From the documentation:
If you define your AMD module as a function that returns an object
literal, use the #exports tag to document the module's name. JSDoc
will automatically detect that the object's properties are members of
the module.
So, your code example could be marked up like this:
/**
* functions module.
* #module my/functions
*/
(function(){
"use strict";
/**
* #namespace
* #type {{}}
* #exports my/functions
* #name myFunctions
*/
window.myFunctions = {};
/**
* Return the sum
* #param {number} i
* #param {number} j
* #return {number}
* #exports my/functions
* #name sum
*/
window.myFunctions.sum = function(i, j) {
/**
* #type {number}
* #exports my/functions
* #name k
*/
var k = i + j;
return k;
};
/**
* Return the difference i - j
* #param {number} i
* #param {number} j
* #return {number}
* #exports my/functions
* #name diff
*/
window.myFunctions.diff = function(i, j) {
/**
* #inner
* #memberof! window.myFunctions.diff
* #type {number}
* #name window.myFunctions.diff~k
* #exports my/functions
*/
var k = i - j;
return k;
};
/**
* Return the product
* #namespace
* #param {number} i
* #param {number} j
* #return {number}
* #exports my/functions
* #name multiply
*/
window.myFunctions.multiply = function(i, j) {
/**
* #type {number}
* #exports my/functions
* #name k
*/
var k = i * j;
return k;
}
});
I am trying to do a conversor from markdown syntax to Latex and vice versa. But I am facing a problem I can not solve so far. Lets say, we have the following text:
* item
* item
* _Italic_ item
* Item with __nested _italic_ text__
Right now, my lex program would do this to the text:
\begin{itemize}
\item{item}
\item{item}
\item{_Italic_ item}
\item{Item with __nested _italic_ text__}
\end{itemize}
And, if I run the program again on that output, I get:
\begin{itemize}
\item{item}
\item{item}
\item{\textit{Italic} item}
\item{Item with nested \textit{italic} text}}
\end{itemize}
Which is the expected result, But it is supposed to do it in one run.
I want to know if it is possible to indicate Flex to run yylex() again on the output. Reading the documentation I've found something about Reentrant C Scanners and Multiple Input Buffers, but I do not know if that would solve my problem.
What is the best solution? Reentrant scanners, multiple Input buffers, or something more simpler?
I also thought on implementing the function yywrap to tell lex launch the scanner again, but with no luck:
int yywrap(){
if (first_run == 1){
first_run++;
yyin = fopen ("/tmp/out1", "rt");
yyout = fopen("salida", "wt");
if (yyin == NULL) {
printf ("El fichero no se puede abrir\n");
exit (-1);
}
if (yyout == NULL) {
printf ("El fichero no se puede abrir\n");
exit (-1);
}
yyrestart(yyin);
return 0;
} else {
return 1;
}
}
Here is my code:
/*----- Sección de Declaraciones --------------*/
%option case-insensitive
%option debug
%option verbose
%{
#include<stdio.h>
#include<string.h>
int from_italic_text = 0; /* Para saber si venimos de una italic anidada en la bold*/
int from_bold_text = 0;
%}
/* Primitives */
word .+
scstrong "__"|"**"
scem "_"|"*"
list ^"* "|"- "
%x IN_MARKDOWN_LIST
%x BOLD_TEXT_NESTED_ITALIC ITALIC_TEXT
%x BOLD_TEXT ITALIC_TEXT_NESTED_BOLD
%%
/*----- Sección de Reglas ----------------*/
{list} {BEGIN(IN_MARKDOWN_LIST);fprintf(yyout, "\\begin{itemize}\n");}
<IN_MARKDOWN_LIST>{
^\n fprintf(yyout, "\\end{itemize}\n\n");BEGIN(INITIAL); /* si volvemos a detectar línea vacia, hemos acabado el itemize, o no era nada y salimos */
^"* "|"- " /* Eliminar la sintáxis de itemize en markdown */
[^"*"\-\n]+ fprintf(yyout, "\t\\item{%s}\n", yytext); /* Éste es el texto que compone cada línea del itemize */
\n yylineno++;BEGIN(IN_MARKDOWN_LIST); /* Si detectamos salto de línea, aumentar el número de línea, y seguimos comprobando dentro de IN_MARKDOWN_LIST buscando más items*/
}
{scstrong} { BEGIN(BOLD_TEXT_NESTED_ITALIC); /* Comienzo de un strong __....*/}
<BOLD_TEXT_NESTED_ITALIC>{
"__" fprintf(yyout, "}");BEGIN(INITIAL); // Eat the end and exit
"_" BEGIN(ITALIC_TEXT); // Hay otro elemento anidado, un italic, pasamos a procesarlo
[^_\n]* {
if (from_italic_text)
fprintf(yyout, "%s", yytext); // Texto a continuación del italic
else
fprintf(yyout, "\\textbf{%s", yytext);
}
\n BEGIN(INITIAL);
}
<ITALIC_TEXT>{
[^_\n]* fprintf(yyout, "\\textit{%s", yytext);
"_" fprintf(yyout, "}"); BEGIN(BOLD_TEXT_NESTED_ITALIC); from_italic_text = 1; /* Llegado al último _, cerramos }, volvemos al stado BOLD_TEXT y ponemos from_italic_text a 1 para saber que estuvimos aquí, y no cerra antes de tiempo el \textbf*/
}
{scem} { BEGIN(ITALIC_TEXT_NESTED_BOLD); /* Comienzo de un strong __....*/}
<ITALIC_TEXT_NESTED_BOLD>{
"_" fprintf(yyout, "}"); BEGIN(INITIAL); // Eat the end and exit
"__" BEGIN(BOLD_TEXT); // Hay otro elemento anidado, un italic, pasamos a procesarlo
[^_\n]* {
if (from_bold_text)
fprintf(yyout, "%s", yytext); // Texto a continuación del italic
else
fprintf(yyout, "\\textit{%s", yytext);
}
\n BEGIN(INITIAL);
}
<BOLD_TEXT>{
[^_\n]* fprintf(yyout, "\\textbf{%s", yytext);
"__" fprintf(yyout, "}"); BEGIN(ITALIC_TEXT_NESTED_BOLD); from_bold_text = 1; /* Llegado al último _, cerramos }, volvemos al stado BOLD_TEXT y ponemos from_italic_text a 1 para saber que estuvimos aquí, y no cerra antes de tiempo el \textbf*/
}
.|\n {ECHO;}
%%
/*----- Sección de Procedimientos --------*/
int main (int argc, char *argv[]) {
if (argc == 2) {
yyin = fopen (argv[1], "rt");
if (yyin == NULL) {
printf ("El fichero %s no se puede abrir\n", argv[1]);
exit (-1);
}
} else
yyin = stdin;
yyout = fopen("/tmp/out1", "wt");
if (yyout == NULL) {
printf ("El fichero %s no se puede abrir\n", argv[1]);
exit (-1);
}
yylex ();
return 0;
}
Instead of doing multiple traversals you do it with a single traversal and stacks:
%option stack
and change BEGIN state with
yy_push_state(STATE) // ... push current-state; BEGIN STATE
yy_pop_state // instead of BEGIN INITIAL we "return" to prev. state
This way easily have nested commands
{list} { yy_push_state(IN_MARKDOWN_LIST);fprintf(yyout, "\\begin{itemize}\n");}
<IN_MARKDOWN_LIST>{
^\n { yy_pop_state(); fprintf(yyout, "\\end{itemize}\n\n");
"__" { yy_push_state(BOLD); fprintf(yyout, "\\textbf{");
"_" { yy_push_state(ITALIC); fprintf(yyout, "\\textit{");
...
}
<ITALIC>{
"_" { yy_pop_state(); fprintf(yyout, "}"); }
....
. { fprintf(yyout, "%s",yytext);}
}
I finally came out with a solution. I do not know if it is the best, but it worked. I implemented yywrap like this:
int yywrap(){
if (first_run == 1){
first_run++;
fclose(yyout);
fclose(yyin);
yyin = fopen ("/tmp/out", "rt");
yyout = fopen("salida", "wt");
if (yyin == NULL) {
printf ("El fichero no se puede abrir\n");
exit (-1);
}
if (yyout == NULL) {
printf ("El fichero no se puede abrir\n");
exit (-1);
}
return 0;
} else {
return 1;
}
}
int main (int argc, char *argv[]) {
yyin = fopen (argv[1], "rt");
// ...
yyout = fopen("/tmp/out", "wt");
// .....
yylex();
}
Hello I have small issue.
Got such chart with kinda large values. Is there any EASY way to fix that? I would love to see
numbers something as 1e15.
Coming in the 1.0.18 release is a new TickUnitSource implementation that should resolve this problem:
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2014, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* -------------------------
* NumberTickUnitSource.java
* -------------------------
* (C) Copyright 2014, by Object Refinery Limited.
*
* Original Author: David Gilbert (for Object Refinery Limited);
* Contributor(s): -;
*
* Changes
* -------
* 18-Mar-2014 : Version 1 (DG);
*
*/
package org.jfree.chart.axis;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* A tick unit source implementation that returns NumberTickUnit instances
* that are multiples of 1 or 5 times some power of 10.
*
* #since 1.0.18
*/
public class NumberTickUnitSource implements TickUnitSource, Serializable {
private int power = 0;
private int factor = 1;
/**
* Creates a new instance.
*/
public NumberTickUnitSource() {
this.power = 0;
this.factor = 1;
}
#Override
public TickUnit getLargerTickUnit(TickUnit unit) {
TickUnit t = getCeilingTickUnit(unit);
if (t.equals(unit)) {
next();
t = new NumberTickUnit(getTickSize(), getTickLabelFormat(),
getMinorTickCount());
}
return t;
}
#Override
public TickUnit getCeilingTickUnit(TickUnit unit) {
return getCeilingTickUnit(unit.getSize());
}
#Override
public TickUnit getCeilingTickUnit(double size) {
this.power = (int) Math.ceil(Math.log10(size));
this.factor = 1;
return new NumberTickUnit(getTickSize(), getTickLabelFormat(),
getMinorTickCount());
}
private boolean next() {
if (factor == 1) {
factor = 5;
return true;
}
if (factor == 5) {
power++;
factor = 1;
return true;
}
throw new IllegalStateException("We should never get here.");
}
private boolean previous() {
if (factor == 1) {
factor = 5;
power--;
return true;
}
if (factor == 5) {
factor = 1;
return true;
}
throw new IllegalStateException("We should never get here.");
}
private double getTickSize() {
return this.factor * Math.pow(10.0, this.power);
}
private DecimalFormat dfNeg4 = new DecimalFormat("0.0000");
private DecimalFormat dfNeg3 = new DecimalFormat("0.000");
private DecimalFormat dfNeg2 = new DecimalFormat("0.00");
private DecimalFormat dfNeg1 = new DecimalFormat("0.0");
private DecimalFormat df0 = new DecimalFormat("#,##0");
private NumberFormat getTickLabelFormat() {
if (power == -4) {
return dfNeg4;
}
if (power == -3) {
return dfNeg3;
}
if (power == -2) {
return dfNeg2;
}
if (power == -1) {
return dfNeg1;
}
if (power >= 0 && power <= 6) {
return df0;
}
return new DecimalFormat("0.#E0");
}
private int getMinorTickCount() {
if (factor == 1) {
return 10;
} else if (factor == 5) {
return 5;
}
return 0;
}
#Override
public boolean equals(Object obj) {
return (obj instanceof NumberTickUnitSource);
}
}
Add this to your code:
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(new NumberTickUnitSource());