SSJ API Documentation
Stochastic Simulation in Java
Loading...
Searching...
No Matches
Axis.java
1/*
2 * Class: Axis
3 * Description: An axis of a chart and its properties
4 * Environment: Java
5 * Software: SSJ
6 * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
7 * Organization: DIRO, Universite de Montreal
8 * @author
9 * @since
10 *
11 *
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
15 *
16 * http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 *
24 */
25package umontreal.ssj.charts;
26
27import org.jfree.chart.axis.NumberAxis;
28import org.jfree.chart.axis.NumberTickUnit;
29import java.util.Locale;
30import java.util.Formatter;
31import java.lang.Math;
32
42public class Axis {
43
44 public static final boolean ORIENTATION_VERTICAL = false;
45 public static final boolean ORIENTATION_HORIZONTAL = true;
46
47 private NumberAxis axis; // Uses JFreeChart API
48 private boolean orientation; // Vertical or horizontal
49 private double twinAxisPosition = 0;
50
51 private boolean tick0Flag; // if true, label number at origin is shifted
52 // right or above a little to prevent the label from being on the
53 // perpendicular axis.
54
55 private boolean labelsFlag; // true : enable manual labels
56 private String[] labelsName; // Sets manual labels
57 private double[] labelsValue; // Where putting the labels on the axes
58
59 private double fixStep(double minA, double maxA, double step) {
60 // reset step for labels on axes if it is too large or too small
61 final double H = 10;
62 final double inter = maxA - minA;
63 while (step > inter) // step is too large, make it smaller
64 step /= H;
65 while (step < inter / H) // step is too small, make it larger
66 step *= H;
67 if (step > inter / 2)
68 step /= 2;
69 return step;
70 }
71
79 public Axis(NumberAxis inAxis, boolean orientation) {
80 this.axis = inAxis;
81 this.orientation = orientation;
82 this.labelsFlag = false;
83 this.labelsValue = null;
84 this.labelsName = null;
85 inAxis.setAutoRangeIncludesZero(false);
86 inAxis.setLowerMargin(0);
87 inAxis.setUpperMargin(0);
88 axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
89 }
90
98 protected NumberAxis getAxis() {
99 return axis;
100 }
101
107 public String getLabel() {
108 return axis.getLabel();
109 }
110
117 public void setLabel(String label) {
118 axis.setLabel(label);
119 }
120
127 public void setLabels(double tick) {
128 axis.setTickUnit(new NumberTickUnit(tick));
129 labelsFlag = false;
130 }
131
135 public void setLabelsAuto() {
136 axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
137 labelsFlag = false;
138 }
139
143
147 @Deprecated
148 public void enableCustomLabels() {
149 labelsFlag = true;
150 }
151
155 @Deprecated
156 public void disableCustomLabels() {
157 labelsFlag = false;
158 }
159
169 public void setLabels(double[] position) {
170 this.labelsName = null;
171 this.labelsValue = position.clone();
172 double max = max(position);
173 if (axis.getUpperBound() < max)
174 axis.setUpperBound(max);
175 double min = min(position);
176 if (axis.getLowerBound() > min)
177 axis.setLowerBound(min);
178 labelsFlag = true;
179 }
180
192 public void setLabels(double[] position, String[] label) {
193 if (label.length != position.length)
194 throw new IllegalArgumentException("A label is required for each given position");
195 this.labelsName = label.clone();
196 this.labelsValue = position.clone();
197 double max = max(position);
198 if (axis.getUpperBound() < max)
199 axis.setUpperBound(max);
200 double min = min(position);
201 if (axis.getLowerBound() > min)
202 axis.setLowerBound(min);
203 labelsFlag = true;
204 }
205
211 public double getTwinAxisPosition() {
212 return this.twinAxisPosition;
213 }
214
221 public void setTwinAxisPosition(double position) {
222 this.twinAxisPosition = position;
223 }
224
233 public String toLatex(double scale) {
234 Formatter formatter = new Formatter(Locale.US);
235 double maxAxis = Math.max(axis.getRange().getUpperBound(), twinAxisPosition); // valeur du label le plus grand
236 double minAxis = Math.min(axis.getRange().getLowerBound(), twinAxisPosition); // valeur du label le plus petit
237
238 String precisionAffichageLabel; // determine combien de decimales seront affichees pour les labels
239 double pas = axis.getTickUnit().getSize(); // step d'affichage des labels
240 pas = fixStep(minAxis, maxAxis, pas);
241
242 int puissDix; // echelle des valeurs sur l'axe
243 if (Math.log10(pas) < 0)
244 puissDix = (int) Math.log10(pas) - 1;
245 else
246 puissDix = (int) Math.log10(pas);
247
248 // Placement des fleches, on pourrait facilement les rendre personnalisables...
249 String arrowLeftType = "latex";
250 String arrowRightType = "latex";
251 String arrowLeftMargin = "3mm";
252 String arrowRightMargin = "3mm";
253 if (twinAxisPosition == minAxis) {
254 arrowLeftType = "";
255 arrowLeftMargin = "0mm";
256 } else if (twinAxisPosition == maxAxis) {
257 arrowRightType = "";
258 arrowRightMargin = "0mm";
259 }
260
261 // Label pour l'axe, avec eventuellement l'echelle
262 String label = axis.getLabel();
263 if (label == null)
264 label = " ";
265
266 if (puissDix < -2 || puissDix > 2)
267 label = label + " $(10^{" + puissDix + "})$";
268 else
269 puissDix = 0;
270
271 if (orientation) { // on est sur l'axe des abscisses
272
273 // affichage de l'axe
274 formatter.format("\\draw [%s-%s] ([xshift=-%s] %s,0) -- ([xshift=%s] %s,0) node[right] {%s};%n", arrowLeftType,
275 arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition) * scale, arrowRightMargin,
276 (maxAxis - twinAxisPosition) * scale, label);
277
278 if (labelsFlag) { // labels manuels
279 String name;
280 double value;
281 double labelTemp;
282 for (int i = 0; i < labelsValue.length; i++) {
283 value = labelsValue[i];
284 if (labelsName == null) {
285 labelTemp = (value * 100.0 / Math.pow(10, puissDix));
286 // if(labelTemp == (int)(labelTemp))
287 name = Integer.toString((int) (labelTemp / 100.0));
288 // else
289 // name = Double.toString(Math.round(labelTemp)/100.0);
290 } else
291 name = labelsName[i];
292 if (value == twinAxisPosition && tick0Flag)
293 formatter.format("\\draw (%s,00) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%s};%n",
294 (value - twinAxisPosition) * scale, name);
295 else
296 formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%s};%n",
297 (value - twinAxisPosition) * scale, name);
298 }
299 } else { // labels automatiques
300 double labelTemp;
301 double k = twinAxisPosition;
302 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
303 if (labelTemp == (int) (labelTemp))
304 precisionAffichageLabel = "0";
305 else if (labelTemp * 10 == (int) (labelTemp * 10))
306 precisionAffichageLabel = "1";
307 else
308 precisionAffichageLabel = "2";
309 if (tick0Flag)
310 formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%."
311 + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp);
312 else
313 formatter.format(
314 "\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n",
315 (k - twinAxisPosition) * scale, labelTemp);
316 k += pas;
317 while (k <= maxAxis) { // cote positif
318 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
319 if (labelTemp == (int) (labelTemp))
320 precisionAffichageLabel = "0";
321 else if (labelTemp * 10 == (int) (labelTemp * 10))
322 precisionAffichageLabel = "1";
323 else
324 precisionAffichageLabel = "2";
325 formatter.format(
326 "\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n",
327 (k - twinAxisPosition) * scale, labelTemp);
328 k += pas;
329 }
330
331 k = twinAxisPosition - pas;
332 while (k >= minAxis) { // cote negatif
333 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
334 if (labelTemp == (int) (labelTemp))
335 precisionAffichageLabel = "0";
336 else if (labelTemp * 10 == (int) (labelTemp * 10))
337 precisionAffichageLabel = "1";
338 else
339 precisionAffichageLabel = "2";
340 formatter.format(
341 "\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n",
342 (k - twinAxisPosition) * scale, labelTemp);
343 k -= pas;
344 }
345 }
346
347 } else { // On est sur l'axe des ordonnees
348
349 // affichage de l'axe
350 formatter.format("\\draw [%s-%s] ([yshift=-%s] 0,%s) -- ([yshift=%s] 0, %s) node[above] {%s};%n",
351 arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition) * scale, arrowRightMargin,
352 (maxAxis - twinAxisPosition) * scale, label);
353
354 if (labelsFlag) {
355 // labels manuels
356 String name;
357 double value;
358 double labelTemp;
359 for (int i = 0; i < labelsValue.length; i++) {
360 value = labelsValue[i];
361 if (labelsName == null) {
362 labelTemp = (value * scale * 100 / Math.pow(10, puissDix));
363 if (labelTemp == (int) (labelTemp))
364 name = Integer.toString((int) (labelTemp / 100.0));
365 else
366 name = Double.toString(Math.round(labelTemp) / 100.0);
367 } else
368 name = labelsName[i];
369 if (value == twinAxisPosition && tick0Flag)
370 formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%s};%n", 0,
371 (value - twinAxisPosition) * scale, name);
372 else
373 formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%s};%n", 0,
374 (value - twinAxisPosition) * scale, name);
375 }
376 } else {
377 // Les labels automatiques
378 double k = twinAxisPosition;
379 double labelTemp;
380 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
381 if (labelTemp == (int) (labelTemp))
382 precisionAffichageLabel = "0";
383 else if (labelTemp * 10 == (int) (labelTemp * 10))
384 precisionAffichageLabel = "1";
385 else
386 precisionAffichageLabel = "2";
387 if (tick0Flag)
388 formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%."
389 + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp);
390 else
391 formatter.format(
392 "\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n",
393 (k - twinAxisPosition) * scale, labelTemp);
394 k += pas;
395 while (k <= maxAxis) { // cote positif de l'axe
396 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
397 if (labelTemp == (int) (labelTemp))
398 precisionAffichageLabel = "0";
399 else if (labelTemp * 10 == (int) (labelTemp * 10))
400 precisionAffichageLabel = "1";
401 else
402 precisionAffichageLabel = "2";
403 formatter.format(
404 "\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n",
405 (k - twinAxisPosition) * scale, labelTemp);
406 k += pas;
407 }
408 k = twinAxisPosition - pas;
409 while (k >= minAxis) { // cote negatif de l'axe
410 labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
411 if (labelTemp == (int) (labelTemp))
412 precisionAffichageLabel = "0";
413 else if (labelTemp * 10 == (int) (labelTemp * 10))
414 precisionAffichageLabel = "1";
415 else
416 precisionAffichageLabel = "2";
417 formatter.format(
418 "\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n",
419 (k - twinAxisPosition) * scale, labelTemp);
420 k -= pas;
421 }
422 }
423 }
424 return formatter.toString();
425 }
426
427 private double computeAutoTickValue() {
428 // Calcule le pas d'affichage par defaut des labels
429 double pas;
430 double[] bounds = new double[2];
431 bounds[0] = Math.min(axis.getRange().getLowerBound(), twinAxisPosition);
432 bounds[1] = Math.max(axis.getRange().getUpperBound(), twinAxisPosition);
433 if (bounds[1] - bounds[0] >= 1) {
434 int nbMaxDeFois = (int) (bounds[1] - bounds[0]);
435 int puissDix = (int) Math.log10(nbMaxDeFois);
436 nbMaxDeFois = (int) (nbMaxDeFois / Math.pow(10, puissDix)) + 1;
437 if (nbMaxDeFois > 5)
438 pas = Math.pow(10, puissDix);
439 else if (nbMaxDeFois > 3)
440 pas = 0.5 * Math.pow(10, puissDix);
441 else if (nbMaxDeFois > 1)
442 pas = 0.25 * Math.pow(10, puissDix);
443 else // nbMaxDeFois==1
444 pas = 0.1 * Math.pow(10, puissDix);
445 } else {
446 double nbMaxDeFois = bounds[1] - bounds[0];
447 int puissDix = 0;
448 while (nbMaxDeFois < 1) {
449 nbMaxDeFois *= 10;
450 puissDix++;
451 }
452 if (nbMaxDeFois > 5)
453 pas = 1 / Math.pow(10, puissDix);
454 else if (nbMaxDeFois > 3)
455 pas = 0.5 / Math.pow(10, puissDix);
456 else if (nbMaxDeFois > 1)
457 pas = 0.3 / Math.pow(10, puissDix);
458 else // nbMaxDeFois==1
459 pas = 0.1 / Math.pow(10, puissDix);
460 }
461 return pas;
462 }
463
464 private static double max(double[] t) {
465 if (t == null)
466 throw new IllegalArgumentException("max: null argument.");
467 double aux = t[0];
468 for (int i = 1; i < t.length; i++)
469 if (t[i] > aux)
470 aux = t[i];
471 return aux;
472 }
473
474 private static double min(double[] t) {
475 if (t == null)
476 throw new IllegalArgumentException("min: null argument.");
477 double aux = t[0];
478 for (int i = 1; i < t.length; i++)
479 if (t[i] < aux)
480 aux = t[i];
481 return aux;
482 }
483
484 void setTick0Flag(boolean flag) {
485 tick0Flag = flag;
486 }
487}
488
void setLabel(String label)
Sets the axis description.
Definition Axis.java:117
String getLabel()
Returns the axis description.
Definition Axis.java:107
Axis(NumberAxis inAxis, boolean orientation)
Create a new Axis instance from an existing NumberAxis instance with vertical ( -axis) or horizontal ...
Definition Axis.java:79
NumberAxis getAxis()
Returns the NumberAxis instance (from JFreeChart) linked with the current variable.
Definition Axis.java:98
String toLatex(double scale)
Formats and returns a string containing a LaTeX-compatible source code which represents this axis and...
Definition Axis.java:233
void disableCustomLabels()
Not used anymore.
Definition Axis.java:156
void setLabels(double[] position)
Sets the position of each label on this axis.
Definition Axis.java:169
void setLabelsAuto()
Calculates and sets an automatic tick unit.
Definition Axis.java:135
void setTwinAxisPosition(double position)
Defines where the opposite axis must be drawn on the current axis, where it should appear,...
Definition Axis.java:221
void enableCustomLabels()
Not used anymore.
Definition Axis.java:148
void setLabels(double[] position, String[] label)
Assigns custom labels to user-defined positions on the axis.
Definition Axis.java:192
void setLabels(double tick)
Sets a periodic label display.
Definition Axis.java:127
double getTwinAxisPosition()
Returns the drawing position parameter (default equals 0).
Definition Axis.java:211