KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > action > layout > AxisLabelLayout


1 package prefuse.action.layout;
2
3 import java.awt.geom.Rectangle2D JavaDoc;
4 import java.text.NumberFormat JavaDoc;
5 import java.util.Iterator JavaDoc;
6 import java.util.logging.Logger JavaDoc;
7
8 import prefuse.Constants;
9 import prefuse.data.Schema;
10 import prefuse.data.query.ObjectRangeModel;
11 import prefuse.data.tuple.TupleSet;
12 import prefuse.data.util.Index;
13 import prefuse.util.MathLib;
14 import prefuse.util.PrefuseLib;
15 import prefuse.util.ui.ValuedRangeModel;
16 import prefuse.visual.VisualItem;
17 import prefuse.visual.VisualTable;
18
19 /**
20  * Layout Action that positions axis grid lines and labels for a given
21  * range model.
22  *
23  * @author <a HREF="http://jheer.org">jeffrey heer</a>
24  */

25 public class AxisLabelLayout extends Layout {
26
27     public static final String JavaDoc FRAC = "frac";
28     public static final String JavaDoc LABEL = "_label";
29     public static final String JavaDoc VALUE = "_value";
30     
31     private AxisLayout m_layout; // pointer to matching layout, if any
32
private ValuedRangeModel m_model;
33     private double m_lo, m_hi, m_prevlo, m_prevhi;
34     
35     private NumberFormat JavaDoc m_nf = NumberFormat.getInstance();
36     private int m_axis;
37     private boolean m_asc = true;
38     private int m_scale = Constants.LINEAR_SCALE;
39     
40     private double m_spacing; // desired spacing between axis labels
41

42     /**
43      * Create a new AxisLabelLayout layout.
44      * @param group the data group of the axis lines and labels
45      * @param axis the axis type, either {@link prefuse.Constants#X_AXIS}
46      * or {@link prefuse.Constants#Y_AXIS}.
47      * @param values the range model that defines the span of the axis
48      */

49     public AxisLabelLayout(String JavaDoc group, int axis, ValuedRangeModel values)
50     {
51         this(group, axis, values, null);
52     }
53     
54     /**
55      * Create a new AxisLabelLayout layout.
56      * @param group the data group of the axis lines and labels
57      * @param axis the axis type, either {@link prefuse.Constants#X_AXIS}
58      * or {@link prefuse.Constants#Y_AXIS}.
59      * @param values the range model that defines the span of the axis
60      * @param bounds the layout bounds within which to place the axis marks
61      */

62     public AxisLabelLayout(String JavaDoc group, int axis, ValuedRangeModel values,
63             Rectangle2D JavaDoc bounds)
64     {
65         super(group);
66         if ( bounds != null )
67             setLayoutBounds(bounds);
68         m_model = values;
69         m_axis = axis;
70         m_spacing = 50;
71     }
72     
73     /**
74      * Create a new AxisLabelLayout layout.
75      * @param group the data group of the axis lines and labels
76      * @param layout an {@link AxisLayout} instance to model this layout after.
77      * The axis type and range model of the provided instance will be used.
78      */

79     public AxisLabelLayout(String JavaDoc group, AxisLayout layout) {
80         this(group, layout, null, 50);
81     }
82     
83     /**
84      * Create a new AxisLabelLayout layout.
85      * @param group the data group of the axis lines and labels
86      * @param layout an {@link AxisLayout} instance to model this layout after.
87      * The axis type and range model of the provided instance will be used.
88      * @param bounds the layout bounds within which to place the axis marks
89      */

90     public AxisLabelLayout(String JavaDoc group, AxisLayout layout, Rectangle2D JavaDoc bounds) {
91         this(group, layout, bounds, 50);
92     }
93
94     /**
95      * Create a new AxisLabelLayout layout.
96      * @param group the data group of the axis lines and labels
97      * @param layout an {@link AxisLayout} instance to model this layout after.
98      * The axis type and range model of the provided instance will be used.
99      * @param bounds the layout bounds within which to place the axis marks
100      * @param spacing the minimum spacing between axis labels
101      */

102     public AxisLabelLayout(String JavaDoc group, AxisLayout layout, Rectangle2D JavaDoc bounds,
103             double spacing)
104     {
105         super(group);
106         if ( bounds != null )
107             setLayoutBounds(bounds);
108         m_layout = layout;
109         m_model = layout.getRangeModel();
110         m_axis = layout.getAxis();
111         m_scale = layout.getScale();
112         m_spacing = spacing;
113     }
114     
115     // ------------------------------------------------------------------------
116

117     /**
118      * Get the formatter used to format labels for numerical values.
119      * @return the <code>NumberFormat</code> used to format numerical labels.
120      */

121     public NumberFormat JavaDoc getNumberFormat() {
122         return m_nf;
123     }
124
125     /**
126      * Set the formatter used to format labels for numerical values.
127      * @param nf the <code>NumberFormat</code> used to format numerical labels.
128      */

129     public void setNumberFormat(NumberFormat JavaDoc nf) {
130         m_nf = nf;
131     }
132     
133     /**
134      * Get the required minimum spacing between axis labels.
135      * @return the axis label spacing
136      */

137     public double getSpacing() {
138         return m_spacing;
139     }
140
141     /**
142      * Set the required minimum spacing between axis labels.
143      * @param spacing the axis label spacing to use
144      */

145     public void setSpacing(double spacing) {
146         m_spacing = spacing;
147     }
148     
149     /**
150      * Returns the scale type used for the axis. This setting only applies
151      * for numerical data types (i.e., when axis values are from a
152      * <code>NumberValuedRange</code>).
153      * @return the scale type. One of
154      * {@link prefuse.Constants#LINEAR_SCALE},
155      * {@link prefuse.Constants#SQRT_SCALE}, or
156      * {@link Constants#LOG_SCALE}.
157      */

158     public int getScale() {
159         return m_scale;
160     }
161     
162     /**
163      * Sets the scale type used for the axis. This setting only applies
164      * for numerical data types (i.e., when axis values are from a
165      * <code>NumberValuedRange</code>).
166      * @param scale the scale type. One of
167      * {@link prefuse.Constants#LINEAR_SCALE},
168      * {@link prefuse.Constants#SQRT_SCALE}, or
169      * {@link Constants#LOG_SCALE}.
170      */

171     public void setScale(int scale) {
172         if ( scale < 0 || scale >= Constants.SCALE_COUNT ) {
173             throw new IllegalArgumentException JavaDoc(
174                 "Unrecognized scale type: "+scale);
175         }
176         m_scale = scale;
177     }
178     
179     /**
180      * Indicates if the axis values should be presented in ascending order
181      * along the axis.
182      * @return true if data values increase as pixel coordinates increase,
183      * false if data values decrease as pixel coordinates increase.
184      */

185     public boolean isAscending() {
186         return m_asc;
187     }
188     
189     /**
190      * Sets if the axis values should be presented in ascending order
191      * along the axis.
192      * @param asc true if data values should increase as pixel coordinates
193      * increase, false if data values should decrease as pixel coordinates
194      * increase.
195      */

196     public void setAscending(boolean asc) {
197         m_asc = asc;
198     }
199     
200     /**
201      * Sets the range model used to layout this axis.
202      * @param model the range model
203      */

204     public void setRangeModel(ValuedRangeModel model) {
205         m_model = model;
206     }
207     
208     // ------------------------------------------------------------------------
209

210     /**
211      * @see prefuse.action.GroupAction#run(double)
212      */

213     public void run(double frac) {
214         if ( m_model == null && m_layout != null )
215             m_model = m_layout.getRangeModel();
216         
217         if ( m_model == null ) {
218             Logger.getLogger(this.getClass().getName())
219                 .warning("Axis labels missing a range model.");
220             return;
221         }
222         
223         VisualTable labels = getTable();
224         
225         // check the axis label group to see if we can get a
226
// more precise reading of the previous scale
227
Double JavaDoc dfrac = (Double JavaDoc)labels.getClientProperty(FRAC);
228         double fr = dfrac==null ? 1.0 : dfrac.doubleValue();
229         m_prevlo = m_prevlo + fr*(m_lo-m_prevlo);
230         m_prevhi = m_prevhi + fr*(m_hi-m_prevhi);
231         
232         // now compute the layout
233
if ( m_model instanceof ObjectRangeModel )
234         { // ordinal layout
235
// get the current high and low values
236
m_lo = m_model.getValue();
237             m_hi = m_lo + m_model.getExtent();
238             
239             // compute the layout
240
ordinalLayout(labels);
241         }
242         else
243         { // numerical layout
244
// get the current high and low values
245
m_lo = ((Number JavaDoc)m_model.getLowValue()).doubleValue();
246             m_hi = ((Number JavaDoc)m_model.getHighValue()).doubleValue();
247             
248             // compute the layout
249
switch ( m_scale ) {
250             case Constants.LOG_SCALE:
251                 logLayout(labels);
252                 break;
253             case Constants.SQRT_SCALE:
254                 sqrtLayout(labels);
255                 break;
256             case Constants.LINEAR_SCALE:
257             default:
258                 linearLayout(labels);
259             }
260         }
261         
262         // get rid of any labels that are no longer being used
263
garbageCollect(labels);
264     }
265     
266     // ------------------------------------------------------------------------
267
// Quantitative Axis Layout
268

269     /**
270      * Calculates a quantitative, linearly scaled layout.
271      */

272     protected void linearLayout(VisualTable labels) {
273         Rectangle2D JavaDoc b = getLayoutBounds();
274         double breadth = getBreadth(b);
275         
276         double span = m_hi-m_lo;
277         double pspan = m_prevhi-m_prevlo;
278         double vlo = 0;
279         if ( m_lo >= 0 ) {
280             vlo = Math.pow(10, Math.floor(MathLib.log10(m_lo)));
281         } else {
282             vlo = -Math.pow(10, 1+Math.floor(MathLib.log10(-m_lo)));
283         }
284         //if ( vlo == 10 || vlo == 1 || vlo == 0.1 ) vlo = 0;
285

286         // mark previously visible labels
287
Iterator JavaDoc iter = labels.tuples();
288         while ( iter.hasNext() ) {
289             VisualItem item = (VisualItem)iter.next();
290             reset(item);
291             double v = item.getDouble(VALUE);
292             double x = span==0 ? 0 : ((v-m_lo)/span)*breadth;
293             set(item, x, b);
294         }
295
296         Index index = labels.index(VALUE);
297         double step = getLinearStep(span, span==0 ? 0 : breadth/span);
298         if ( step == 0 ) step = 1;
299         int r;
300
301         for ( double x, v=vlo; v<=m_hi; v+=step ) {
302             x = ((v-m_lo)/span)*breadth;
303             if ( x < -0.5 ) {
304                 continue;
305             } else if ( (r=index.get(v)) >= 0 ) {
306                 VisualItem item = labels.getItem(r);
307                 item.setVisible(true);
308                 item.setEndVisible(true);
309             } else {
310                 VisualItem item = labels.addItem();
311                 item.set(LABEL, m_nf.format(v));
312                 item.setDouble(VALUE, v);
313                 double f = pspan==0 ? 0 : ((v-m_prevlo)/pspan);
314                 if ( f <= 0 || f >= 1.0 ) item.setStartVisible(true);
315                 set(item, f*breadth, b);
316                 set(item, x, b);
317             }
318         }
319     }
320     
321     /**
322      * Calculates a quantitative, square root scaled layout.
323      */

324     protected void sqrtLayout(VisualTable labels) {
325         Rectangle2D JavaDoc b = getLayoutBounds();
326         double breadth = getBreadth(b);
327         
328         double span = m_hi-m_lo;
329         double splo = MathLib.safeSqrt(m_prevlo);
330         double spspan = MathLib.safeSqrt(m_prevhi)-splo;
331         double vlo = Math.pow(10, Math.floor(MathLib.safeLog10(m_lo)));
332         double slo = MathLib.safeSqrt(m_lo);
333         double sspan = MathLib.safeSqrt(m_hi)-slo;
334         
335         // mark previously visible labels
336
Iterator JavaDoc iter = labels.tuples();
337         while ( iter.hasNext() ) {
338             VisualItem item = (VisualItem)iter.next();
339             reset(item);
340             double v = item.getDouble(VALUE);
341             double x = span==0 ? 0 : ((MathLib.safeSqrt(v)-slo)/sspan)*breadth;
342             set(item, x, b);
343         }
344         
345         Index index = labels.index(VALUE);
346         double step = getLinearStep(span, breadth/span);
347         if ( step == 0 ) step = 1;
348         int r;
349         for ( double x, v=vlo; v<=m_hi; v+=step ) {
350             x = ((MathLib.safeSqrt(v)-slo)/sspan)*breadth;
351             if ( x < -0.5 ) {
352                 continue;
353             } else if ( (r=index.get(v)) >= 0 ) {
354                 VisualItem item = labels.getItem(r);
355                 item.setVisible(true);
356                 item.setEndVisible(true);
357             } else {
358                 VisualItem item = labels.addItem();
359                 item.set(LABEL, m_nf.format(v));
360                 item.setDouble(VALUE, v);
361                 double f = spspan==0 ? 0 : ((MathLib.safeSqrt(v)-splo)/spspan);
362                 if ( f <= 0 || f >= 1.0 ) {
363                     item.setStartVisible(true);
364                 }
365                 set(item, f*breadth, b);
366                 set(item, x, b);
367             }
368         }
369     }
370     
371     /**
372      * Calculates a quantitative, logarithmically-scaled layout.
373      * TODO: This method is currently not working correctly.
374      */

375     protected void logLayout(VisualTable labels) {
376         Rectangle2D JavaDoc b = getLayoutBounds();
377         double breadth = getBreadth(b);
378         
379         labels.clear();
380         
381         // get span in log space
382
// get log of the difference
383
// if [0.1,1) round to .1's 0.1-->0.1
384
// if [1,10) round to 1's 1-->1
385
// if [10,100) round to 10's 10-->10
386
double llo = MathLib.safeLog10(m_lo);
387         double lhi = MathLib.safeLog10(m_hi);
388         double lspan = lhi - llo;
389         
390         double d = MathLib.log10(lhi-llo);
391         int e = (int)Math.floor(d);
392         int ilo = (int)Math.floor(llo);
393         int ihi = (int)Math.ceil(lhi);
394         
395         double start = Math.pow(10,ilo);
396         double end = Math.pow(10, ihi);
397         double step = start * Math.pow(10, e);
398         //System.out.println((hi-lo)+"\t"+e+"\t"+start+"\t"+end+"\t"+step);
399

400         // TODO: catch infinity case if diff is zero
401
// figure out label cases better
402
for ( double val, v=start, i=0; v<=end; v+=step, ++i ) {
403             val = MathLib.safeLog10(v);
404             if ( i != 0 && Math.abs(val-Math.round(val)) < 0.0001 ) {
405                 i = 0;
406                 step = 10*step;
407             }
408             val = ((val-llo)/lspan)*breadth;
409             if ( val < -0.5 ) continue;
410             
411             VisualItem item = labels.addItem();
412             set(item, val, b);
413             String JavaDoc label = i==0 ? m_nf.format(v) : null;
414             item.set(LABEL, label);
415             item.setDouble(VALUE, v);
416         }
417     }
418     
419     /**
420      * Get the "breadth" of a rectangle, based on the axis type.
421      */

422     protected double getBreadth(Rectangle2D JavaDoc b) {
423         switch ( m_axis ) {
424         case Constants.X_AXIS:
425             return b.getWidth();
426         default:
427             return b.getHeight();
428         }
429     }
430     
431     /**
432      * Adjust a value according to the current scale type.
433      */

434     protected double adjust(double v) {
435         switch ( m_scale ) {
436         case Constants.LOG_SCALE:
437             return Math.pow(10,v);
438         case Constants.SQRT_SCALE:
439             return v*v;
440         case Constants.LINEAR_SCALE:
441         default:
442             return v;
443         }
444     }
445     
446     /**
447      * Compute a linear step between axis marks.
448      */

449     protected double getLinearStep(double span, double scale) {
450         double log10 = Math.log(span)/Math.log(10);
451         double step = Math.pow(10, Math.floor(log10));
452         
453         double delta = step * scale / m_spacing;
454         if (delta > 20) {
455             step /= 20;
456         } else if (delta > 10) {
457             step /= 10;
458         } else if (delta > 5) {
459             step /= 5;
460         } else if (delta > 4) {
461             step /= 4;
462         } else if (delta > 2) {
463             step /= 2;
464         } else if (delta < 1) {
465             step *= 2;
466         }
467         return step;
468     }
469     
470     // ------------------------------------------------------------------------
471
// Ordinal Axis Layout
472

473     /**
474      * Compute an ordinal layout of axis marks.
475      */

476     protected void ordinalLayout(VisualTable labels) {
477         ObjectRangeModel model = (ObjectRangeModel)m_model;
478         double span = m_hi-m_lo;
479         double pspan = m_prevhi-m_prevlo;
480         
481         Rectangle2D JavaDoc b = getLayoutBounds();
482         double breadth = getBreadth(b);
483         double scale = breadth/span;
484         int step = getOrdinalStep(span, scale);
485         if ( step <= 0 ) step = 1;
486         
487         // mark previously visible labels
488
Iterator JavaDoc iter = labels.tuples();
489         while ( iter.hasNext() ) {
490             VisualItem item = (VisualItem)iter.next();
491             reset(item);
492             double v = item.getDouble(VALUE);
493             double x = span==0 ? 0 : ((v-m_lo)/span)*breadth;
494             set(item, x, b);
495         }
496
497         Index index = labels.index(VALUE);
498         
499         // handle remaining labels
500
for ( int r, v=(int)m_lo; v<=m_hi; v+=step ) {
501             if ( (r=index.get((double)v)) >= 0 ) {
502                 VisualItem item = labels.getItem(r);
503                 item.set(VisualItem.LABEL, model.getObject(v).toString());
504                 item.setVisible(true);
505                 item.setEndVisible(true);
506             } else {
507                 VisualItem item = labels.addItem();
508                 item.set(VisualItem.LABEL, model.getObject(v).toString());
509                 item.setDouble(VisualItem.VALUE, v);
510                 double f = pspan==0 ? 0 : ((v-m_prevlo)/pspan);
511                 if ( f <= 0 || f >= 1.0 ) item.setStartVisible(true);
512                 set(item, f*breadth, b);
513                 set(item, (v-m_lo)*breadth/span, b);
514             }
515         }
516     }
517     
518     /**
519      * Compute an ordinal step between axis marks.
520      */

521     protected int getOrdinalStep(double span, double scale) {
522         return (scale >= m_spacing ? 1 : (int)Math.ceil(m_spacing/scale));
523     }
524     
525     // ------------------------------------------------------------------------
526
// Auxiliary methods
527

528     /**
529      * Set the layout values for an axis label item.
530      */

531     protected void set(VisualItem item, double xOrY, Rectangle2D JavaDoc b) {
532         switch ( m_axis ) {
533         case Constants.X_AXIS:
534             xOrY = m_asc ? xOrY + b.getMinX() : b.getMaxX() - xOrY;
535             PrefuseLib.updateDouble(item, VisualItem.X, xOrY);
536             PrefuseLib.updateDouble(item, VisualItem.Y, b.getMinY());
537             PrefuseLib.updateDouble(item, VisualItem.X2, xOrY);
538             PrefuseLib.updateDouble(item, VisualItem.Y2, b.getMaxY());
539             break;
540         case Constants.Y_AXIS:
541             xOrY = m_asc ? b.getMaxY() - xOrY - 1 : xOrY + b.getMinY();
542             PrefuseLib.updateDouble(item, VisualItem.X, b.getMinX());
543             PrefuseLib.updateDouble(item, VisualItem.Y, xOrY);
544             PrefuseLib.updateDouble(item, VisualItem.X2, b.getMaxX());
545             PrefuseLib.updateDouble(item, VisualItem.Y2, xOrY);
546         }
547     }
548     
549     /**
550      * Reset an axis label VisualItem
551      */

552     protected void reset(VisualItem item) {
553         item.setVisible(false);
554         item.setEndVisible(false);
555         item.setStartStrokeColor(item.getStrokeColor());
556         item.revertToDefault(VisualItem.STROKECOLOR);
557         item.revertToDefault(VisualItem.ENDSTROKECOLOR);
558         item.setStartTextColor(item.getTextColor());
559         item.revertToDefault(VisualItem.TEXTCOLOR);
560         item.revertToDefault(VisualItem.ENDTEXTCOLOR);
561         item.setStartFillColor(item.getFillColor());
562         item.revertToDefault(VisualItem.FILLCOLOR);
563         item.revertToDefault(VisualItem.ENDFILLCOLOR);
564     }
565     
566     /**
567      * Remove axis labels no longer being used.
568      */

569     protected void garbageCollect(VisualTable labels) {
570         Iterator JavaDoc iter = labels.tuples();
571         while ( iter.hasNext() ) {
572             VisualItem item = (VisualItem)iter.next();
573             if ( !item.isStartVisible() && !item.isEndVisible() ) {
574                 labels.removeTuple(item);
575             }
576         }
577     }
578     
579     /**
580      * Create a new table for representing axis labels.
581      */

582     protected VisualTable getTable() {
583         TupleSet ts = m_vis.getGroup(m_group);
584         if ( ts == null ) {
585             Schema s = PrefuseLib.getAxisLabelSchema();
586             VisualTable vt = m_vis.addTable(m_group, s);
587             vt.index(VALUE);
588             return vt;
589         } else if ( ts instanceof VisualTable ) {
590             return (VisualTable)ts;
591         } else {
592             throw new IllegalStateException JavaDoc(
593                 "Group already exists, not being used for labels");
594         }
595     }
596     
597 } // end of class AxisLabels
598
Popular Tags