KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > monitorenter > gui > chart > AAxis


1 /*
2  * AAxis.java (bold as love), base class for an axis of the Chart2D.
3  * Copyright (C) Achim Westermann, created on 21.03.2005, 16:41:06
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * If you modify or optimize the code in a useful way please let me know.
20  * Achim.Westermann@gmx.de
21  *
22  */

23
24 package info.monitorenter.gui.chart;
25
26 import info.monitorenter.gui.chart.labelformatters.ALabelFormatter;
27 import info.monitorenter.gui.chart.labelformatters.LabelFormatterAutoUnits;
28 import info.monitorenter.gui.chart.labelformatters.LabelFormatterSimple;
29 import info.monitorenter.gui.chart.rangepolicies.ARangePolicy;
30 import info.monitorenter.gui.chart.rangepolicies.RangePolicyUnbounded;
31 import info.monitorenter.util.Range;
32
33 import java.awt.Dimension JavaDoc;
34 import java.awt.FontMetrics JavaDoc;
35 import java.awt.event.MouseEvent JavaDoc;
36 import java.beans.PropertyChangeEvent JavaDoc;
37 import java.beans.PropertyChangeListener JavaDoc;
38 import java.beans.PropertyChangeSupport JavaDoc;
39 import java.util.LinkedList JavaDoc;
40
41 /**
42  * The base class for an axis of the <code>{@link Chart2D}</code>.
43  * <p>
44  * Normally - as the design and interaction of an <code>Axis</code> with the
45  * <code>Chart2D</code> is very fine-grained - it is not instantiated by users
46  * of jchart2d: It is automatically instantiated by the constructor of
47  * <code>Chart2D</code>. It then may be retrieved from the
48  * <code>Chart2D</code> by the methods {@link Chart2D#getAxisX()} and
49  * {@link Chart2D#getAxisY()} for further configuration.
50  * <p>
51  *
52  * @author <a HREF="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
53  *
54  * @version $Revision: 1.7 $
55  */

56 public abstract class AAxis implements IAxis {
57
58   /** Support for acting as a property change event producer for listeners. */
59   private PropertyChangeSupport JavaDoc m_propertyChangeSupport;
60
61   /** Boolean switch for painting x gridlines. * */
62   private boolean m_paintGrid = false;
63
64   /**
65    * Returns wether the x grid is painted or not.
66    * <p>
67    *
68    * @return wether the x grid is painted or not.
69    */

70   public final boolean isPaintGrid() {
71     return this.m_paintGrid;
72   }
73
74   /**
75    * Set wether the grid in this dimension should be painted or not.
76    * <p>
77    * A repaint operation for the chart is triggered.
78    * <p>
79    *
80    * @param grid
81    * true if the grid should be painted or false if not.
82    */

83   public final void setPaintGrid(final boolean grid) {
84     boolean oldValue = this.m_paintGrid;
85     this.m_paintGrid = grid;
86     if (oldValue != grid) {
87
88       Chart2D chart2D = this.getAccessor().getChart();
89       if (grid) {
90         // TODO: this is hardcoded behaviour that is not explained!
91
this.setPaintScale(true);
92       }
93       this.m_propertyChangeSupport.firePropertyChange(new PropertyChangeEvent JavaDoc(this,
94           IAxis.PROPERTY_PAINTGRID, new Boolean JavaDoc(oldValue), new Boolean JavaDoc(this.m_paintGrid)));
95       chart2D.repaint(200);
96     }
97   }
98
99   /** Boolean switch for painting the scale in this dimension. */
100   private boolean m_paintScale = true;
101
102   /**
103    * Set if the scale for this axis should be shown.
104    * <p>
105    *
106    * @param show
107    * true if the scale on this axis should be shown, false else.
108    */

109   public final void setPaintScale(final boolean show) {
110     this.m_paintScale = show;
111   }
112
113   /**
114    * Returns the translation of the mouse event coordinates of the given mouse
115    * event to the value within the chart for the dimension (x,y) covered by this
116    * axis.
117    * <p>
118    * Note that the mouse event has to be an event fired on the correspondinig
119    * chart component!
120    * <p>
121    *
122    * @param mouseEvent
123    * a mouse event that has been fired on this component.
124    * @return the translation of the mouse event coordinates of the given mouse
125    * event to the value within the chart for the dimension covered by
126    * this axis (x or y) or null if no calculations could be performed as
127    * the chart was not painted before.
128    */

129   protected abstract double translateMousePosition(final MouseEvent JavaDoc mouseEvent);
130
131   /**
132    * Scales the given absolute value into a value between 0 and 1.0 (if it is in
133    * the range of the data).
134    * <p>
135    * If the given absolute value is not in the display- range of the
136    * <code>Chart2D</code>, negative values or values greater than 1.0 may
137    * result.
138    * <p>
139    *
140    * @param absolute
141    * a value in the real value range of the corresponding chart.
142    *
143    * @return a value between 0.0 and 1.0 that is mapped to a position within the
144    * chart.
145    */

146   protected abstract double getScaledValue(final double absolute);
147
148   /**
149    * An internal connector class that will connect the axis to the a Chart2D.
150    * <p>
151    * It is aggregated to the {@link AAxis} in order to access either y values or
152    * x values of the Chart2D thus making the IAxis an Y Axis or X axis. This
153    * strategy reduces redundant code for label creation. It avoids complex
154    * inheritance / interface implements for different IAxis implementation that
155    * would be necessary for y-axis / x-axis implementations.
156    * <p>
157    *
158    * @author <a HREF="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
159    *
160    */

161   public abstract class Chart2DDataAccessor {
162
163     /**
164      * Returns the translation of the mouse event coordinates of the given mouse
165      * event to the value within the chart for the dimension (x,y) covered by
166      * this axis.
167      * <p>
168      * Note that the mouse event has to be an event fired on this component!
169      * <p>
170      *
171      * @param mouseEvent
172      * a mouse event that has been fired on this component.
173      * @return the translation of the mouse event coordinates of the given mouse
174      * event to the value within the chart for the dimension covered by
175      * this axis (x or y) or null if no calculations could be performed
176      * as the chart was not painted before.
177      */

178     public abstract double translateMousePosition(final MouseEvent JavaDoc mouseEvent);
179
180     /** The chart that is acessed. */
181     protected Chart2D m_chart;
182
183     /**
184      * Returns the amount of pixel avalable for displaying the values on the
185      * chart in the dimension this accessor stands for.
186      * <p>
187      * This method must not be called within the first lines of a paint cycle
188      * (neccessary underlying values then are computed new).
189      * <p>
190      *
191      * @return the amount of pixel avalable for displaying the values on the
192      * chart in the dimension this accessor stands for.
193      */

194     protected abstract int getPixelRange();
195
196     /**
197      * A pluggable range policy.
198      */

199     protected IRangePolicy m_rangePolicy = new RangePolicyUnbounded(Range.RANGE_UNBOUNDED);
200
201     /**
202      * Constructor with the chart that is acessed.
203      * <p>
204      *
205      * @param chart
206      * the chart that is acessed.
207      */

208     protected Chart2DDataAccessor(final Chart2D chart) {
209
210       AAxis.this.setAccessor(this);
211       this.m_chart = chart;
212       this.m_rangePolicy.addPropertyChangeListener(ARangePolicy.PROPERTY_RANGE, chart);
213     }
214
215     /**
216      * Returns the chart that is accessed.
217      * <p>
218      *
219      * @return the chart that is accessed.
220      */

221     public final Chart2D getChart() {
222       return this.m_chart;
223     }
224
225     /**
226      * Returns the maximum value from the Chart2D's axis (X or Y) this instance
227      * is standing for with respect to the installed range policy.
228      * <p>
229      *
230      * @return the maximum value from the Chart2D's axis (X or Y) this instance
231      * is standing for with respect to the installed range policy.
232      */

233     protected abstract double getMax();
234
235     /**
236      * @return the maximum value from the Chart2D's "axis" (X or Y) this
237      * instance is standing for.
238      *
239      */

240     protected abstract double getMaxFromAxis();
241
242     /**
243      * Returns the maximum pixels that will be needed to paint a label.
244      * <p>
245      *
246      * @return the maximum pixels that will be needed to paint a label.
247      */

248     protected abstract double getMaximumPixelForLable();
249
250     /**
251      * @return the minimum value from the Chart2D's "axis" (X or Y) this
252      * instance is standing for with respect to the installed range
253      * policy.
254      *
255      */

256     protected abstract double getMin();
257
258     /**
259      * @return the minimum value from the Chart2D's "axis" (X or Y) this
260      * instance is standing for.
261      *
262      */

263     protected abstract double getMinFromAxis();
264
265     /**
266      * Returns the minimum amount of increase in the value that will be needed
267      * to display all labels without overwriting each others.
268      * <p>
269      *
270      * This procedure needs the amount of pixels needed by the largest possible
271      * label and relies on the implementation of
272      * {@link #getMaximumPixelForLable()}, whose result is multiplied with the
273      * "value per pixel" quantifier.
274      * <p>
275      *
276      * @return the minimum amount of increase in the value that will be needed
277      * to display all labels without overwriting each others.
278      *
279      */

280     protected abstract double getMinimumValueDistanceForLables();
281
282     /**
283      * @return Returns the rangePolicy.
284      */

285     public final IRangePolicy getRangePolicy() {
286
287       return this.m_rangePolicy;
288     }
289
290     /**
291      * <p>
292      * Returns the value distance on the current chart that exists for the given
293      * amount of pixel distance in the given direction of this <code>Axis</code>.
294      * </p>
295      * <p>
296      * Depending on the width of the actual Chart2D and the contained values,
297      * the relation between displayed distances (pixel) and value distances (the
298      * values of the addes <code>{@link ITrace2D}</code> instances changes.
299      * </p>
300      * <p>
301      * This method calculates depending on the actual painting area of the
302      * Chart2D, the shift in value between two points that have a screen
303      * distance of the given pixel. <br>
304      * This method is not used by the chart itself but a helper for outside use.
305      * </p>
306      *
307      * @param pixel
308      * The desired distance between to scalepoints of the x- axis in
309      * pixel.
310      * @return a scaled (from pixel to internal value-range) and normed (to the
311      * factor of the current unit of the axis) value usable to calculate
312      * the coords for the scalepoints of the axis.
313      */

314     protected abstract double getValueDistanceForPixel(int pixel);
315
316     /**
317      * Sets the RangePolicy.
318      * <p>
319      * A new Range with minimum and maxium of the chart is set to it to ensure
320      * that after the change all traces are shown.
321      * <p>
322      *
323      * @param rangePolicy
324      * The rangePolicy to set.
325      */

326     public void setRangePolicy(final IRangePolicy rangePolicy) {
327
328       // initially configure the range to show all data (in case a fixed
329
// viewport rp is used):
330
rangePolicy.setRange(new Range(this.getMinFromAxis(), this.getMaxFromAxis()));
331       this.m_rangePolicy.removePropertyChangeListener(this.m_chart);
332       this.m_rangePolicy = rangePolicy;
333       this.m_rangePolicy.addPropertyChangeListener(ARangePolicy.PROPERTY_RANGE, this.m_chart);
334       this.m_rangePolicy.addPropertyChangeListener(ARangePolicy.PROPERTY_RANGE_MAX, this.m_chart);
335       this.m_rangePolicy.addPropertyChangeListener(ARangePolicy.PROPERTY_RANGE_MIN, this.m_chart);
336     }
337   }
338
339   /**
340    *
341    * An accessor for the x axis of a chart.
342    * <p>
343    *
344    * @author <a HREF="mailto:Achim.Westermann@gmx.de>Achim Westermann </a>
345    *
346    * @see Chart2D#getAxisX()
347    */

348   public final class XDataAccessor extends AAxis.Chart2DDataAccessor {
349
350     /**
351      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#translateMousePosition(java.awt.event.MouseEvent)
352      */

353     public double translateMousePosition(final MouseEvent JavaDoc mouseEvent) {
354       double result = 0;
355       // relate to the offset:
356
double mouseX = mouseEvent.getX() - this.m_chart.getXChartStart();
357
358       int rangeX = this.m_chart.getXChartEnd() - this.m_chart.getXChartStart();
359       if (rangeX == 0) {
360         // return null
361
} else {
362         double scaledX = mouseX / (double) rangeX;
363         Range valueRangeX = AAxis.this.getRange();
364         result = scaledX * valueRangeX.getExtent() + valueRangeX.getMin();
365       }
366       return result;
367     }
368
369     /**
370      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getPixelRange()
371      */

372     protected int getPixelRange() {
373       return this.m_chart.getXChartEnd() - this.m_chart.getXChartStart();
374     }
375
376     /**
377      * Creates an instance that accesses the given chart's x axis.
378      * <p>
379      *
380      * @param chart
381      * the chart to access.
382      */

383     public XDataAccessor(final Chart2D chart) {
384
385       super(chart);
386     }
387
388     /**
389      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMax()
390      */

391     protected double getMax() {
392       return this.m_rangePolicy.getMax(this.m_chart.getMinX(), this.m_chart.getMaxX());
393     }
394
395     /**
396      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMaxFromAxis()
397      */

398     protected final double getMaxFromAxis() {
399
400       return m_chart.getMaxX();
401     }
402
403     /**
404      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMaximumPixelForLable()
405      */

406     protected double getMaximumPixelForLable() {
407
408       FontMetrics JavaDoc fontdim = m_chart.getGraphics().getFontMetrics();
409       int fontwidth = fontdim.charWidth('0');
410       // multiply with longest possible number.
411
// longest possible number is the non-fraction part of
412
// the highest number plus the maximum amount of fraction digits
413
// plus one for the fraction separator dot.
414

415       int len = AAxis.this.getFormatter().getMaxAmountChars();
416       return fontwidth * (len + 2);
417     }
418
419     /**
420      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMin()
421      */

422     protected final double getMin() {
423       return this.m_rangePolicy.getMin(this.m_chart.getMinX(), this.m_chart.getMaxX());
424     }
425
426     /**
427      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMinFromAxis()
428      */

429     protected final double getMinFromAxis() {
430
431       return m_chart.getMinX();
432     }
433
434     /**
435      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMinimumValueDistanceForLables()
436      */

437     protected final double getMinimumValueDistanceForLables() {
438
439       Dimension JavaDoc d = this.m_chart.getSize();
440       int pxrange = (int) d.getWidth() - 60;
441       if (pxrange <= 0) {
442         return 1;
443       }
444       double valuerange = AAxis.this.getMax() - AAxis.this.getMin();
445       if (valuerange == 0) {
446         valuerange = 10;
447       }
448       double pxToValue = valuerange / pxrange;
449       double ret = pxToValue * this.getMaximumPixelForLable();
450       return ret;
451     }
452
453     /**
454      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getValueDistanceForPixel(int)
455      */

456     protected double getValueDistanceForPixel(final int pixel) {
457       Dimension JavaDoc d = this.m_chart.getSize();
458       int pxrangex = (int) d.getWidth() - 60;
459       if (pxrangex <= 0) {
460         return -1d;
461       }
462       double valuerangex = this.getMax() - this.getMin();
463       double pxToValue = valuerangex / pxrangex;
464       double ret = pxToValue * pixel;
465       return ret;
466     }
467
468     /**
469      * @see AAxis.Chart2DDataAccessor#setRangePolicy(IRangePolicy)
470      */

471     public void setRangePolicy(final IRangePolicy rangePolicy) {
472
473       double xmax = this.getMax();
474       double xmin = this.getMin();
475       super.setRangePolicy(rangePolicy);
476       // check for scaling changes:
477
if (xmax != this.getMax() || xmin != this.getMin()) {
478         this.m_chart.propertyChange(new PropertyChangeEvent JavaDoc(rangePolicy,
479             ARangePolicy.PROPERTY_RANGE, new Range(xmin, xmax), this.m_rangePolicy.getRange()));
480       }
481     }
482
483     /**
484      * Returns "X".
485      * <p>
486      *
487      * @return "X"
488      */

489     public String JavaDoc toString() {
490       return "X";
491     }
492   }
493
494   /**
495    * Accesses the y axis of the {@link Chart2D}.
496    * <p>
497    *
498    * @see AAxis#setAccessor(AAxis.Chart2DDataAccessor)
499    *
500    * @see Chart2D#getAxisY()
501    */

502
503   public final class YDataAccessor extends AAxis.Chart2DDataAccessor {
504
505     /**
506      * Creates an instance that accesses the y axis of the given chart.
507      * <p>
508      *
509      * @param chart
510      * the chart to access.
511      */

512     public YDataAccessor(final Chart2D chart) {
513
514       super(chart);
515     }
516
517     /**
518      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getPixelRange()
519      */

520     protected int getPixelRange() {
521       return this.m_chart.getYChartStart() - this.m_chart.getYChartEnd();
522     }
523
524     /**
525      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMax()
526      */

527     protected final double getMax() {
528       return this.m_rangePolicy.getMax(this.m_chart.getMinY(), this.m_chart.getMaxY());
529     }
530
531     /**
532      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMaxFromAxis()
533      */

534     protected final double getMaxFromAxis() {
535
536       return m_chart.getMaxY();
537     }
538
539     /**
540      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMaximumPixelForLable()
541      */

542     protected double getMaximumPixelForLable() {
543
544       FontMetrics JavaDoc fontdim = m_chart.getGraphics().getFontMetrics();
545       int fontheight = fontdim.getHeight();
546       return fontheight + 10;
547     }
548
549     /**
550      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMin()
551      */

552     protected final double getMin() {
553       return this.m_rangePolicy.getMin(this.m_chart.getMinY(), this.m_chart.getMaxY());
554     }
555
556     /**
557      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMinFromAxis()
558      */

559     protected final double getMinFromAxis() {
560
561       return m_chart.getMinY();
562     }
563
564     /**
565      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getMinimumValueDistanceForLables()
566      */

567     protected final double getMinimumValueDistanceForLables() {
568
569       Dimension JavaDoc d = this.m_chart.getSize();
570       int pxrange = (int) d.getHeight() - 40;
571       if (pxrange <= 0) {
572         return 1;
573       }
574       double valuerange = AAxis.this.getMax() - AAxis.this.getMin();
575       if (valuerange == 0) {
576         valuerange = 10;
577       }
578       double pxToValue = valuerange / pxrange;
579       double ret = pxToValue * this.getMaximumPixelForLable();
580       return ret;
581     }
582
583     /**
584      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#getValueDistanceForPixel(int)
585      */

586     protected double getValueDistanceForPixel(final int pixel) {
587       Dimension JavaDoc d = this.m_chart.getSize();
588       int pxrangey = (int) d.getHeight() - 40;
589       if (pxrangey <= 0) {
590         return -1d;
591       }
592       double valuerangey = this.getMaxFromAxis() - this.getMinFromAxis();
593       double pxToValue = valuerangey / pxrangey;
594       double ret = pxToValue * pixel;
595       return ret;
596     }
597
598     /**
599      * @see AAxis.Chart2DDataAccessor#setRangePolicy(IRangePolicy)
600      */

601     public void setRangePolicy(final IRangePolicy rangePolicy) {
602       double ymax = this.getMax();
603       double ymin = this.getMin();
604       super.setRangePolicy(rangePolicy);
605       // check for scaling changes:
606
if (ymax != this.getMax() || ymin != this.getMin()) {
607         this.m_chart.propertyChange(new PropertyChangeEvent JavaDoc(rangePolicy,
608             ARangePolicy.PROPERTY_RANGE, new Range(ymin, ymax), this.m_rangePolicy.getRange()));
609       }
610     }
611
612     /**
613      * Returns "Y".
614      * <p>
615      *
616      * @return "Y"
617      */

618     public String JavaDoc toString() {
619       return "Y";
620     }
621
622     /**
623      * @see info.monitorenter.gui.chart.AAxis.Chart2DDataAccessor#translateMousePosition(java.awt.event.MouseEvent)
624      */

625     public double translateMousePosition(final MouseEvent JavaDoc mouseEvent) {
626       double result = 0;
627       // relate to the offset:
628
double mouseY = this.m_chart.getYChartStart() - mouseEvent.getY();
629
630       int rangeY = this.m_chart.getYChartStart() - this.m_chart.getYChartEnd();
631       if (rangeY == 0) {
632         // return null
633
} else {
634         double scaledY = mouseY / (double) rangeY;
635         Range valueRangeY = AAxis.this.getRange();
636         result = scaledY * valueRangeY.getExtent() + valueRangeY.getMin();
637       }
638       return result;
639     }
640   }
641
642   /** Debugging flag for sysouts. */
643   private static final boolean DEBUG = false;
644
645   /**
646    * The accessor to the Chart2D.
647    * <p>
648    *
649    * It determines, which axis (x or y) this instance is representing.
650    * <p>
651    */

652   protected Chart2DDataAccessor m_accessor;
653
654   /**
655    * Formatting of the labels.
656    */

657   protected ILabelFormatter m_formatter;
658
659   /**
660    * The major tick spacing for label generations.
661    * <p>
662    *
663    * @see #setMajorTickSpacing(double)
664    */

665
666   protected double m_majorTickSpacing = 5;
667
668   /**
669    * The minor tick spacing for label generations.
670    * <p>
671    *
672    * @see #setMinorTickSpacing(double)
673    */

674   protected double m_minorTickSpacing = 1;
675
676   /** Internally used for rouding to ticks, calculated once per paint iteration. */
677   protected double m_power;
678
679   /**
680    * Controls wether scale values are started from major ticks.
681    * <p>
682    *
683    * Default is false.
684    * <p>
685    */

686   private boolean m_startMajorTick = false;
687
688   /**
689    * Default constructor that uses a {@link LabelFormatterAutoUnits} for
690    * formatting labels.
691    * <p>
692    *
693    */

694   public AAxis() {
695     this(new LabelFormatterAutoUnits(new LabelFormatterSimple()));
696   }
697
698   /**
699    * Constructor that uses the given label formatter for formatting labels.
700    * <p>
701    *
702    */

703   public AAxis(final ALabelFormatter formatter) {
704     this.setFormatter(formatter);
705     this.m_propertyChangeSupport = new PropertyChangeSupport JavaDoc(this);
706   }
707
708   /**
709    * @see info.monitorenter.gui.chart.IAxis#addPropertyChangeListener(java.lang.String,
710    * java.beans.PropertyChangeListener)
711    */

712   public void addPropertyChangeListener(final String JavaDoc propertyName,
713       final PropertyChangeListener JavaDoc listener) {
714     this.m_propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
715   }
716
717   /**
718    * Returns the accessor to the chart.
719    * <p>
720    *
721    * @return the accessor to the chart.
722    */

723   public Chart2DDataAccessor getAccessor() {
724     return this.m_accessor;
725   }
726
727   /**
728    * @return Returns the formatter.
729    */

730   public final ILabelFormatter getFormatter() {
731
732     return this.m_formatter;
733   }
734
735   /**
736    * @see AAxis#getLabels(double)
737    */

738   protected LabeledValue[] getLabels(final double resolution) {
739     if (resolution <= 0) {
740       return new LabeledValue[] {};
741     }
742     LinkedList JavaDoc collect = new LinkedList JavaDoc();
743     Range domain = this.getRange();
744     double min = domain.getMin();
745     double max = domain.getMax();
746     String JavaDoc oldLabelName = "";
747     LabeledValue label;
748     double range = max - min;
749     LabeledValue[] ret = null;
750     double value = min;
751     String JavaDoc labelName = "start";
752     int loopStop = 0;
753     boolean firstMajorFound = false;
754     // first tick, manual init
755
while (value <= max && loopStop < 100) {
756       if (loopStop == 99) {
757         if (AAxis.DEBUG) {
758           System.out.println(this.m_accessor.toString() + " axis: loop to high");
759         }
760       }
761       if (oldLabelName.equals(labelName)) {
762         if (AAxis.DEBUG) {
763           System.out.println("constant Label");
764         }
765       }
766       label = this.roundToTicks(value, false, !firstMajorFound && this.m_startMajorTick);
767
768       oldLabelName = labelName;
769       labelName = label.m_label;
770       value = label.m_value;
771
772       loopStop++;
773       if (firstMajorFound || !this.m_startMajorTick || label.isMajorTick()) {
774         firstMajorFound = true;
775         if (value <= max) {
776           collect.add(label);
777         } else {
778           // System.out.println("Dropping label (too high) : (" + label + ")
779
// [max: " + max + "]");
780
}
781       }
782       value += resolution;
783     }
784     int stop = collect.size();
785
786     ret = new LabeledValue[stop];
787     for (int i = 0; i < stop; i++) {
788       label = (LabeledValue) collect.get(i);
789       label.m_value = (label.m_value - min) / range;
790       ret[i] = label;
791     }
792     return ret;
793   }
794
795   /**
796    * Get the major tick spacing for label generation.
797    * <p>
798    *
799    * @see #setMajorTickSpacing(double)
800    *
801    */

802
803   public double getMajorTickSpacing() {
804     return this.m_majorTickSpacing;
805   }
806
807   /**
808    * The maximum value access method for the Axis this instance is aggregated
809    * to.
810    * <p>
811    * It supports the retrieval of data from the corrcet dimension of the
812    * connected Chart2 (X or Y) as well as the respect to the configured
813    * {@link IRangePolicy}.
814    * <p>
815    *
816    * @return the maximum value access method for the Axis this instance is
817    * aggregated to.
818    */

819   public double getMax() {
820     return this.m_accessor.getMax();
821   }
822
823   /**
824    * Returns the minimum value access method for the Axis this instance is
825    * aggregated to.
826    * <p>
827    * It supports the retrieval of data from the corrcet dimension of the
828    * connected Chart2 (X or Y) as well as the respect to the configured
829    * {@link IRangePolicy}.
830    * <p>
831    *
832    * @return the minimum value access method for the Axis this instance is
833    * aggregated to.
834    */

835   public double getMin() {
836
837     return this.m_accessor.getMin();
838
839   }
840
841   /**
842    * Get the minor tick spacing for label generation.
843    * <p>
844    *
845    * @return he minor tick spacing for label generation.
846    *
847    * @see #setMinorTickSpacing(double)
848    *
849    */

850   public double getMinorTickSpacing() {
851     return this.m_minorTickSpacing;
852   }
853
854   /**
855    * This method is used by the Chart2D to scale it's values during painting.
856    * <p>
857    * Caution: This method does not necessarily return the Range configured with
858    * {@link #setRange(Range)}. The internal {@link IRangePolicy} is taken into
859    * account.
860    * <p>
861    *
862    * @return the range corresponding to the upper and lower bound of the values
863    * that will be displayable on this Axis of the Chart2D.
864    *
865    * @see #setRangePolicy(IRangePolicy)
866    * @see Chart2DDataAccessor#getRange()
867    *
868    */

869   public final Range getRange() {
870     Range result;
871     double min = this.getMin();
872     double max = this.getMax();
873     if (min == max) {
874       max += 10;
875     }
876     result = new Range(min, max);
877     return result;
878   }
879
880   /**
881    * See!
882    * <p>
883    *
884    * @see Chart2DDataAccessor#getRangePolicy()
885    *
886    */

887   public IRangePolicy getRangePolicy() {
888
889     return this.m_accessor.getRangePolicy();
890   }
891
892   /**
893    * Returns the array of labeled values that will be used by the
894    * {@link Chart2D} to paint labels.
895    * <p>
896    *
897    * @return the array of labeled values that will be used by the
898    * {@link Chart2D} to paint labels.
899    */

900   protected LabeledValue[] getScaleValues() {
901     double labelspacepx = this.m_accessor.getMinimumValueDistanceForLables();
902     double formattingspace = this.m_formatter.getMinimumValueShiftForChange();
903     double max = Math.max(labelspacepx, formattingspace);
904     return this.getLabels(max);
905   }
906
907   /**
908    * <p>
909    * Returns the value distance on the current chart that exists for the given
910    * amount of pixel distance in the given direction of this <code>Axis</code>.
911    * </p>
912    * <p>
913    * Depending on the width of the actual Chart2D and the contained values, the
914    * relation between displayed distances (pixel) and value distances (the
915    * values of the addes <code>{@link ITrace2D}</code> instances changes.
916    * </p>
917    * <p>
918    * This method calculates depending on the actual painting area of the
919    * Chart2D, the shift in value between two points that have a screen distance
920    * of the given pixel. <br>
921    * This method is not used by the chart itself but a helper for outside use.
922    * </p>
923    *
924    * @param pixel
925    * The desired distance between to scalepoints of the x- axis in
926    * pixel.
927    * @return a scaled (from pixel to internal value-range) and normed (to the
928    * factor of the current unit of the axis) value usable to calculate
929    * the coords for the scalepoints of the axis.
930    */

931   protected final double getValueDistanceForPixel(final int pixel) {
932     return this.m_accessor.getValueDistanceForPixel(pixel);
933   }
934
935   /**
936    * Perfomres expensive calculations for various values that are used by many
937    * calls throughout a paint iterations.
938    * <p>
939    *
940    * These values are constant throughout a paint iteration by the contract that
941    * no point is added removed or changed in this period. Because these values
942    * are used from many methods it is impossible to calculate them at a
943    * "transparent" method that may perform this caching over a paint period
944    * without knowledge from outside. The first method called in a paint
945    * iteration is called several further times in the iteration. So this is the
946    * common hook to invoke before painting a chart.
947    * <p>
948    *
949    */

950   protected void initPaintIteration() {
951
952     // get the powers of ten of the range, a minor Tick of 1.0 has to be
953
// able to
954
// be 100 times in a range of 100
955
// (match 1,2,3,... instead of 10,20,30,....
956
double range = this.getMax() - this.getMin();
957     if (range == 0 || range == Double.NEGATIVE_INFINITY || range == Double.POSITIVE_INFINITY) {
958       range = 1;
959     }
960     double tmpPower = 0;
961     if (range > 1) {
962       while (range > 10) {
963         range /= 10;
964         tmpPower++;
965       }
966       tmpPower = Math.pow(10, tmpPower - 1);
967     } else {
968       while (range < 1) {
969         range *= 10;
970         tmpPower++;
971       }
972
973       tmpPower = 1 / Math.pow(10, tmpPower);
974     }
975     this.m_power = tmpPower;
976
977     // This is needed e.g. for LabelFormatterAutoUnits to choose the unit
978
// according to the actual range of this paint iteration.
979
this.m_formatter.initPaintIteration();
980   }
981
982   /**
983    * Check wether scale values are started from major ticks.
984    * <p>
985    *
986    * @return true if scale values start from major ticks.
987    *
988    * @see AAxis#setMajorTickSpacing(double)
989    */

990   public boolean isStartMajorTick() {
991     return this.m_startMajorTick;
992   }
993
994   /**
995    * Internal rounding routine.
996    * <p>
997    *
998    * Arguments are not chosen to be "understandable" or "usable" but optimized
999    * for performance.
1000   * <p>
1001   *
1002   * The <code> findMajorTick</code> argument may be used e.g. to force labels
1003   * to start from a major tick.
1004   * <p>
1005   *
1006   * @param value
1007   * the value to round.
1008   *
1009   * @param floor
1010   * if true, rounding goes to floor else to ceiling.
1011   *
1012   * @param findMajorTick
1013   * if true the returned value will be a major tick (which might be
1014   * fare more away from the given value than the next major tick).
1015   *
1016   * @return the value rounded to minor or major ticks.
1017   */

1018  protected LabeledValue roundToTicks(final double value, final boolean floor,
1019      final boolean findMajorTick) {
1020    LabeledValue ret = new LabeledValue();
1021
1022    double minorTick = this.m_minorTickSpacing * this.m_power;
1023    double majorTick = this.m_majorTickSpacing * this.m_power;
1024
1025    double majorRound;
1026
1027    if (floor) {
1028      majorRound = Math.floor(value / majorTick);
1029    } else {
1030      majorRound = Math.ceil(value / majorTick);
1031    }
1032    boolean majorZeroHit = majorRound == 0 && value != 0;
1033    majorRound *= majorTick;
1034    double minorRound;
1035    if (floor) {
1036      minorRound = Math.floor(value / minorTick);
1037    } else {
1038      minorRound = Math.ceil(value / minorTick);
1039    }
1040    boolean minorZeroHit = minorRound == 0 && value != 0;
1041    minorRound *= minorTick;
1042    if (majorZeroHit || minorZeroHit) {
1043      if (AAxis.DEBUG) {
1044        System.out.println("zeroHit");
1045      }
1046    }
1047
1048    double minorDistance = Math.abs(value - minorRound);
1049    double majorDistance = Math.abs(value - majorRound);
1050
1051    double majorMinorRelation = minorDistance / majorDistance;
1052    if (majorMinorRelation == Double.NaN) {
1053      majorMinorRelation = 1.0;
1054    }
1055
1056    if (majorDistance <= minorDistance || findMajorTick) {
1057      ret.m_value = majorRound;
1058      ret.m_isMajorTick = true;
1059    } else {
1060      ret.m_value = minorRound;
1061      ret.m_isMajorTick = false;
1062    }
1063
1064    // format label string.
1065
ret.m_label = this.getFormatter().format(ret.m_value);
1066    // as formatting rounds too, reparse value so that it is exactly at the
1067
// point the
1068
// label string describes.
1069
ret.m_value = this.getFormatter().parse(ret.m_label).doubleValue();
1070    return ret;
1071  }
1072
1073  /**
1074   * Sets the accessor to the axis of the chart.
1075   * <p>
1076   *
1077   * @param accessor
1078   * the accessor to the axis of the chart.
1079   */

1080  protected final void setAccessor(final Chart2DDataAccessor accessor) {
1081
1082    this.m_accessor = accessor;
1083  }
1084
1085  /**
1086   * Sets the formatter to use for labels.
1087   * <p>
1088   *
1089   * @param formatter
1090   * The formatter to set.
1091   */

1092  public void setFormatter(final ALabelFormatter formatter) {
1093
1094    this.m_formatter = formatter;
1095    this.m_formatter.setAxis(this);
1096  }
1097
1098  /**
1099   * This method sets the major tick spacing for label generation.
1100   * <p>
1101   *
1102   * Only values between 0.0 and 100.0 are allowed.
1103   * <p>
1104   *
1105   * The number that is passed-in represents the distance, measured in values,
1106   * between each major tick mark. If you have a trace with a range from 0 to 50
1107   * and the major tick spacing is set to 10, you will get major ticks next to
1108   * the following values: 0, 10, 20, 30, 40, 50.
1109   * <p>
1110   *
1111   * <b>Note: </b> <br>
1112   * Ticks are free of any multiples of 1000. If the chart contains values
1113   * between 0 an 1000 and configured a tick of 2 the values 0, 200, 400, 600,
1114   * 800 and 1000 will highly probable to be displayed. This depends on the size
1115   * (in pixels) of the <code>Chart2D<</code>. Of course there is a
1116   * difference: ticks are used in divisions and multiplications: If the
1117   * internal values are very low and the ticks are very high, huge rounding
1118   * errors might occur (division by ticks results in very low values a double
1119   * cannot hit exactly. So prefer setting ticks between 0 an 10 or - if you
1120   * know your values are very small (e.g. in nano range [10 <sup>-9 </sup>])
1121   * use a small value (e.g. 2*10 <sup>-9 </sup>).
1122   * <p>
1123   *
1124   * @param majorTickSpacing
1125   * the major tick spacing for label generation.
1126   */

1127  public void setMajorTickSpacing(final double majorTickSpacing) {
1128    this.m_majorTickSpacing = majorTickSpacing;
1129  }
1130
1131  /**
1132   * This method sets the minor tick spacing for label generation.
1133   * <p>
1134   *
1135   * The number that is passed-in represents the distance, measured in values,
1136   * between each major tick mark. If you have a trace with a range from 0 to 50
1137   * and the major tick spacing is set to 10, you will get major ticks next to
1138   * the following values: 0, 10, 20, 30, 40, 50.
1139   * <p>
1140   *
1141   * <b>Note: </b> <br>
1142   * Ticks are free of any powers of 10. There is no difference between setting
1143   * a tick to 2, 200 or 20000 because ticks cannot break the rule that every
1144   * scale label has to be visible. If the chart contains values between 0 an
1145   * 1000 and configured a tick of 2 the values 0, 200, 400, 600, 800 and 1000
1146   * will highly probable to be displayed. This depends on the size (in pixels)
1147   * of the <code>Chart2D<</code>. Of course there is a difference: ticks
1148   * are used in divisions and multiplications: If the internal values are very
1149   * low and the ticks are very high, huge rounding errors might occur (division
1150   * by ticks results in very low values a double cannot hit exactly. So prefer
1151   * setting ticks between 0 an 10 or - if you know your values are very small
1152   * (e.g. in nano range [10 <sup>-9 </sup>]) use a small value (e.g. 2*10
1153   * <sup>-9 </sup>).
1154   * <p>
1155   *
1156   * @param minorTickSpacing
1157   * the minor tick spacing to set.
1158   *
1159   */

1160  public void setMinorTickSpacing(final double minorTickSpacing) {
1161    this.m_minorTickSpacing = minorTickSpacing;
1162  }
1163
1164  /**
1165   * <p>
1166   * Sets a Range to use for filtering the view to the the connected Axis. Note
1167   * that it's effect will be affected by the internal {@link IRangePolicy}.
1168   * </p>
1169   * <p>
1170   * To get full control use: <br>
1171   * <code> setRangePolicy(new &lt;AnARangePolicy&gt;(range);</code>
1172   * </p>
1173   *
1174   * @param range
1175   * Range to use for filtering the view to the the connected Axis.
1176   *
1177   * @see #getRangePolicy()
1178   *
1179   * @see IRangePolicy#setRange(Range)
1180   */

1181  public final void setRange(final Range range) {
1182
1183    this.m_accessor.getRangePolicy().setRange(range);
1184  }
1185
1186  /**
1187   * <p>
1188   * Sets the RangePolicy.
1189   * </p>
1190   * <p>
1191   * If the given RangePolicy has an unconfigured internal Range (
1192   * {@link Range#RANGE_UNBOUNDED}) the old internal RangePolicy is taken into
1193   * account: <br>
1194   * If the old RangePolicy has a configured Range this is transferred to the
1195   * new RangePolicy.
1196   * </p>
1197   * A property change event for {@link IAxis#PROPERTY_RANGEPOLICY} is fired and
1198   * receives listeners if a change took place.
1199   * <p>
1200   *
1201   * @param rangePolicy
1202   * The rangePolicy to set.
1203   */

1204  public void setRangePolicy(final IRangePolicy rangePolicy) {
1205
1206    IRangePolicy old = this.m_accessor.getRangePolicy();
1207    this.m_accessor.setRangePolicy(rangePolicy);
1208    this.m_propertyChangeSupport.firePropertyChange(PROPERTY_RANGEPOLICY, old, rangePolicy);
1209  }
1210
1211  /**
1212   * Set wether scale values are started from major ticks.
1213   * <p>
1214   *
1215   * @param majorTick
1216   * true if scale values shall start with a major tick.
1217   *
1218   * @see AAxis#setMajorTickSpacing(double)
1219   */

1220  public void setStartMajorTick(final boolean majorTick) {
1221    this.m_startMajorTick = majorTick;
1222  }
1223
1224  /**
1225   * Returns whether the scale for this axis should be painted or not.
1226   * <p>
1227   *
1228   * @return whether the scale for this axis should be painted or not.
1229   */

1230  public final boolean isPaintScale() {
1231    return this.m_paintScale;
1232  }
1233}
1234
Popular Tags