001 /*
002 * Copyright 2007,2008,2009 John C. Gunther
003 *
004 * Licensed under the Apache License, Version 2.0 (the
005 * "License"); you may not use this file except in compliance
006 * with the License. You may obtain a copy of the License at:
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing,
011 * software distributed under the License is distributed on an
012 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific
014 * language governing permissions and limitations under the
015 * License.
016 *
017 */
018 package com.googlecode.gchart.client;
019
020 import com.google.gwt.i18n.client.DateTimeFormat;
021 import com.google.gwt.i18n.client.NumberFormat;
022 import com.google.gwt.user.client.DOM;
023 import com.google.gwt.user.client.Window;
024 import com.google.gwt.dom.client.Element;
025 import com.google.gwt.dom.client.EventTarget;
026 import com.google.gwt.event.dom.client.ClickEvent;
027 import com.google.gwt.event.dom.client.ClickHandler;
028 import com.google.gwt.event.dom.client.HasClickHandlers;
029 import com.google.gwt.event.shared.HandlerRegistration;
030 import com.google.gwt.user.client.Event;
031 import com.google.gwt.user.client.ui.AbsolutePanel;
032 import com.google.gwt.user.client.ui.Composite;
033 import com.google.gwt.user.client.ui.Grid;
034 import com.google.gwt.user.client.ui.HasHTML;
035 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
036 import com.google.gwt.user.client.ui.HasText;
037 import com.google.gwt.user.client.ui.HasVerticalAlignment;
038 import com.google.gwt.user.client.ui.HTML;
039 import com.google.gwt.user.client.ui.Image;
040 import com.google.gwt.user.client.ui.SimplePanel;
041 import com.google.gwt.user.client.ui.UIObject;
042 import com.google.gwt.user.client.ui.Widget;
043 import java.util.ArrayList;
044 import java.util.Date;
045 import com.google.gwt.core.client.GWT;
046
047 /**
048 * A GChart can represent and display a line chart, a bar chart,
049 * a pie chart, an area chart, or a chart that contains arbitrary
050 * combinations of line, bar, pie, and/or area based curves.
051 *
052 * <p>
053 * For detailed examples, with screen shots, visit the
054 * <a href="package-summary.html#ChartGallery">
055 * Chart Gallery</a>.
056 *
057 * <p>
058 * For detailed instructions on how to integrate Client-side GChart
059 * into your GWT application, see
060 * <a href="package-summary.html#InstallingGChart">
061 * Installing Client-side GChart</a>.
062 *
063 * <p>
064 * <b>CSS Style Rule</b>
065 * <ul>
066 * .gchart-GChart { the GChart's primary top-level styles }
067 * </ul>
068 *
069 *
070 * It is sometimes more natural to consider certain CSS
071 * attributes as properties of a GChart Java object. So, GChart
072 * supports "CSS convenience methods" that let you (optionally) use
073 * Java to specify GChart CSS attributes such as
074 * <tt>border-color</tt> and <tt>background-color</tt>. See
075 * {@link #USE_CSS USE_CSS} for a detailed description of these
076 * CSS convenience methods--which won't interfere with standard
077 * CSS-based specifications if you never invoke them.
078 *
079 *
080 **/
081
082 public class GChart extends Composite implements HasClickHandlers {
083
084
085 /**
086 ** Defines the location of a data point's annotation or hover
087 ** annotation (which can be defined by either plain text, HTML,
088 ** or a widget) relative to the location of that point's
089 ** symbol. The "Field Summary"
090 ** section below lists all available annotation locations.
091 ** <p>
092 **
093 ** The default annotation location is {@link
094 ** AnnotationLocation#SOUTH SOUTH} for annotations and
095 ** is symbol-type-dependent for hover annotations. See the
096 ** <tt>setHoverLocation</tt> method for list of these defaults.
097 **
098 ** <p>
099 **
100 ** You can further adjust the position of a point's
101 ** annotation (or hover annotation) by specifying non-zero
102 ** positional shifts via the <tt>setAnnotationXShift</tt>
103 ** and <tt>setAnnotationYShift</tt> (or via the
104 ** <tt>setHoverXShift</tt>, <tt>setHoverYShift</tt>),
105 ** and <tt>setHoverAnnotationSymbolType</tt> methods for
106 ** hover annotations).
107 ** <p>
108 **
109 ** @see Curve.Point#setAnnotationLocation Point.setAnnotationLocation
110 ** @see Curve.Point#setAnnotationXShift Point.setAnnotationXShift
111 ** @see Curve.Point#setAnnotationYShift Point.setAnnotationYShift
112 ** @see Symbol#setHoverLocation Symbol.setHoverLocation
113 ** @see Symbol#setHoverAnnotationSymbolType
114 ** Symbol.setHoverAnnotationSymbolType
115 ** @see Symbol#setHoverXShift Symbol.setHoverXShift
116 ** @see Symbol#setHoverYShift Symbol.setHoverYShift
117 ** @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
118 **
119 **/
120 public static final class AnnotationLocation {
121 // non-public tagging-only locations used by ANCHOR_MOUSE_* symbol types
122 static final AnnotationLocation AT_THE_MOUSE =
123 new AnnotationLocation(0,0);
124 static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_X =
125 new AnnotationLocation(0,0);
126 static final AnnotationLocation AT_THE_MOUSE_SNAP_TO_Y =
127 new AnnotationLocation(0,0);
128 /**
129 ** Specifies that a point's annotation (label) should
130 ** be positioned so as to be centered on the symbol
131 ** used to represent the point.
132 **
133 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
134 **/
135 public static final AnnotationLocation CENTER =
136 new AnnotationLocation(0,0);
137
138 private static final AnnotationLocation north =
139 new AnnotationLocation(0,-1);
140 private static final AnnotationLocation west =
141 new AnnotationLocation(-1, 0);
142 private static final AnnotationLocation south =
143 new AnnotationLocation(0, 1);
144
145 /**
146 ** Specifies that a point's annotation (label) should be
147 ** placed just above, and centered horizontally on,
148 ** vertical bars that grow down from a horizontal
149 ** baseline, and just below, and centered horizontally on,
150 ** vertical bars that grow up from a horizontal baseline.
151 **
152 ** <p>
153 **
154 ** This another name for
155 ** <tt>AnnotationLocation.NORTH</tt>. Its sole purpose is
156 ** to clarify/document the behavior of this location type
157 ** when used in conjunction with curves that employ
158 ** <tt>VBAR_BASELINE_*</tt> symbol types.
159 **
160 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
161 ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
162 **
163 **/
164 public static final AnnotationLocation
165 CLOSEST_TO_HORIZONTAL_BASELINE = north;
166
167 /**
168 ** Specifies that a point's annotation (label) should be
169 ** placed just to the right of, and centered vertically
170 ** on, horizontal bars that grow left from a vertical
171 ** baseline, and just to the left of, and centered
172 ** vertically on, horizontal bars that grow right from a
173 ** vertical baseline.
174 **
175 ** <p>
176 **
177 ** This another name for
178 ** <tt>AnnotationLocation.WEST</tt>. Its sole purpose is
179 ** to clarify/document the behavior of this location type
180 ** when used in conjunction with curves that employ the
181 ** <tt>HBAR_BASELINE_*</tt> symbol types.
182 **
183 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
184 ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
185 **
186 **/
187
188 public static final AnnotationLocation
189 CLOSEST_TO_VERTICAL_BASELINE = west;
190
191 /**
192 ** Specifies that a point's annotation (label) should
193 ** be positioned just to the right of, and vertically
194 ** centered on, the symbol used to represent the
195 ** point.
196 **
197 ** @see Curve.Point#setAnnotationLocation
198 **/
199 public static final AnnotationLocation EAST =
200 new AnnotationLocation(1, 0);
201
202 /**
203 ** Specifies that a point's annotation (label) should be
204 ** placed just below, and centered horizontally on,
205 ** vertical bars that grow down from a horizontal
206 ** baseline, and just above, and centered horizontally on,
207 ** vertical bars that grow up from a horizontal baseline.
208 **
209 ** <p>
210 **
211 ** This another name for
212 ** <tt>AnnotationLocation.SOUTH</tt>. Its sole purpose is
213 ** to clarify/document the behavior of this location type
214 ** when used in conjunction with curves that employ
215 ** <tt>VBAR_BASELINE_*</tt> symbol types.
216 **
217 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
218 ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
219 **
220 **/
221 public static final AnnotationLocation
222 FARTHEST_FROM_HORIZONTAL_BASELINE = south;
223
224 /**
225 ** Specifies that a point's annotation (label) should be
226 ** placed just to the left of, and centered vertically on,
227 ** horizontal bars that grow left from a vertical
228 ** baseline, and just to the right of, and centered
229 ** vertically on, horizontal bars that grow right from a
230 ** vertical baseline.
231 **
232 ** <p>
233 **
234 ** This another name for
235 ** <tt>AnnotationLocation.EAST</tt>. Its sole purpose is
236 ** to clarify/document the behavior of this location type
237 ** when used in conjunction with curves that employ the
238 ** <tt>HBAR_BASELINE_*</tt> family of symbol types.
239 **
240 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
241 ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
242 **
243 **/
244 public static final AnnotationLocation
245 FARTHEST_FROM_VERTICAL_BASELINE = EAST;
246
247
248 /**
249 ** Specifies that a point's annotation (label) should
250 ** be positioned just inside, and centered on, the
251 ** arc side of a pie slice.
252 ** <p>
253 **
254 ** You can move a pie slice's annotation a specific number
255 ** of pixels radially away from (or towards) the pie
256 ** center by passing a positive (or negative) argument to
257 ** the associated <tt>Point</tt>'s
258 ** <tt>setAnnotationXShift</tt> method.
259 **
260 ** <p> This is pie-friendly synonym for, and when used
261 ** with non-pie symbol types will behave exactly the same
262 ** as, <tt>AnnotationLocation.NORTH</tt>
263 **
264 ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
265 ** @see #ON_PIE_ARC ON_PIE_ARC
266 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
267 ** @see AnnotationLocation#NORTH NORTH
268 **/
269 public static final AnnotationLocation INSIDE_PIE_ARC = north;
270
271 /**
272 ** Specifies that a point's annotation (label) should
273 ** be positioned just above, and horizontally centered on,
274 ** the symbol used to represent the point.
275 **
276 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
277 **/
278 public static final AnnotationLocation NORTH = north;
279
280
281 /**
282 ** Specifies that a point's annotation (label) should
283 ** be positioned just to the right of and above,
284 ** the symbol used to represent the
285 ** point.
286 **
287 ** @see Curve.Point#setAnnotationLocation
288 **/
289 public static final AnnotationLocation NORTHEAST =
290 new AnnotationLocation(1, -1);
291
292 /**
293 ** Specifies that a point's annotation (label) should
294 ** be positioned just to the left of and above,
295 ** the symbol used to represent the
296 ** point.
297 **
298 ** @see Curve.Point#setAnnotationLocation
299 **/
300 public static final AnnotationLocation NORTHWEST =
301 new AnnotationLocation(-1, -1);
302
303
304 /**
305 ** Specifies that a point's annotation (label) should
306 ** be centered on the center-point of the
307 ** arc side of a pie slice.
308 ** <p>
309 **
310 ** You can move a pie slice's annotation a specific number
311 ** of pixels radially away from (or towards) the pie
312 ** center by passing a positive (or negative) argument to
313 ** the associated <tt>Point</tt>'s
314 ** <tt>setAnnotationXShift</tt> method.
315 **
316 **
317 **
318 ** <p> This is pie-friendly synonym for, and when used
319 ** with non-pie symbol types will behave exactly the same
320 ** as, <tt>AnnotationLocation.CENTER</tt>
321 **
322 ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
323 ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
324 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
325 ** @see AnnotationLocation#CENTER CENTER
326 **
327 **/
328 public static final AnnotationLocation ON_PIE_ARC = CENTER;
329
330 /**
331 ** Specifies that a point's annotation (label) should
332 ** be positioned just outside, and centered on, the
333 ** arc side of a pie slice.
334 ** <p>
335 **
336 ** You can move a pie slice's annotation a specific number
337 ** of pixels radially away from (or towards) the pie
338 ** center by passing a positive (or negative) argument to
339 ** the associated <tt>Point</tt>'s
340 ** <tt>setAnnotationXShift</tt> method.
341 **
342 ** <p> This is pie-friendly synonym for, and when used
343 ** with non-pie symbol types will behave exactly the same
344 ** as, <tt>AnnotationLocation.SOUTH</tt>
345 **
346 ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
347 ** @see #ON_PIE_ARC ON_PIE_ARC
348 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
349 ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
350 ** @see AnnotationLocation#SOUTH SOUTH
351 **/
352 public static final AnnotationLocation OUTSIDE_PIE_ARC = south;
353
354 /**
355 ** Specifies that a point's annotation (label) should
356 ** be positioned just below, and horizontally centered on,
357 ** the symbol used to represent the point.
358 **
359 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
360 **/
361 public static final AnnotationLocation SOUTH = south;
362
363
364 /**
365 ** Specifies that a point's annotation (label) should
366 ** be positioned just to the right of and below,
367 ** the symbol used to represent the
368 ** point.
369 **
370 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
371 **/
372 public static final AnnotationLocation SOUTHEAST =
373 new AnnotationLocation(1, 1);
374 /**
375 ** Specifies that a point's annotation (label) should
376 ** be positioned just to the left of and below,
377 ** the symbol used to represent the
378 ** point.
379 **
380 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
381 **/
382 public static final AnnotationLocation SOUTHWEST =
383 new AnnotationLocation(-1, 1);
384
385 /**
386 ** Specifies that a point's annotation (label) should
387 ** be positioned just to the left of, and vertically
388 ** centered on, the symbol used to represent the
389 ** point.
390 **
391 ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
392 **/
393 public static final AnnotationLocation WEST = west;
394
395
396
397 // these multiply the width and height of the annotation and
398 // the symbol it is attached to in order to define the
399 // center of the annotation (see equations in later code),
400 // and thus the upper left corner anchoring point.
401 private int heightMultiplier;
402 private int widthMultiplier;
403 private AnnotationLocation(int widthMultiplier,
404 int heightMultiplier) {
405 validateMultipliers(widthMultiplier, heightMultiplier);
406 this.widthMultiplier = widthMultiplier;
407 this.heightMultiplier = heightMultiplier;
408 }
409 // retrieves a static location given its multipliers
410 private static AnnotationLocation getAnnotationLocation(
411 int widthMultiplier, int heightMultiplier) {
412 final AnnotationLocation[][] locationMap = {
413 {NORTHWEST, NORTH, NORTHEAST},
414 {WEST, CENTER, EAST},
415 {SOUTHWEST, SOUTH, SOUTHEAST}};
416 // assumes both multiplier are -1, 0, or 1
417 AnnotationLocation result =
418 locationMap[heightMultiplier+1][widthMultiplier+1];
419 return result;
420 }
421
422 // Negative width or height "turn the symbol inside-out",
423 // requiring a corresponding "reflection" of annotation
424 // location (only needed for baseline-based bar symbols)
425 static AnnotationLocation transform(AnnotationLocation a,
426 int signWidth,
427 int signHeight) {
428 AnnotationLocation result = a;
429 if (signWidth < 0 || signHeight < 0)
430 result = getAnnotationLocation(
431 signWidth*a.widthMultiplier,
432 signHeight*a.heightMultiplier);
433
434 return result;
435 }
436 // These define the alignment of the label within it's
437 // containing 1 x 1 Grid. For example, if this
438 // containing grid is to the left of the labeled
439 // symbol (widthMultiplier==-1) the horizontal
440 // alignment will be ALIGN_RIGHT, so as to bring the
441 // contained label flush against the left edge of the
442 // labeled symbol.
443 HasHorizontalAlignment.HorizontalAlignmentConstant
444 getHorizontalAlignment() {
445 HasHorizontalAlignment.HorizontalAlignmentConstant result;
446 if (widthMultiplier == -1)
447 result = HasHorizontalAlignment.ALIGN_RIGHT;
448 else if (widthMultiplier == 0)
449 result = HasHorizontalAlignment.ALIGN_CENTER;
450 else if (widthMultiplier == 1)
451 result = HasHorizontalAlignment.ALIGN_LEFT;
452 else
453 throw new IllegalStateException(
454 "Invalid widthMultiplier: " + widthMultiplier +
455 " 1, 0, or -1 were expected.");
456 return result;
457 }
458
459 /* Given the x-coordinate at the center of the symbol
460 * that this annotation annotates, the annotation's
461 * width, and the symbol's width, this method returns
462 * the x-coordinate of the upper left corner of
463 * this annotation.
464 */
465 int getUpperLeftX(double x, double w, double symbolW) {
466 int result = (int) Math.round(x +
467 (widthMultiplier * (w + symbolW) - w)/2.);
468 return result;
469 }
470
471 /* analogous to getUpperLeftX, except for the y-coordinate */
472 int getUpperLeftY(double y, double h, double symbolH) {
473 int result = (int) Math.round(y +
474 (heightMultiplier * (h + symbolH) - h)/2.);
475 return result;
476 }
477 // analogous to getHorizontalAlignment
478 HasVerticalAlignment.VerticalAlignmentConstant
479 getVerticalAlignment() {
480 HasVerticalAlignment.VerticalAlignmentConstant result;
481 if (heightMultiplier == -1)
482 result = HasVerticalAlignment.ALIGN_BOTTOM;
483 else if (heightMultiplier == 0)
484 result = HasVerticalAlignment.ALIGN_MIDDLE;
485 else if (heightMultiplier == 1)
486 result = HasVerticalAlignment.ALIGN_TOP;
487 else
488 throw new IllegalStateException(
489 "Invalid heightMultiplier: " + heightMultiplier +
490 " -1, 0, or 1 were expected.");
491 return result;
492 }
493
494
495 /*
496 * This method returns the annotation location whose
497 * "attachment point" keeps the annotation either
498 * completely outside, centered on, or completely inside
499 * (depending on if the heightMultiplier of this annotation
500 * is 1, 0, or -1) the point on the pie's circumference
501 * associated with the given angle.
502 * <p>
503 *
504 * The use of heightMultiplier rather than widthMultiplier
505 * is somewhat arbitrary, but was chosen so that the
506 * NORTH, CENTER, and SOUTH annotation locations have the
507 * same interpretation for a pie slice whose bisecting
508 * radius points due south (due south is the default initial
509 * pie slice orientation) and for a 1px x 1px BOX_CENTER
510 * type symbol positioned at the due south position on the
511 * pie's circumference. As the pie-slice-arc-bisection
512 * point moves clockwise around the pie perimeter, the
513 * attachment point (except for vertically-centered
514 * annotations, which remain centered on the pie arc) also
515 * moves clockwise, but in discrete jumps (e.g. from
516 * NORTH, to NORTHEAST, to EAST, to SOUTHEAST, to SOUTH,
517 * etc. for annotations inside the pie) so the annotation
518 * remains appropriately attached to the center of the
519 * slice's arc as the angle changes.
520 *
521 */
522 AnnotationLocation decodePieLocation(double thetaMid) {
523 // a sin or cos that is small enough so that the
524 // associated angle is horizontal (for sines) or vertical
525 // (for cosines) enough to warrant use of a "centered"
526 // annotation location.
527 final double LOOKS_VERTICAL_OR_HORIZONTAL_DELTA = 0.1;
528 double sinTheta = Math.sin(thetaMid);
529 double cosTheta = Math.cos(thetaMid);
530 int pieTransformedWidthMultiplier = heightMultiplier *
531 ((cosTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 :
532 ((cosTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 : 0));
533 int pieTransformedHeightMultiplier = heightMultiplier *
534 ((sinTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 :
535 ((sinTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 : 0));
536
537 return getAnnotationLocation(pieTransformedWidthMultiplier,
538 pieTransformedHeightMultiplier);
539
540 }
541
542 } // end of class AnnotationLocation
543
544
545 /**
546 ** Represents an axis of the chart, for example, the x,
547 ** y, or y2 axis. An axis consists of the axis itself,
548 ** along with its tick marks, tick labels and gridlines.
549 **
550 ** @see XAxis XAxis
551 ** @see YAxis YAxis
552 ** @see Y2Axis Y2Axis
553 ** @see #getXAxis getXAxis
554 ** @see #getYAxis getYAxis
555 ** @see #getY2Axis getY2Axis
556 **
557 **
558 **/
559 public abstract class Axis {
560 protected boolean isHorizontalAxis; // true for X, false for Y
561 protected int ticksId; // sys curve representing ticks
562 protected int gridlinesId; // sys curve representing gridlines
563 protected int axisId; // sys curve representing axis line
564 protected int axisPosition; // +/-1 for right/left axes
565 protected TickLocation tickLocation = DEFAULT_TICK_LOCATION;
566 private int nCurvesVisibleOnAxis = 0; // # of developer curves on axis.
567 // (count does not include system or
568 // invisible curves)
569
570 void incrementCurves() {nCurvesVisibleOnAxis++;}
571 void decrementCurves() {nCurvesVisibleOnAxis--;}
572
573 protected class AxisLimits {
574 double min; double max; // in user-defined model units
575 AxisLimits(double min, double max) {
576 this.min = min;
577 this.max = max;
578 }
579 boolean equals(AxisLimits al) {
580 boolean result = (al.min == min && al.max == max);
581 return result;
582 }
583 }
584
585 // different initial curr, prev ==> "limits have changed" state
586 private AxisLimits currentLimits = new AxisLimits(
587 Double.MAX_VALUE, -Double.MAX_VALUE);
588 private AxisLimits previousLimits = new AxisLimits(
589 -Double.MAX_VALUE, Double.MAX_VALUE);
590
591 private Widget axisLabel;
592 protected int axisLabelThickness = GChart.NAI;
593 private boolean hasGridlines = false;
594 protected int tickCount = DEFAULT_TICK_COUNT;
595 // axes auto-scale whenever min or max are NaN.
596 protected double axisMax = Double.NaN;
597 protected double axisMin = Double.NaN;
598 // this symbol facilitates rendering of gridlines & axes
599 protected String tickLabelFontColor = DEFAULT_TICK_LABEL_FONT_COLOR;
600 // In CSS font-size pixels. These define the height of each
601 // character; our code relies on the rule of thumb that
602 // character width is approximately 3/5th this height to
603 // obtain a reasonably tight upper bound on tick label widths.
604 protected int tickLabelFontSize = DEFAULT_TICK_LABEL_FONTSIZE;
605 protected String tickLabelFontStyle = DEFAULT_TICK_LABEL_FONT_STYLE;
606 protected String tickLabelFontWeight = DEFAULT_TICK_LABEL_FONT_WEIGHT;
607
608 protected String tickLabelFormat = DEFAULT_TICK_LABEL_FORMAT;
609 protected int tickLabelThickness = GChart.NAI;
610 protected int tickLabelPadding = 0;
611 protected int ticksPerLabel = 1;
612 protected int ticksPerGridline = 1;
613 protected int tickLength = DEFAULT_TICK_LENGTH;
614
615 // this symbol facilitates rendering of labeled tick-marks
616 protected int tickThickness = DEFAULT_TICK_THICKNESS;
617
618 // is axis itself visible (has no impact ticks or their labels)
619 boolean axisVisible = true;
620
621 /**
622 * Adds a tick on this axis at the specified position.
623 * Note that explicitly adding a single tick via this method
624 * will eliminate any implicitly generated ticks associated with the
625 * <tt>setTickCount</tt> method.
626 * <p>
627 * The label associated with this tick will be generated by
628 * applying the format specified via <tt>setTickLabelFormat</tt>
629 * to the specified position.
630 * <p>
631 * This is a convenience method equivalent to
632 * <tt>addTick(tickPosition, thisAxis.formatAsTickLabel(tickPosition), GChart.NAI,
633 * GChart.NAI)</tt>. See {@link #addTick(double,String,int,int)
634 * addTick(tickPosition,tickLabel,widthUpperBound,heightUpperBound)}
635 * for details.
636 *
637 * @param tickPosition the position, in model units,
638 * along this axis at which this tick is displayed.
639 * For example, if the axis range goes from 0 to 100,
640 * a tick at position 50 would appear in the middle of
641 * the axis.
642 *
643 * @see #clearTicks clearTicks
644 * @see #addTick(double,String) addTick(double,String)
645 * @see #addTick(double,String,int,int) addTick(double,String,int,int)
646 * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
647 * @see #formatAsTickLabel formatAsTickLabel
648 * @see #setTickCount setTickCount
649 * @see #setTickLabelFormat setTickLabelFormat
650 * @see #setTickLabelFontStyle setTickLabelFontStyle
651 * @see #setTickLabelFontColor setTickLabelFontColor
652 * @see #setTickLabelFontWeight setTickLabelFontWeight
653 * @see #setTickLabelFontSize setTickLabelFontSize
654 *
655 */
656 public void addTick(double tickPosition) {
657 addTick(tickPosition, formatAsTickLabel(tickPosition));
658 }
659 // adds a labeled tick mark via this Axis' special system tick curve
660 private void addTickAsPoint(double tickPosition, String tickLabel,
661 Widget tickWidget, int widthUpperBound,
662 int heightUpperBound) {
663
664 Curve c = getSystemCurve(ticksId);
665 if (isHorizontalAxis)
666 c.addPoint(tickPosition, axisPosition*Double.MAX_VALUE);
667 else
668 c.addPoint(axisPosition*Double.MAX_VALUE, tickPosition);
669
670 // unlabeled tick--we are done, so return to save time
671 if (null == tickLabel && null == tickWidget)
672 return;
673
674 //add an annotation representing the tick label
675 Curve.Point p = c.getPoint();
676 if (isHorizontalAxis) {
677 // below tick on X, above it on (the future) X2
678 p.setAnnotationLocation(
679 (axisPosition < 0) ? AnnotationLocation.SOUTH :
680 AnnotationLocation.NORTH);
681 if (tickLabelPadding != 0) // padding < 0 is rare but allowed
682 p.setAnnotationYShift(axisPosition*tickLabelPadding);
683 // else stick with default of 0 y-shift
684
685 }
686 else {
687 // to left of tick mark on Y, to right of it on Y2
688 p.setAnnotationLocation(
689 (axisPosition < 0) ? AnnotationLocation.WEST :
690 AnnotationLocation.EAST);
691 if (tickLabelPadding != 0)
692 p.setAnnotationXShift(axisPosition*tickLabelPadding);
693 // else stick with default of 0 x-shift
694 }
695
696
697 if (null != tickLabel)
698 p.setAnnotationText(tickLabel, widthUpperBound, heightUpperBound);
699 else if (null != tickWidget)
700 p.setAnnotationWidget(tickWidget, widthUpperBound, heightUpperBound);
701
702 p.setAnnotationFontSize(getTickLabelFontSize());
703 p.setAnnotationFontStyle(getTickLabelFontStyle());
704 p.setAnnotationFontColor(getTickLabelFontColor());
705 p.setAnnotationFontWeight(getTickLabelFontWeight());
706
707 }
708 /**
709 * Adds a tick at the specified position with the specified
710 * label on this axis, whose width and height are within
711 * the specified upper-bounds.
712 *
713 * <p>
714 * Note that explicitly adding a single tick via this method
715 * will eliminate any auto-generated ticks associated with the
716 * <tt>setTickCount</tt> method.
717 *
718 * <p>
719 * Use this method to specify unusually spaced
720 * tick marks with labels that do not directly
721 * reflect the position (for example, for a logarithmic axis,
722 * or for a bar chart with special keyword-type labels, or
723 * a time axis that places date and time on two separate lines).
724 *
725 * @param tickPosition the position, in model units, along
726 * this axis at which the tick is displayed.
727 * For example, if the axis range goes from 0 to 1,
728 * a tick at position 0.5 would appear in the middle of
729 * the axis.
730 *
731 * @param tickLabel the label for this tick. HTML is
732 * supported in tick labels, but it must be prefixed by
733 * <tt><html></tt>. See the {@link
734 * Curve.Point#setAnnotationText(String,int,int)
735 * setAnnotationText} method for more information.
736 *
737 * @param widthUpperBound an upper bound on the width of
738 * the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
739 * get GChart to estimate this width for you. See the
740 * <tt>setAnnotationText</tt> method for more information.
741 *
742 * @param heightUpperBound an upper bound on the height of
743 * the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
744 * get GChart to estimate this height for you. See the
745 * <tt>setAnnotationText</tt> method for more information.
746 *
747 * @see #clearTicks clearTicks
748 * @see #addTick(double) addTick(double)
749 * @see #addTick(double,String) addTick(double,String)
750 * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
751 * @see #setTickCount setTickCount
752 * @see #setTickLabelFormat setTickLabelFormat
753 * @see #setTickLabelFontSize setTickLabelFontSize
754 * @see #setTickLabelFontStyle setTickLabelFontStyle
755 * @see #setTickLabelFontColor setTickLabelFontColor
756 * @see #setTickLabelFontWeight setTickLabelFontWeight
757 * @see Curve.Point#setAnnotationText(String,int,int)
758 * setAnnotationText
759 * @see Curve.Point#setAnnotationWidget setAnnotationWidget
760 *
761 */
762 public void addTick(double tickPosition, String tickLabel,
763 int widthUpperBound,
764 int heightUpperBound) {
765 chartDecorationsChanged = true;
766 if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
767 Curve cTicks = getSystemCurve(ticksId);
768 cTicks.clearPoints();
769 tickCount = GChart.NAI;
770 }
771 addTickAsPoint(tickPosition, tickLabel, null, widthUpperBound,
772 heightUpperBound);
773 }
774
775 /**
776 * Adds a tick at the specified position with the specified
777 * label on this axis.
778 * <p>
779 *
780 * This is a convenience method equivalent to
781 * <tt>addTick(tickPosition, tickLabel, GChart.NAI,
782 * GChart.NAI)</tt>. Most applications can usually just
783 * use this convenience method. See {@link #addTick(double,String,int,int)
784 * addTick(tickPosition,tickLabel,
785 * widthUpperBound,heightUpperBound)} for the fine print.
786 *
787 * @param tickPosition the position, in model units, along
788 * this axis at which the tick is displayed.
789 *
790 * @param tickLabel the plain text or
791 * (<tt><html></tt>-prefixed) HTML defining the tick's
792 * label.
793 *
794 * @see #addTick(double,String,int,int) addTick(double,String,int,int)
795 * @see #addTick(double,Widget) addTick(double,Widget)
796 *
797 */
798 public void addTick(double tickPosition,
799 String tickLabel) {
800 addTick(tickPosition, tickLabel, GChart.NAI, GChart.NAI);
801 }
802
803 /**
804 * Adds a widget-defined tick label at the specified
805 * position, whose width and height are within
806 * the specified upper-bounds.
807 *
808 *
809 * <p>
810 **
811 ** This method is similar to
812 ** <tt>addTick(double,String,int,int)</tt> except that it
813 ** uses a widget, rather than a string, to define the
814 ** tick's label. Although the string-based method is faster
815 ** on first chart rendering, and uses less memory, the
816 ** widget-based method allows you to change the label
817 ** independently of the chart--potentially bypassing (or
818 ** speeding up) expensive chart updates later on.
819 **
820 ** <p>
821 **
822 ** You might use a widget-based tick label to pop up a
823 ** dialog that allows the user to edit the parameters
824 ** defining the axis (min, max, etc.) whenever they click
825 ** on one of the tick labels on that axis, to define
826 ** hovertext that appears when the user mouses over
827 ** a tick label, to use images for your tick labels, etc.
828 **
829 * @param tickPosition the position, in model units, along
830 * this axis at which the tick is displayed.
831 * For example, if the axis range goes from 0 to 1,
832 * a tick at position 0.5 would appear in the middle of
833 * the axis.
834 *
835 * @param tickWidget the label for this tick, as defined
836 * by any GWT Widget.
837 *
838 * @param widthUpperBound an upper bound on the width of
839 * the widget, in pixels. If this and the next
840 * parameter are omitted, GChart will use
841 * <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
842 *
843 * @param heightUpperBound an upper bound on the height of
844 * the widget, in pixels. If this and the previous
845 * parameter are omitted, GChart will use <tt>
846 * DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
847 *
848 * @see #addTick(double,Widget) addTick(double,Widget)
849 * @see #addTick(double,String,int,int) addTick(double,String,int,int)
850 * @see Curve.Point#setAnnotationWidget setAnnotationWidget
851 * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
852 * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
853 **/
854 public void addTick(double tickPosition,
855 Widget tickWidget,
856 int widthUpperBound,
857 int heightUpperBound) {
858 chartDecorationsChanged = true;
859 if (GChart.NAI != tickCount) { // clear out any auto-generated ticks
860 Curve cTicks = getSystemCurve(ticksId);
861 cTicks.clearPoints();
862 tickCount = GChart.NAI;
863 }
864 addTickAsPoint(tickPosition, null, tickWidget, widthUpperBound,
865 heightUpperBound);
866
867 }
868
869 /**
870 * Adds a Widget-defined tick label at the specified
871 * position. Convenience method equivalent to
872 * <tt>addTick(tickPosition, tickWidget,
873 * DEFAULT_WIDGET_WIDTH_UPPERBOUND,
874 * DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>.
875 *
876 * @param tickPosition the position, in model units, along
877 * this axis at which the tick is displayed.
878 * For example, if the axis range goes from 0 to 1,
879 * a tick at position 0.5 would appear in the middle of
880 * the axis.
881 *
882 * @param tickWidget the label for this tick, as defined
883 * by any GWT Widget.
884 *
885 * @see #addTick(double,Widget,int,int)
886 * addTick(double,Widget,int,int)
887 *
888 */
889 public void addTick(double tickPosition,
890 Widget tickWidget) {
891 addTick(tickPosition, tickWidget,
892 DEFAULT_WIDGET_WIDTH_UPPERBOUND,
893 DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
894 }
895 /**
896 *
897 * Removes all ticks from this axis. Specifically,
898 * erases any ticks that were explicitly specified via
899 * <tt>addTick</tt>, and also sets the tick count to 0.
900 * <p>
901 *
902 * @see #setTickCount setTickCount
903 * @see #addTick(double) addTick(double)
904 * @see #addTick(double,String) addTick(double,String)
905 * @see #addTick(double,String,int,int) addTick(double,String,int,int)
906 * @see #addTick(double,Widget) addTick(double,Widget)
907 * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
908 *
909 */
910 public void clearTicks() {
911 chartDecorationsChanged = true;
912 tickCount = GChart.NAI;
913 Curve c = getSystemCurve(ticksId);
914 c.clearPoints();
915 }
916
917
918 /**
919 * Converts a pixel, client-window coordinate position along this
920 * axis into the model units associated with this axis.
921 * <p>
922 *
923 * For example, if the client coordinate associated with
924 * this axis' midpoint were passed to this method, it would return
925 * <tt>(getAxisMin() + getAxisMax())/2.0</tt>.
926 * <p>
927 *
928 * <small> Note that the client/model coordinate mapping used is as
929 * of the last <tt>update</tt>. Before the first <tt>update</tt>,
930 * this method returns <tt>GChart.NaN</tt>. This method also
931 * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
932 * <tt>getAbsoluteLeft</tt> (for the x axis),
933 * and these GWT methods return 0 if the chart isn't actually
934 * rendered within the browser. So, results likely won't be useful
935 * to you until after the page containing your chart becomes
936 * visible to the user. Since most applications are expected to
937 * invoke this method in response to the user mousing over the
938 * page, these requirements should usually be satisfied. </small>
939 * <p>
940 *
941 * <small> Saurabh Hirani in <a href=
942 * "http://groups.google.com/group/Google-Web-Toolkit/msg/80301715acb6f719">
943 * this GWT Forum post</a> and in GChart <a
944 * href="http://code.google.com/p/gchart/issues/detail?id=21">issue
945 * #22</a> most recently suggested the need for client to model
946 * coordinate mapping. Client to
947 * model conversion was requested earlier in GChart <a
948 * href="http://code.google.com/p/gchart/issues/detail?id=9">issue
949 * #9</a> from <a href="http://yoxel.com">yoxel.com</a>.
950 * </small>
951 *
952 * @param clientCoordinate a pixel-based coordinate that defines
953 * the dimension associated with this axis in the standard
954 * client window coordinates of GWT.
955 *
956 * @return the location defined by the client-coordinate argument,
957 * but converted into the model units associated
958 * with this axis.
959 *
960 * @see #getMouseCoordinate getMouseCoordinate
961 * @see #modelToClient modelToClient
962 * @see #pixelToModel pixelToModel
963 * @see #modelToPixel modelToPixel
964 *
965 *
966 */
967 public abstract double clientToModel(int clientCoordinate);
968
969 // these are used in formatting tick positions into tick labels:
970 private NumberFormat numberFormat =
971 NumberFormat.getFormat(DEFAULT_TICK_LABEL_FORMAT);
972 private DateTimeFormat dateFormat =
973 DateTimeFormat.getShortDateTimeFormat();
974 private final int NUMBER_FORMAT_TYPE = 0;
975 private final int DATE_FORMAT_TYPE = 1;
976 private final int LOG10INVERSE_FORMAT_TYPE = 2;
977 private final int LOG2INVERSE_FORMAT_TYPE = 3;
978 private int tickLabelFormatType = NUMBER_FORMAT_TYPE;
979 /**
980 *
981 * Applies this axis' tick label format to format a given value.
982 *
983 * @return the value formated as per this axis' currently specified
984 * tick label format.
985 *
986 * @see #setTickLabelFormat(String) setTickLabelFormat
987 *
988 */
989 public String formatAsTickLabel(double value) {
990 String result = null;
991 switch (tickLabelFormatType) {
992 case DATE_FORMAT_TYPE:
993 Date transDate = new Date((long) value);
994 result = dateFormat.format(transDate);
995 break;
996 case LOG10INVERSE_FORMAT_TYPE:
997 value = Math.pow(10., value);
998 result = numberFormat.format(value);
999 break;
1000 case LOG2INVERSE_FORMAT_TYPE:
1001 value = Math.pow(2., value);
1002 result = numberFormat.format(value);
1003 break;
1004 default:
1005 result = numberFormat.format(value);
1006 break;
1007 }
1008
1009 return result;
1010 }
1011 /**
1012 * @deprecated
1013 *
1014 * Equivalent to the better-named formatAsTickLabel.
1015
1016 * <p>
1017 *
1018 * @see #formatAsTickLabel formatAsTickLabel
1019 *
1020 */
1021 public String formatNumberAsTickLabel(double value) {
1022 return formatAsTickLabel(value);
1023 }
1024
1025 /** Returns the previously specified label of this axis.
1026 **
1027 ** @return the Widget used as the label of this axis
1028 **
1029 ** @see #setAxisLabel setAxisLabel
1030 **
1031 */
1032 public Widget getAxisLabel() {
1033 return axisLabel;
1034 }
1035
1036 /** Returns the thickness of the axis-label-holding region
1037 ** adjacent to the region allocated for this axis' tick labels.
1038 ** <p>
1039 **
1040 ** Note that if the axis label is <tt>null</tt> (the
1041 ** default) then this method always returns 0, since
1042 ** in that case no rectangular region will be allocated
1043 ** for the axis label.
1044 ** <p>
1045 **
1046 ** @return the thickness of the axis-label-holding
1047 ** region, in pixels.
1048 **
1049 ** @see #setAxisLabelThickness setAxisLabelThickness
1050 **
1051 */
1052 public int getAxisLabelThickness() {
1053 int result = 0;
1054 // Base class implementation is for y axes (x-axis will override).
1055 final int EXTRA_CHARWIDTH = 2; // 1-char padding on each side
1056 final int DEF_CHARWIDTH = 1; // when widget has no text
1057 if (null == getAxisLabel())
1058 result = 0;
1059 else if (GChart.NAI != axisLabelThickness)
1060 result = axisLabelThickness;
1061 else if (getAxisLabel() instanceof HasHTML) {
1062 int charWidth = htmlWidth(
1063 ((HasHTML) (getAxisLabel())).getHTML());
1064 result = (int) Math.round((charWidth + EXTRA_CHARWIDTH) *
1065 getTickLabelFontSize() *
1066 TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1067 }
1068 else if (getAxisLabel() instanceof HasText) {
1069 String text = ((HasText) (getAxisLabel())).getText();
1070 result = (int) Math.round((EXTRA_CHARWIDTH +
1071 ((null==text)?0:text.length())) *
1072 getTickLabelFontSize() *
1073 TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1074 }
1075 else // non-text widget. Not a clue, just use def width
1076 result = (int) Math.round(
1077 (DEF_CHARWIDTH + EXTRA_CHARWIDTH) *
1078 getTickLabelFontSize() *
1079 TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1080 return result;
1081 }
1082 /**
1083 ** Returns the maximum value displayed on this axis.
1084 ** If the explicitly specified maximum value is
1085 ** undefined (<tt>Double.NaN</tt>) the maximum value returned
1086 ** by this function is calculated as the maximum of
1087 ** all of the values either displayed on this axis via
1088 ** points on a curve, or explicitly specified via tick
1089 ** positions.
1090 **
1091 ** @return maximum value visible on this axis, in
1092 ** "model units" (arbitrary, application-specific,
1093 ** units)
1094 **
1095 ** @see #setAxisMax setAxisMax
1096 ** @see #getDataMin getDataMin
1097 ** @see #getDataMax getDataMax
1098 **/
1099 public double getAxisMax() {
1100
1101 if (!(axisMax!=axisMax)) { // x!=x is a faster isNaN
1102 return axisMax;
1103 }
1104 else if (GChart.NAI != tickCount) {
1105 return getDataMax();
1106 }
1107 else {
1108 return Math.max(getDataMax(), getTickMax());
1109 }
1110 }
1111 /**
1112 **
1113 ** Returns the minimum value displayed on this axis.
1114 ** If the minimum value is undefined (<tt>Double.NaN</tt>) the
1115 ** minimum value returned by this function is the
1116 ** minimum of all of the values either displayed on
1117 ** this axis via points on a curve, or explicitly specified
1118 ** via tick positions.
1119 **
1120 ** @return minimum value visible on this axis, in
1121 ** "model units" (arbitrary, application-specific,
1122 ** units)
1123 **
1124 ** @see #setAxisMin setAxisMin
1125 **/
1126 public double getAxisMin() {
1127 if (!(axisMin!=axisMin)) { // x!=x is a faster isNaN
1128 return axisMin; // explicitly set
1129 }
1130 else if (GChart.NAI != tickCount) {
1131 return getDataMin();
1132 }
1133 else {
1134 return Math.min(getDataMin(), getTickMin());
1135 }
1136 }
1137
1138 /** Is axis line visible on the chart? Note that
1139 ** this property only determines the visibility of the axis line
1140 ** itself. It does not control the visibility of the
1141 ** tick marks or tick labels along this axis.
1142 ** <p>
1143 **
1144 ** @return true if the axis line is visible, false otherwise.
1145 **
1146 ** @see #setAxisVisible setAxisVisible
1147 **
1148 **/
1149 public boolean getAxisVisible() {
1150 return axisVisible;
1151 }
1152
1153
1154
1155 /** Returns the maximum data value associated with values
1156 ** represented on this axis. For example, for the left
1157 ** y-axis, this would be the largest y-value of all points
1158 ** contained in curves that are displayed on the left y-axis.
1159 **
1160 ** @return the maximum value associated with values
1161 ** mapped onto this axis.
1162 **
1163 ** @see #getDataMin getDataMin
1164 ** @see #getAxisMax getAxisMax
1165 ** @see #getAxisMin getAxisMin
1166 **
1167 */
1168 public abstract double getDataMax();
1169 /** Returns the minimum data value associated with values
1170 ** represented on this axis. For example, for the left
1171 ** y-axis, this would be the smallest y-value of all points
1172 ** contained in curves that are displayed on the left y-axis.
1173 **
1174 ** @return the minimum value associated with values
1175 ** mapped onto this axis.
1176 **
1177 ** @see #getDataMax getDataMax
1178 ** @see #getAxisMax getAxisMax
1179 ** @see #getAxisMin getAxisMax
1180 **
1181 */
1182 public abstract double getDataMin();
1183
1184 /** Returns the gridline setting previously made with
1185 ** <tt>setHasGridlines</tt>.
1186 **
1187 ** @return true if gridlines have been enabled, false if not.
1188 **
1189 ** @see #setHasGridlines setHasGridlines
1190 **
1191 **/
1192 public boolean getHasGridlines() {
1193 return hasGridlines;
1194 }
1195
1196 /**
1197 * Returns the coordinate along this axis that
1198 * is associated with the last "GChart-tracked" mouse
1199 * location.
1200 * <p>
1201 *
1202 * The coordinate returned is in the "scale" associated
1203 * with the axis. For example, if the axis mininum is
1204 * 0 and the maximum is 100, and the mouse is at the
1205 * axis midpoint, this method would return 50.
1206 * <p>
1207 *
1208 * The main intended use for this method is to allow you to create
1209 * points that, if they have x and y coordinates defined by calling
1210 * this method on appropriate axes, will be positioned on the chart
1211 * at the last GChart-tracked mouse location.
1212 * <p>
1213 *
1214 * As the user moves their mouse over the chart, GChart watches
1215 * those mouse moves and updates it's currently "tracked" mouse
1216 * location. That internally maintained position is the basis for
1217 * the value returned by this method. Note that the actual,
1218 * physical, mouse cursor position could differ from this
1219 * GChart-tracked position because:
1220 *
1221 * <p>
1222 *
1223 * <ol>
1224 *
1225 * <li>The mouse has moved off the chart, and it's
1226 * GChart-tracked location has become undefined
1227 * (this method returns <tt>Double.NaN</tt> in that case)
1228 *
1229 * <li>You have invoked <tt>setHoverTouchingEnabled(false)</tt>
1230 * which means that mouse moves are no longer tracked,
1231 * so the last GChart-tracked mouse location will
1232 * be the last position that the user clicked on.
1233 *
1234 * <li>You have popped up a modal dialog that "eats" mouse
1235 * moves so GChart no longer sees them. In that case,
1236 * the GChart-tracked mouse location is the location the
1237 * mouse was at when the modal dialog popped up.
1238 *
1239 * <li>You are mousing over the opened hover widget (popup).
1240 * Note that, to prevent the user from accidentally "touching"
1241 * nearby points while interacting with the opened
1242 * hover widget, GChart ignores mouse moves over
1243 * the opened hover widget.
1244 *
1245 * <li>Other, similar, reasons.
1246 *
1247 * </ol>
1248 * <p>
1249 *
1250 * In other words, this routine tells you where, for
1251 * hit testing and hover selection feedback purposes,
1252 * GChart considers the mouse to be, not the actual
1253 * physical location of the mouse. Despite the potential
1254 * for differences, in most cases, with the default
1255 * setting of <tt>setHoverTouchingEnabled(true)</tt>,
1256 * and when you are not over the opened hover widget,
1257 * you can use the value returned by this method
1258 * as if it represented the physical mouse location.
1259 * <p>
1260 *
1261 * For an example that uses this method to create
1262 * points at the current mouse location within a very
1263 * simple line chart editor, see <a
1264 * href="package-summary.html#GChartExample22a">the
1265 * Chart Gallery's GChartExample22a</a>. <p>
1266 *
1267 *
1268 * @return the coordinate, projected along this axis, in
1269 * the scale defined by this axis, representing the
1270 * position GChart has currently "tracked" the mouse to,
1271 * or <tt>Double.NaN</tt> if GChart has tracked the mouse
1272 * right off the edge of the chart.
1273 *
1274 * @see #clientToModel clientToModel
1275 * @see #modelToClient modelToClient
1276 * @see #pixelToModel pixelToModel
1277 * @see #modelToPixel modelToPixel
1278 * @see GChart#setHoverTouchingEnabled setHoverTouchingEnabled
1279 *
1280 */
1281 public abstract double getMouseCoordinate();
1282 /**
1283 * Returns the number of visible curves displayed on this axis.
1284 * <p>
1285 *
1286 * @return the number of visible curves on this axis, or <tt>0</tt> if
1287 * there are no visible curves on this axis.
1288 *
1289 * @see Axis#setVisible setVisible
1290 *
1291 */
1292 public int getNCurvesVisibleOnAxis() { return nCurvesVisibleOnAxis; }
1293 /**
1294 ** Returns the number of ticks on this axis.
1295 **
1296 ** @return the number of ticks on this axis.
1297 **
1298 ** @see #setTickCount setTickCount
1299 ** @see #addTick(double) addTick(double)
1300 ** @see #addTick(double,String) addTick(double,String)
1301 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
1302 ** @see #addTick(double,Widget) addTick(double,Widget)
1303 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
1304 ** @see #clearTicks clearTicks
1305 **
1306 **/
1307 public int getTickCount() {
1308 int result = tickCount;
1309 if (GChart.NAI == tickCount) {
1310 Curve c = getSystemCurve(ticksId);
1311 result = c.getNPoints();
1312 }
1313 return result;
1314
1315 }
1316 /**
1317 ** Returns the CSS font-weight specification to be used
1318 ** by this axis' tick labels.
1319 **
1320 ** @return font-weight of this axis' tick labels
1321 **
1322 ** @see #setTickLabelFontWeight setTickLabelFontWeight
1323 **/
1324 public String getTickLabelFontWeight() {
1325 return tickLabelFontWeight;
1326 }
1327 /**
1328 ** Returns the color of the font used to display the
1329 ** text of the tick labels on this axis.
1330 **
1331 **
1332 ** @return CSS color string defining the color of the text of
1333 ** the tick labels for this axis.
1334 **
1335 ** @see #setTickLabelFontColor setTickLabelFontColor
1336 **
1337 ** @see #DEFAULT_TICK_LABEL_FONT_COLOR DEFAULT_TICK_LABEL_FONT_COLOR
1338 **
1339 **
1340 **
1341 **/
1342 public String getTickLabelFontColor() {
1343 return tickLabelFontColor;
1344 }
1345
1346 /**
1347 ** Returns the font-style of the font used to render tick
1348 ** labels on this axis (typically either "italic" or
1349 ** "normal")
1350 **
1351 ** @return the CSS font-style in which tick labels of this axis
1352 ** are rendered.
1353 **
1354 ** @see #setTickLabelFontStyle setTickLabelFontStyle
1355 **/
1356 public String getTickLabelFontStyle() {
1357 return tickLabelFontStyle;
1358 }
1359 /** Returns the CSS font size, in pixels, used for tick labels
1360 ** on this axis.
1361 **
1362 ** @return the tick label font size in pixels
1363 **
1364 ** @see #setTickLabelFontSize setTickLabelFontSize
1365 **/
1366 public int getTickLabelFontSize() {
1367 return tickLabelFontSize;
1368 }
1369
1370 /**
1371 ** Returns the tick label numeric format string for this
1372 ** axis.
1373 **
1374 ** @return numeric format used to generate tick labels.
1375 **
1376 ** @see #setTickLabelFormat setTickLabelFormat
1377 **
1378 **/
1379 public String getTickLabelFormat() {
1380 return tickLabelFormat;
1381 }
1382 /**
1383 ** Returns the amount of padding (blank space) between the
1384 ** ticks and their labels.<p>
1385 **
1386 ** @return amount of padding between ticks and their labels,
1387 ** in pixels.
1388 **
1389 ** @see #setTickLabelPadding setTickLabelPadding
1390 **
1391 **/
1392 public int getTickLabelPadding() {
1393 return tickLabelPadding;
1394 }
1395 // Does real work of public getTickLabelThickness; flag saves time
1396 // during repeated calls made in updateChartDecorations.
1397 int getTickLabelThickness(boolean needsPopulation) {
1398 int maxLength = 0;
1399 int result;
1400 if (tickLabelThickness != GChart.NAI)
1401 result = tickLabelThickness;
1402 else { // use an heuristic to estimate thickness
1403 if (needsPopulation) maybePopulateTicks();
1404 Curve c = getSystemCurve(ticksId);
1405 int nTicks = c.getNPoints();
1406 for (int i=0; i < nTicks; i++) {
1407 String tt = c.getPoint(i).getAnnotationText();
1408 if (null != tt)
1409 maxLength = Math.max(maxLength,
1410 Annotation.getNumberOfCharsWide(tt));
1411 }
1412 result = (int) Math.round(maxLength * tickLabelFontSize *
1413 TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
1414 }
1415 return result;
1416 }
1417
1418 /** Returns the thickness of the band adjacent to
1419 ** this axis that GChart will
1420 ** allocate to hold this axis' tick labels.
1421 ** <p>
1422 **
1423 ** @return width of band, in pixels, GChart will reserve
1424 ** for this axis' tick labels.
1425 **
1426 ** @see #setTickLabelThickness setTickLabelThickness
1427 **
1428 **/
1429 public int getTickLabelThickness() {
1430 int result = getTickLabelThickness(true);
1431 return result;
1432 }
1433
1434 /**
1435 ** Returns the ratio of the number of ticks to the number of
1436 ** ticks that have an associated gridline.
1437 **
1438 ** @return number of ticks per gridline for this axis
1439 **
1440 ** @see #setTicksPerGridline setTicksPerGridline
1441 **
1442 **/
1443 public int getTicksPerGridline() {
1444 return ticksPerGridline;
1445 }
1446 /**
1447 ** Returns the ratio of the number of ticks to the number of
1448 ** labeled ticks.
1449 **
1450 ** @return number of ticks per label.
1451 **
1452 ** @see #setTicksPerLabel setTicksPerLabel
1453 **
1454 **/
1455 public int getTicksPerLabel() {
1456 return ticksPerLabel;
1457 }
1458
1459 /**
1460 * Returns the length of ticks for this axis.
1461 *
1462 * @return the length of this axis' ticks, in pixels.
1463 *
1464 * @see #setTickLength setTickLength
1465 */
1466 public int getTickLength() {
1467 return tickLength;
1468 }
1469
1470 // GChart adds a pixel to even, centered, tick lengths (only
1471 // odd-length HTML ticks can be exactly centered on 1px axis)
1472 int getActualTickLength() {
1473 int result = tickLength;
1474 if (TickLocation.CENTERED == tickLocation &&
1475 0 == (tickLength % 2) && tickLength > 0)
1476 result++;
1477 return result;
1478 }
1479
1480 /**
1481 * Returns relative location of ticks on this axis.
1482 * <p>
1483 *
1484 * @see #setTickLocation setTickLocation
1485 *
1486 * @return <tt>TickLocation.INSIDE</tt>,
1487 * <tt>TickLocation.OUTSIDE</tt>, or
1488 * <tt>TickLocation.CENTERED</tt>
1489 *
1490 */
1491 public TickLocation getTickLocation() {
1492 return tickLocation;
1493 }
1494
1495
1496 /** Returns the amount of space along the axis reserved for
1497 * the tick marks themselves, in pixels.
1498 * <p>
1499 *
1500 * This equals the length of
1501 * the part of the tick that is outside of the plot area.
1502 *
1503 * @see #setTickLength setTickLength
1504 * @see #setTickLabelPadding setTickLabelPadding
1505 * @see #setTickLocation setTickLocation
1506 *
1507 * @return the space GChart will allocate just outside the
1508 * axis to hold any tick marks.
1509 *
1510 */
1511
1512 public int getTickSpace() {
1513 int result;
1514 if (TickLocation.CENTERED == tickLocation)
1515 result = (tickLength+1)/2; // round up to nearest pixel
1516 else if (TickLocation.OUTSIDE == tickLocation)
1517 result = tickLength;
1518 else // INSIDE
1519 result = 0;
1520
1521 return result;
1522 }
1523
1524
1525 /**
1526 * Returns the thickness of ticks for this axis.
1527 *
1528 * @return the thickness of this axis' ticks, in pixels.
1529 *
1530 * @see #setTickThickness setTickThickness
1531 * @see #getTickLength getTickLength
1532 */
1533 public int getTickThickness() {
1534 return tickThickness;
1535 }
1536
1537
1538 /**
1539 * Converts a coordinate position in the model units associated
1540 * with this axis into a corresponding coordinate position
1541 * expressed in standard GWT client-window pixel coordinates.
1542 *
1543 * <p>
1544 *
1545 * For example, consider a completely undecorated chart (no axes,
1546 * tick labels, legend keys, etc.) that exactly fills a
1547 * 1000px wide client window, and whose x-axis min and max
1548 * are 0 and 100. Then <tt>getXAxis().modelToClient(50)</tt> would
1549 * return <tt>500</tt>.
1550 * <p>
1551 *
1552 * <small> Note that the client/model coordinate mapping used is as
1553 * of the last <tt>update</tt>. Before the first <tt>update</tt>,
1554 * this method returns <tt>GChart.NaN</tt>. This method also
1555 * invokes either <tt>getAbsoluteTop</tt> (for the y or y2 axis) or
1556 * <tt>getAbsoluteLeft</tt> (for the x axis),
1557 * and these GWT methods return 0 if the chart isn't actually
1558 * rendered within the browser. So, results likely won't be useful
1559 * to you until after the page containing your chart becomes
1560 * visible to the user. Since most applications are expected to
1561 * invoke this method in response to the user mousing over the
1562 * page, these requirements should usually be satisfied. </small>
1563 *
1564 * @param modelCoordinate the position along this axis defined
1565 * in the model units associated with this axis.
1566 *
1567 * @return a pixel-based coordinate that defines
1568 * the position associated with the argument in the standard
1569 * pixel, client window, coordinates of GWT.
1570 *
1571 * @see #getMouseCoordinate getMouseCoordinate
1572 * @see #clientToModel clientToModel
1573 * @see #pixelToModel pixelToModel
1574 * @see #modelToPixel modelToPixel
1575 *
1576 *
1577 */
1578 public abstract double modelToClient(double modelCoordinate);
1579
1580 /**
1581 * Converts a coordinate position in the model units associated
1582 * with this axis into a corresponding coordinate position
1583 * expressed in GChart's decorated chart pixel coordinates.
1584 * <p>
1585 *
1586 * These
1587 * coordinates have their origin at the upper left corner
1588 * of the decorated GChart, and x pixel-coordinates that increase
1589 * as you move right, and y pixel-coordinates that increase
1590 * as you move down. They are related to GWT's standard
1591 * client window coordinates via the following equations:
1592 *
1593 * <pre>
1594 * xClient = plotPanel.getAbsoluteLeft()
1595 * - Window.getScrollLeft()
1596 * + xPixel;
1597 * yClient = plotPanel.getAbsoluteTop()
1598 * - Window.getScrollTop()
1599 * + yPixel;
1600 * </pre>
1601 * <p>
1602 *
1603 * In the above <tt>plotPanel</tt> is an internal
1604 * <tt>AbsolutePanel</tt>
1605 * GChart creates to hold the entire, decorated, chart. Apart from
1606 * borders and such applied to the GChart as a whole, its
1607 * absolute top and left positions should be the same as
1608 * those of the GChart itself.
1609 * <p>
1610 *
1611 * <i>Tip:</i> In applications that continuously track mouse moves
1612 * over the chart, and where absolute and scroll positions cannot
1613 * change, you can gain a significant performance boost by
1614 * computing the difference between pixel and client coordinates
1615 * once (<tt>modelToPixel(axisMin)-modelToClient(axisMin)</tt>)
1616 * and then adding that difference to the client coordinates
1617 * to get the pixel coordinates, and then using
1618 * <tt>pixelToModel</tt>, instead of using <tt>clientToModel</tt>
1619 * directly, which must repeatedly call GWT's scroll and absolute
1620 * position methods.
1621 *
1622 * <p>
1623 *
1624 *
1625 * For example, for a completely undecorated chart (no tick labels,
1626 * legend keys, etc.) the plot area takes up the entire chart. In
1627 * that case, if the pixel units of the plot area range from
1628 * <tt>0...100</tt> along this axis, and the model coordinates range
1629 * from <tt>0...10</tt> along this axis, then
1630 * <tt>modelToPixel(modelCoordinate)</tt> returns
1631 * <tt>10*modelCoordinate</tt>. <p>
1632 *
1633 * The model/pixel mapping is as of the last <tt>update</tt>;
1634 * this method returns <tt>Double.NaN</tt> before the first
1635 * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
1636 * and <tt>modelToClient</tt>, the GChart does <i>not</i>
1637 * need to be actually rendered within the browser for you to
1638 * use this method--a call to update is sufficient.
1639 * <p>
1640 *
1641 * <i>Tip:</i> If you need to access this mapping before
1642 * the first real update, you can explicitly specify the min and
1643 * max of this axis via <tt>setAxisMin</tt> and
1644 * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
1645 * any curves to the chart (which, since the chart is empty, should
1646 * be very fast). This approach will allow you to convert between
1647 * model and pixel coordinates before the first real update, and
1648 * before the chart is rendered in the browser.
1649 * <p>
1650 *
1651 *
1652 *
1653 * @param modelCoordinate a position on this axis expressed
1654 * in the model units associated with this axis.
1655 *
1656 * @return the distance,
1657 * in pixels, from the left edge (for the x axis) or top
1658 * edge (for the y or y2 axis) of
1659 * the decorated chart to the given position on this axis.
1660 *
1661 * @see #getMouseCoordinate getMouseCoordinate
1662 * @see #clientToModel clientToModel
1663 * @see #modelToClient modelToClient
1664 * @see #modelToPlotAreaPixel modelToClient
1665 * @see #pixelToModel pixelToModel
1666 *
1667 *
1668 */
1669 public abstract double modelToPixel(double modelCoordinate);
1670
1671
1672 /**
1673 * Converts a coordinate position in the model units associated
1674 * with this axis into a corresponding coordinate position
1675 * expressed in GChart's plot area pixel coordinates.
1676 * <p>
1677 *
1678 * These
1679 * coordinates have their origin at the upper left corner
1680 * of the plot area, and x pixel-coordinates that increase
1681 * as you move right, and y pixel-coordinates that increase
1682 * as you move down.
1683 * <p>
1684 *
1685 * The plot area is the rectangular region bounded by the
1686 * chart's axes, and with a size specified via
1687 * <tt>setChartSize</tt>, where the chart's curves are
1688 * typically displayed.
1689 * <p>
1690 *
1691 * Apart from a shift in the origin of the pixel coordinates
1692 * used, this method works just like <tt>modelToPixel</tt>;
1693 * see that method for additional details, tips, and
1694 * restrictions.
1695 *
1696 * @param modelCoordinate a position on this axis expressed
1697 * in the model units associated with this axis.
1698 *
1699 * @return the distance,
1700 * in pixels, from the left edge (for the x axis) or top
1701 * edge (for the y or y2 axis) of
1702 * the plot area to the given position on this axis.
1703 *
1704 * @see #getMouseCoordinate getMouseCoordinate
1705 * @see #plotAreaPixelToModel plotAreaPixelToModel
1706 * @see #modelToPixel modelToPixel
1707 * @see #setChartSize setChartSize
1708 *
1709 */
1710 public abstract double modelToPlotAreaPixel(double modelCoordinate);
1711
1712
1713 /**
1714 * Converts a coordinate position in GChart's decorated
1715 * chart pixel
1716 * coordinates into the model units associated with this axis.
1717 * <p>
1718 *
1719 * GChart's decorated chart pixel
1720 * coordinates have their origin at the upper left corner
1721 * of the decorated GChart, and x pixel-coordinates that increase
1722 * as you move right, and y pixel-coordinates that increase
1723 * as you move down. They are related to GWT's standard
1724 * client window coordinates via the following equations:
1725 *
1726 * <pre>
1727 * xClient = plotPanel.getAbsoluteLeft()
1728 * - Window.getScrollLeft()
1729 * + xPixel;
1730 * yClient = plotPanel.getAbsoluteTop()
1731 * - Window.getScrollTop()
1732 * + yPixel;
1733 * </pre>
1734 * <p>
1735 *
1736 *
1737 * In the above <tt>plotPanel</tt> is an internal
1738 * <tt>AbsolutePanel</tt>
1739 * GChart creates to hold the entire, decorated, chart. Apart from
1740 * borders and such applied to the GChart as a whole, its
1741 * absolute top and left positions should be the same as
1742 * those of the GChart itself.
1743 * <p>
1744 *
1745 * For example, for a completely undecorated chart (no tick labels,
1746 * legend keys, etc.) the plot area takes up the entire chart. In
1747 * that case, if the pixel units of the plot area range from
1748 * <tt>0...100</tt> along this axis, and the model coordinates range
1749 * from <tt>0...10</tt> along this axis, then
1750 * <tt>pixelToModel(pixelCoordinate)</tt> returns
1751 * <tt>pixelCoordinate/10.</tt>. <p>
1752 *
1753 * The model/pixel mapping is as of the last <tt>update</tt>;
1754 * this method returns <tt>Double.NaN</tt> before the first
1755 * <tt>update</tt>. Note that, unlike <tt>clientToModel</tt>
1756 * and <tt>modelToClient</tt>, the GChart does <i>not</i>
1757 * need to be actually rendered within the browser for you to
1758 * use this method.
1759 * <p>
1760 *
1761 * <i>Tip:</i> If you need to access this mapping before
1762 * the first real update, you can explicitly specify the min and
1763 * max of this axis via <tt>setAxisMin</tt> and
1764 * <tt>setAxisMax</tt>, and then call <tt>update</tt> before adding
1765 * any curves to the chart (which, since the chart is empty, should
1766 * be very fast). This approach will allow you to convert between
1767 * model and pixel coordinates before the first real update, and
1768 * before the chart is rendered in the browser.
1769 * <p>
1770 *
1771 * @param pixelCoordinate the distance,
1772 * in pixels, from the left edge (for the x axis) or top
1773 * edge (for the y or y2 axis) of
1774 * the decorated chart to a point on this axis.
1775 *
1776 * @return that same position on this axis expressed in the
1777 * the model units associated with this axis.
1778 *
1779 * @see #getMouseCoordinate getMouseCoordinate
1780 * @see #clientToModel clientToModel
1781 * @see #modelToClient modelToClient
1782 * @see #modelToPixel modelToPixel
1783 * @see #plotAreaPixelToModel plotAreaPixelToModel
1784 *
1785 */
1786 public abstract double pixelToModel(int pixelCoordinate);
1787
1788
1789 /**
1790 * Converts a coordinate position in GChart's plot area
1791 * pixel
1792 * coordinates into the model units associated with this axis.
1793 * <p>
1794 *
1795 * GChart's plot area pixel
1796 * coordinates have their origin at the upper left corner
1797 * of the plot area, and x pixel-coordinates that increase
1798 * as you move right, and y pixel-coordinates that increase
1799 * as you move down.
1800 * <p>
1801 *
1802 * The plot area is the rectangular region bounded by the
1803 * chart's axes, and with a size specified via
1804 * <tt>setChartSize</tt>, where the chart's curves are
1805 * typically displayed.
1806 * <p>
1807 *
1808 * Apart from a shift in the origin of the pixel coordinates
1809 * used, this method works just like <tt>pixelToModel</tt>;
1810 * see that method for additional details, tips, and
1811 * restrictions.
1812 *
1813 * @param pixelCoordinate the distance,
1814 * in pixels, from the left edge (for the x axis) or top
1815 * edge (for the y or y2 axis) of
1816 * the plot area to a point on this axis.
1817 *
1818 * @return that same position on this axis expressed in the
1819 * the model units associated with this axis.
1820 *
1821 * @see #modelToPlotAreaPixel modelToPlotAreaPixel
1822 * @see #pixelToModel pixelToModel
1823 * @see #setChartSize setChartSize
1824 *
1825 */
1826 public abstract double plotAreaPixelToModel(int pixelCoordinate);
1827
1828
1829
1830
1831 /** Specifies the label of this axis.
1832 ** <p>
1833 **
1834 ** This label will be positioned just outside of, and
1835 ** centered lengthwise on, the region adjacent to
1836 ** this axis that GChart reserves for this axis' tick labels.
1837 **
1838 ** @param axisLabel a Widget to use as the label of this axis.
1839 **
1840 ** @see #getAxisLabel getAxisLabel
1841 ** @see #setTickLabelThickness setTickLabelThickness
1842 ** @see #setAxisLabelThickness setAxisLabelThickness
1843 **
1844 */
1845
1846 public void setAxisLabel(Widget axisLabel) {
1847 this.axisLabel = axisLabel;
1848 chartDecorationsChanged = true;
1849 }
1850
1851 /**
1852 * Convenience method equivalent to
1853 * <tt>setAxisLabel(new HTML(html))</tt>
1854 *
1855 * @param html HTML text used to define the axis label
1856 *
1857 * @see #setAxisLabel(Widget) setAxisLabel(Widget)
1858 */
1859 public void setAxisLabel(String html) {
1860 setAxisLabel(new HTML(html));
1861 }
1862
1863 /** Sets the thickness of the axis-label-holding region
1864 ** adjacent to the region allocated for tick labels.<p>
1865 **
1866 ** The axis label widget will be centered in this region.
1867 ** Choose a thickness large enough to hold the largest
1868 ** font size you want users to be able to zoom up to
1869 ** without the axis label spilling over into
1870 ** adjacent regions.
1871 ** <p>
1872 **
1873 ** If the axis label thickness is <tt>GChart.NAI</tt> (the
1874 ** default), and the widget defining the axis label
1875 ** implements <tt>HasHTML</tt> (or <tt>HasText</tt>) then
1876 ** GChart uses a thickness based on the estimated number of
1877 ** non-tag characters in the first <tt><br></tt> or
1878 ** <tt><li></tt>
1879 ** delimited line for y-axis labels, and based on the
1880 ** estimated number of (<tt><br></tt> or
1881 ** <tt><li></tt> delimited)
1882 ** text lines for x-axis labels.<p>
1883 **
1884 ** Note that if the axis label is <tt>null</tt> (its
1885 ** default setting) then no space is allocated for the axis
1886 ** label, regardless of this thickness setting.
1887 ** <p>
1888 **
1889 ** @param thickness the thickness of the axis-label-holding
1890 ** region, in pixels, or <tt>GChart.NAI</tt> to use
1891 ** GChart's character-based default thickness estimates.
1892 **
1893 ** @see #getAxisLabelThickness getAxisLabelThickness
1894 ** @see #setAxisLabel setAxisLabel
1895 */
1896 public void setAxisLabelThickness(int thickness) {
1897 axisLabelThickness = thickness;
1898 chartDecorationsChanged = true;
1899 }
1900
1901 /**
1902 ** Specifies the maximum value visible on this axis.
1903 ** <p>
1904 **
1905 ** Aspects of the chart rendered beyond this maximum will
1906 ** be clipped if the chart's <tt>clipToPlotArea</tt>
1907 ** property is <tt>true</tt>.
1908 **
1909 ** <p>
1910 **
1911 ** If <tt>Double.NaN</tt> is specified, this maximum
1912 ** is auto-determined as described in <tt>getAxisMax</tt>.
1913 **
1914 ** <p> <i>Performance tip:</i> Using auto-determined axis
1915 ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
1916 ** next update, to re-render many chart elements whenever
1917 ** the min or max data value displayed on this axis
1918 ** changes. These (often expensive) re-renderings can be
1919 ** avoided by using explicitly specified axis limits
1920 ** whenever possible. <p>
1921 **
1922 ** @param max maximum value visible on this axis, in "model units"
1923 ** (arbitrary, application-specific, units) or <tt>Double.NaN</tt>
1924 ** (the default value) to use an auto-determined maximum.
1925 **
1926 ** @see #getAxisMax getAxisMax
1927 ** @see #getDataMin getDataMin
1928 ** @see #getDataMax getDataMax
1929 ** @see GChart#setClipToPlotArea setClipToPlotArea
1930 **
1931 **/
1932 public void setAxisMax(double max) {
1933 chartDecorationsChanged = true;
1934 this.axisMax = max;
1935 }
1936 /**
1937 ** Specifies the minimum value of this axis.
1938 ** <p>
1939 **
1940 ** Aspects of the chart rendered at positions before this
1941 ** minimum
1942 ** value will be clipped if the chart's
1943 ** <tt>clipToPlotArea</tt> property is <tt>true</tt>.
1944 ** <p>
1945 **
1946 ** If <tt>Double.NaN</tt> is specified, this minimum
1947 ** is auto-determined as described in <tt>getAxisMin</tt>.
1948 **
1949 ** <p> <i>Performance tip:</i> Using auto-determined axis
1950 ** limits (via <tt>Double.NaN</tt>) forces GChart, at the
1951 ** next update, to re-render many chart elements whenever
1952 ** the min or max data value displayed on this axis
1953 ** changes. These (often expensive) re-renderings can be
1954 ** avoided by using explicitly specified axis limits
1955 ** whenever possible. <p>
1956 **
1957 ** @param min minimum value visible on this axis, in "model units"
1958 ** (arbitrary, application-specific, units), or Double.NaN
1959 ** (the default) to use an auto-determined minimum.
1960 **
1961 ** @see #getAxisMin getAxisMin
1962 ** @see #getDataMin getDataMin
1963 ** @see #getDataMax getDataMax
1964 **
1965 **/
1966 public void setAxisMin(double min) {
1967 // min can change axis label width ==> changes position of plot area
1968 chartDecorationsChanged = true;
1969 this.axisMin = min;
1970 }
1971
1972 /**
1973 ** Defines if this axis is visible. Note that
1974 ** this property only defines the visibility of the axis line
1975 ** itself, it does not control the visibility of
1976 ** tick marks or tick labels associated with the axis.
1977 **
1978 ** <p>
1979 ** <i>Tip:</i>Tick marks can be made invisible by using
1980 ** <tt>setTickThickness</tt> to set the tick thickness
1981 ** to 0. Tick labels can be made invisible by using
1982 ** <tt>setTickLabelFontColor</tt> to set the tick label
1983 ** color to the chart's background color.
1984 ** <p>
1985 **
1986 ** @param axisVisible false to hide axis, true to show it.
1987 **
1988 ** @see #setTickThickness setTickThickness
1989 ** @see #setTickLabelFontColor setTickLabelFontColor
1990 ** @see #getAxisVisible getAxisVisible
1991 **/
1992 public void setAxisVisible(boolean axisVisible) {
1993 chartDecorationsChanged = true;
1994 this.axisVisible = axisVisible;
1995 }
1996
1997 /**
1998 ** Specifies if this axis should have gridlines. When an
1999 ** axis has gridlines, tick marks with indexes <tt>0, N,
2000 ** 2*N,...</tt> where <tt>N</tt> is the value of this axis'
2001 ** <tt>ticksPerGridline</tt> property, are in effect
2002 ** extended across the entire chart.
2003 **
2004 ** @param hasGridlines true to display gridlines,
2005 ** false (the default) to not display them.
2006 **
2007 ** @see #getHasGridlines getHasGridlines
2008 ** @see #setTicksPerGridline setTicksPerGridline
2009 **
2010 **/
2011 public void setHasGridlines(boolean hasGridlines) {
2012 chartDecorationsChanged = true;
2013 this.hasGridlines = hasGridlines;
2014 }
2015 /** Sets the number of ticks to be placed on this axis. The
2016 ** default tick count is 10. Ticks are always evenly
2017 ** spaced across the entire axis, unless explicitly
2018 ** specified via <tt>addTick</tt>.
2019 ** <p>
2020 **
2021 ** Note that setting the tick count overrides (erases)
2022 ** any ticks explicitly specified via <tt>addTick</tt>.
2023 **
2024 ** @param tickCount the number of ticks for this axis.
2025 **
2026 ** @see #getTickCount getTickCount
2027 ** @see #addTick(double) addTick(double)
2028 ** @see #addTick(double,String) addTick(double, String)
2029 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2030 ** @see #addTick(double,Widget) addTick(double,Widget)
2031 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2032 ** @see #setTickLabelFormat setTickLabelFormat
2033 ** @see #setTickLabelFontSize setTickLabelFontSize
2034 ** @see #setTickLabelFontStyle setTickLabelFontStyle
2035 ** @see #setTickLabelFontColor setTickLabelFontColor
2036 ** @see #setTickLabelFontWeight setTickLabelFontWeight
2037 **
2038 **/
2039 public void setTickCount(int tickCount) {
2040 chartDecorationsChanged = true;
2041 getSystemCurve(ticksId).clearPoints(); // eliminate user specified ticks
2042 this.tickCount = tickCount;
2043 }
2044 /**
2045 ** Specifies the weight of the font used in this axis' tick
2046 ** labels.
2047 **
2048 ** @param cssWeight the weight of the font, such as bold,
2049 ** normal, light, 100, 200, ... 900, for tick labels.
2050 **
2051 ** @see #getTickLabelFontWeight getTickLabelFontWeight
2052 ** @see #setTickLabelFormat setTickLabelFormat
2053 ** @see #setTickCount setTickCount
2054 ** @see #addTick(double) addTick(double)
2055 ** @see #addTick(double,String) addTick(double,String)
2056 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2057 ** @see #addTick(double,Widget) addTick(double,Widget)
2058 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2059 ** @see #setTickLabelFontStyle setTickLabelFontStyle
2060 ** @see #setTickLabelFontColor setTickLabelFontColor
2061 ** @see #setTickLabelFontSize setTickLabelFontSize
2062 ** @see #DEFAULT_TICK_LABEL_FONT_WEIGHT DEFAULT_TICK_LABEL_FONT_WEIGHT
2063 **/
2064 public void setTickLabelFontWeight(String cssWeight) {
2065 chartDecorationsChanged = true;
2066 // assure that any existing ticks are updated with new weight
2067 Curve c = getSystemCurve(ticksId);
2068 int nPoints = c.getNPoints();
2069 for (int i = 0; i < nPoints; i++)
2070 c.getPoint(i).setAnnotationFontWeight(cssWeight);
2071 tickLabelFontWeight = cssWeight;
2072 }
2073 /**
2074 ** Specifies the color of the font used to render tick labels
2075 ** for this axis.
2076 **
2077 ** <p>
2078 ** For more information on standard CSS color
2079 ** specifications see the discussion in
2080 ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
2081 ** <p>
2082 **
2083 ** @param cssColor color of the font used to display this
2084 ** axis' tick labels, in standard CSS format.
2085 **
2086 ** @see #getTickLabelFontColor getTickLabelFontColor
2087 ** @see #setTickLabelFormat setTickLabelFormat
2088 ** @see #setTickCount setTickCount
2089 ** @see #addTick(double) addTick(double)
2090 ** @see #addTick(double,String) addTick(double,String)
2091 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2092 ** @see #addTick(double,Widget) addTick(double,Widget)
2093 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2094 ** @see #setTickLabelFontStyle setTickLabelFontStyle
2095 ** @see #setTickLabelFontWeight setTickLabelFontWeight
2096 ** @see #setTickLabelFontSize setTickLabelFontSize
2097 **/
2098 public void setTickLabelFontColor(String cssColor) {
2099 chartDecorationsChanged = true;
2100 Curve c = getSystemCurve(ticksId);
2101 int nPoints = c.getNPoints();
2102 for (int i = 0; i < nPoints; i++)
2103 c.getPoint(i).setAnnotationFontColor(cssColor);
2104 tickLabelFontColor = cssColor;
2105 }
2106
2107 /**
2108 ** Specifies the CSS font-style of this
2109 ** axis' tick labels.
2110 **
2111 ** @param cssStyle any valid CSS font-style, namely,
2112 ** normal, italic, oblique, or inherit.
2113 **
2114 ** @see #getTickLabelFontStyle getTickLabelFontStyle
2115 ** @see #setTickLabelFormat setTickLabelFormat
2116 ** @see #setTickCount setTickCount
2117 ** @see #addTick(double) addTick(double)
2118 ** @see #addTick(double,String) addTick(double,String)
2119 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2120 ** @see #addTick(double,Widget) addTick(double,Widget)
2121 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2122 ** @see #setTickLabelFontColor setTickLabelFontColor
2123 ** @see #setTickLabelFontWeight setTickLabelFontWeight
2124 ** @see #setTickLabelFontSize setTickLabelFontSize
2125 ** @see #DEFAULT_TICK_LABEL_FONT_STYLE
2126 ** DEFAULT_TICK_LABEL_FONT_STYLE
2127 **/
2128 public void setTickLabelFontStyle(String cssStyle) {
2129 chartDecorationsChanged = true;
2130 Curve c = getSystemCurve(ticksId);
2131 int nPoints = c.getNPoints();
2132 for (int i = 0; i < nPoints; i++)
2133 c.getPoint(i).setAnnotationFontStyle(cssStyle);
2134 tickLabelFontStyle = cssStyle;
2135 }
2136
2137 /**
2138 ** Sets the CSS font size for tick labels on this
2139 ** axis, in pixels.
2140 **
2141 ** @param tickLabelFontSize the font size of tick labels
2142 ** displayed on this axis.
2143 **
2144 ** @see #getTickLabelFontSize getTickLabelFontSize
2145 ** @see #setTickLabelFormat setTickLabelFormat
2146 ** @see #setTickCount setTickCount
2147 ** @see #addTick(double) addTick(double)
2148 ** @see #addTick(double,String) addTick(double,String)
2149 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2150 ** @see #addTick(double,Widget) addTick(double,Widget)
2151 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2152 ** @see #setTickLabelFontStyle setTickLabelFontStyle
2153 ** @see #setTickLabelFontColor setTickLabelFontColor
2154 ** @see #setTickLabelFontWeight setTickLabelFontWeight
2155 ** @see GChart#DEFAULT_TICK_LABEL_FONTSIZE DEFAULT_TICK_LABEL_FONTSIZE
2156 **
2157 **/
2158
2159 public void setTickLabelFontSize(int tickLabelFontSize) {
2160 chartDecorationsChanged = true;
2161 Curve c = getSystemCurve(ticksId);
2162 int nPoints = c.getNPoints();
2163 for (int i = 0; i < nPoints; i++)
2164 c.getPoint(i).setAnnotationFontSize(tickLabelFontSize);
2165 this.tickLabelFontSize = tickLabelFontSize;
2166 }
2167
2168 /**
2169 * Specifies a format string to be used in
2170 * converting the numeric values associated with each
2171 * tick on this axis into tick labels. This string must
2172 * follow the conventions of the number format patterns
2173 * used by the GWT <a
2174 * href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/NumberFormat.html">
2175 * NumberFormat</a> class, <i>with three
2176 * exceptions:</i>
2177 * <p>
2178 * <ol>
2179 *
2180 * <li><i>Log10 inverse prefix</i>: If the string begins
2181 * with the prefix <tt>=10^</tt> the value is replaced with
2182 * <tt>pow(10.,value)</tt> and the so-transformed value is
2183 * then formatted using the part of the format string that
2184 * comes after this prefix, which must be a valid GWT
2185 * <tt>NumberFormat</tt> pattern (e.g. "##.##").
2186 * <p>
2187 * For an example of how to use this prefix to create a
2188 * semi-log plot, see <a
2189 * href="package-summary.html#GChartExample04">the
2190 * Chart Gallery's GChartExample04</a>.
2191 * <p>
2192 *
2193 * <li><i>Log2 inverse prefix</i>: If the string begins with
2194 * the prefix <tt>=2^</tt> the value is replaced with
2195 * <tt>pow(2.,value)</tt> and the so-transformed value is
2196 * then formatted using the part of the format string that
2197 * comes after this prefix, which must be a valid GWT
2198 * <tt>NumberFormat</tt> pattern.
2199 * <p>
2200 *
2201 * <li><i>Date casting prefix</i>: If the string begins with
2202 * the prefix <tt>=(Date)</tt> the value is replaced with
2203 * <tt>new Date((long) value)</tt> and the so-transformed
2204 * value is then formatted using the format string that
2205 * comes after this prefix, which must be a valid GWT
2206 * <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/DateTimeFormat.html">
2207 * DateTimeFormat</a> pattern (e.g. "yyyy-MM-dd HH:mm").
2208 * For the special case format string of <tt>"=(Date)"</tt>
2209 * (just the date casting prefix) GChart uses the
2210 * <tt>DateTimeFormat</tt> returned by the
2211 * <tt>DateTimeFormat.getShortDateTimeFormat</tt> method. <p>
2212 *
2213 * Note that the values associated with this Axis must
2214 * represent the number of milliseconds since January 1,
2215 * 1970 (in the GMT time zone) whenever this date
2216 * casting prefix is used. <p>
2217 *
2218 *
2219 * For example, if the x-axis tick label format were
2220 * "=(Date)MMM-dd-yyyy HH", then, for a tick located at the
2221 * x position of 0, the tick label would be "Jan-01-1970 00"
2222 * (on a client in the GMT time zone) and for a tick located
2223 * at the x position of 25*60*60*1000 (one day + one hour,
2224 * in milliseconds) the tick label would be "Jan-02-1970 01"
2225 * (again, on a GMT-based client). Results would be
2226 * shifted appropriately on clients in different time zones.
2227 * <p>
2228 *
2229 * Note that if your chart is based on absolute, GMT-based,
2230 * millisecond times then date labels will change when your
2231 * chart is displayed on clients in different time zones.
2232 * Sometimes, this is what you want. To keep the date labels
2233 * the same in all time zones, convert date labels into Java
2234 * <tt>Date</tt> objects in your client-side code, then use
2235 * the <tt>Date.getTime</tt> method, also in your
2236 * client-side code, to convert those dates into the
2237 * millisecond values GChart requires. The <a
2238 * href="package-summary.html#GChartExample12"> Chart
2239 * Gallery's GChartExample12</a> illustrates how to use this
2240 * second approach to produce a time series chart whose
2241 * date-time labels are the same in all time zones.
2242 *
2243 * <p>
2244 *
2245 * <blockquote><small>
2246 *
2247 * Ben Martin describes an alternative (and more flexible)
2248 * approach to formatting time series tick labels in his <a
2249 * href="http://www.linux.com/feature/132854">GChart
2250 * tutorial</a>. Ben's article, along with Malcolm Gorman's
2251 * related <a
2252 * href="http://groups.google.com/group/Google-Web-Toolkit/msg/6125ce39fd2339ac">
2253 * GWT forum post</a> were the origin of this date
2254 * casting prefix. Thanks! </small></blockquote>
2255 *
2256 * </ol>
2257 * <p>
2258 *
2259 *
2260 * <p> Though HTML text is not supported in the tick label
2261 * format string, you can change the size, weight, style, and
2262 * color of tick label text via the
2263 * <tt>setTickLabelFont*</tt> family of methods. You
2264 * <i>can</i> use HTML in tick labels (e.g. for a multi-line
2265 * x-axis label) but but only if you define each tick label
2266 * explicitly using the <tt>addTick</tt> method.
2267 *
2268 * @param format an appropriately prefixed
2269 * GWT <tt>NumberFormat</tt> compatible or
2270 * GWT <tt>DateTimeFormat</tt> compatible format string that
2271 * defines how to convert tick values into tick labels.
2272 *
2273 * @see #setTickCount setTickCount
2274 * @see #addTick(double) addTick(double)
2275 * @see #addTick(double,String) addTick(double,String)
2276 * @see #addTick(double,String,int,int) addTick(double,String,int,int)
2277 * @see #addTick(double,Widget) addTick(double,Widget)
2278 * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2279 * @see #setTickLabelFontSize setTickLabelFontSize
2280 * @see #setTickLabelFontStyle setTickLabelFontStyle
2281 * @see #setTickLabelFontColor setTickLabelFontColor
2282 * @see #setTickLabelFontWeight setTickLabelFontWeight
2283 * @see #getTickLabelFormat getTickLabelFormat
2284 */
2285 public void setTickLabelFormat(String format) {
2286 // interpret prefixes and create an appropriate formatter
2287 if (!tickLabelFormat.equals(format)) {
2288 chartDecorationsChanged = true;
2289 if (format.startsWith("=(Date)")) {
2290 String transFormat = format.substring("=(Date)".length());
2291 if (transFormat.equals("")) // so "=(Date)" works
2292 dateFormat = DateTimeFormat.getShortDateTimeFormat();
2293 else // e.g. "=(Date)mm/dd/yy hh:mm"
2294 dateFormat = DateTimeFormat.getFormat(transFormat);
2295 tickLabelFormatType = DATE_FORMAT_TYPE;
2296 }
2297 else if (format.startsWith("=10^")) {
2298 String transFormat = format.substring("=10^".length());
2299 numberFormat = NumberFormat.getFormat(transFormat);
2300 tickLabelFormatType = LOG10INVERSE_FORMAT_TYPE;
2301 }
2302 else if (format.startsWith("=2^")) {
2303 String transFormat = format.substring("=2^".length());
2304 numberFormat = NumberFormat.getFormat(transFormat);
2305 tickLabelFormatType = LOG2INVERSE_FORMAT_TYPE;
2306 }
2307 else {
2308 numberFormat = NumberFormat.getFormat(format);
2309 tickLabelFormatType = NUMBER_FORMAT_TYPE;
2310 }
2311 }
2312 // remember original format (for use with the getter)
2313 tickLabelFormat = format;
2314 }
2315
2316 /** Specifies the number of pixels of padding (blank space)
2317 ** between the tick marks and their labels. <p>
2318 **
2319 ** With the default of <tt>0</tt>, each
2320 ** tick label is flush against its tick mark.
2321 **
2322 ** @param tickLabelPadding the amount of padding between
2323 ** tick labels and tick marks, in pixels.
2324 **
2325 **
2326 ** @see #getTickLabelPadding getTickLabelPadding
2327 ** @see #setTickLength setTickLength
2328 ** @see #setTickLocation setTickLocation
2329 **
2330 **/
2331 public void setTickLabelPadding(int tickLabelPadding) {
2332 chartDecorationsChanged = true;
2333 this.tickLabelPadding = tickLabelPadding;
2334 }
2335 /** Specifies the thickness of the region adjacent to
2336 ** this axis that GChart will reserve for purposes of
2337 ** holding this axis' tick labels. <p>
2338 ** <p>
2339 **
2340 ** For vertical axes, this represents the width of the
2341 ** widest tick label, for horizontal axes, this represents
2342 ** the height of highest tick label.
2343 ** <p>
2344 **
2345 **
2346 ** By default, this property has the special "undefined"
2347 ** value <tt>GChart.NAI</tt>. With this value, the
2348 ** companion method <tt>getTickLabelThickness</tt> uses an
2349 ** HTML-based heuristic to estimate the thickness.
2350 **
2351 **
2352 ** @see #getTickLabelThickness getTickLabelThickness
2353 ** @see #setTickLabelFontSize setTickLabelFontSize
2354 ** @see #setTickLocation setTickLocation
2355 ** @see #setTickLabelPadding setTickLabelPadding
2356 ** @see #setAxisLabel setAxisLabel
2357 ** @see GChart#NAI NAI
2358 **
2359 **/
2360 public void setTickLabelThickness(int tickLabelThickness) {
2361 chartDecorationsChanged = true;
2362 this.tickLabelThickness = tickLabelThickness;
2363 }
2364 /** Specifies the ratio of the number of tick marks on the
2365 ** axis, to the number of gridlines on the axis.
2366 ** <p>
2367 **
2368 ** For example, with the default value of 1, every tick has
2369 ** an associated gridline, whereas with a
2370 ** <tt>ticksPerGridline</tt> setting of 2, only the first,
2371 ** third, fifth, etc. ticks have gridlines.
2372 **
2373 ** <p>
2374 **
2375 ** This setting only has an impact when the axis' gridlines
2376 ** are turned on, that is, when this axis'
2377 ** <tt>getHasGridlines</tt> method returns true.
2378 **
2379 ** @see #setHasGridlines setHasGridlines
2380 ** @see #setTickCount setTickCount
2381 ** @see #addTick(double) addTick(double)
2382 ** @see #addTick(double,String) addTick(double,String)
2383 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2384 ** @see #addTick(double,Widget) addTick(double,Widget)
2385 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2386 **
2387 ** @param ticksPerGridline the number of ticks on this
2388 ** axis per "gridline-extended" tick.
2389 **
2390 **/
2391 public void setTicksPerGridline(int ticksPerGridline) {
2392 if (ticksPerGridline <= 0)
2393 throw new IllegalArgumentException("ticksPerGridline=" +
2394 ticksPerGridline + "; ticksPerGridline must be > 0");
2395 chartDecorationsChanged = true;
2396 this.ticksPerGridline = ticksPerGridline;
2397 }
2398 /** Specifies the ratio of the number of tick marks on the
2399 ** axis, to the number of labeled tick marks on the axis.
2400 ** <p>
2401 **
2402 ** For example, with the default value of 1, every tick is
2403 ** labeled, whereas with a <tt>ticksPerLabel</tt> setting
2404 ** of 2, only the first, third, fifth, etc. ticks are
2405 ** labeled.
2406 **
2407 ** <p>
2408 **
2409 ** This setting is only used when tick labels
2410 ** are specified implicitly via <tt>setTickCount</tt>. It
2411 ** is ignored when tick positions and their labels are
2412 ** explicitly specified via <tt>addTick</tt>.
2413 **
2414 ** @see #setTickCount setTickCount
2415 ** @see #addTick(double) addTick(double)
2416 ** @see #addTick(double,String) addTick(double,String)
2417 ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
2418 ** @see #addTick(double,Widget) addTick(double,Widget)
2419 ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
2420 **
2421 ** @param ticksPerLabel the ratio of the number of ticks,
2422 ** to the number of labeled ticks.
2423 **
2424 **/
2425 public void setTicksPerLabel(int ticksPerLabel) {
2426 chartDecorationsChanged = true;
2427 if (ticksPerLabel <= 0)
2428 throw new IllegalArgumentException("ticksPerLabel=" +
2429 ticksPerLabel + "; ticksPerLabel must be > 0");
2430 this.ticksPerLabel = ticksPerLabel;
2431 }
2432
2433 /**
2434 * Sets this axis' tick length. Set the tick length to zero to
2435 * eliminate the tick entirely.
2436 * <p>
2437 *
2438 *
2439 * @param tickLength the length of the tick.
2440 *
2441 * @see #getTickLength getTickLength
2442 * @see #setTickThickness setTickThickness
2443 * @see #setTickLabelPadding setTickLabelPadding
2444 * @see #setTickLocation setTickLocation
2445 *
2446 */
2447 abstract public void setTickLength(int tickLength);
2448
2449
2450 /**
2451 * Specifies the location of the tick marks relative to this
2452 * axis, namely, if tick marks are outside, inside, or
2453 * centered on this axis.
2454 * <p>
2455 *
2456 * @see #getTickLocation getTickLocation
2457 * @see #setTickThickness setTickThickness
2458 * @see #setTickLength setTickLength
2459 * @see #setTickLabelPadding setTickLabelPadding
2460 *
2461 * @param tickLocation Specify either
2462 * <tt>TickLocation.INSIDE</tt>,
2463 * <tt>TickLocation.OUTSIDE</tt>, or
2464 * <tt>TickLocation.CENTERED</tt>
2465 *
2466 */
2467 public void setTickLocation(TickLocation tickLocation) {
2468 this.tickLocation = tickLocation;
2469 chartDecorationsChanged = true;
2470 GChart.Symbol sym = getSystemCurve(ticksId).getSymbol();
2471 if (isHorizontalAxis) {
2472 sym.setSymbolType(
2473 tickLocation.getXAxisSymbolType(axisPosition));
2474 sym.setHeight(getActualTickLength());
2475 }
2476 else {
2477 sym.setSymbolType(
2478 tickLocation.getYAxisSymbolType(axisPosition));
2479 sym.setWidth(getActualTickLength());
2480 }
2481 }
2482
2483
2484 /**
2485 * Sets this axis' tick thickness.
2486 * <p>
2487 *
2488 * @param tickThickness the thickness of the tick.
2489 *
2490 * @see #getTickThickness getTickThickness
2491 * @see #setTickLength setTickLength
2492 * @see #setTickLabelPadding setTickLabelPadding
2493 * @see #setTickLocation setTickLocation
2494 *
2495 */
2496 abstract public void setTickThickness(int tickThickness);
2497 void maybePopulateTicks() {
2498 if (tickCount != GChart.NAI) populateTicks();
2499 }
2500
2501 // fills in the tick positions when auto-generated.
2502 private void populateTicks() {
2503 getSystemCurve(ticksId).clearPoints();
2504 //TODO: It should be possible to control the visibility of each axis,
2505 // including ticks and tick labels, independent of the specifications of
2506 // the tick marks on that axis, and independent of if any curves are
2507 // mapped to that axis or not. A setVisible(Boolean isVisible) as a
2508 // three-way, with null being the current, dependent, defaults, and
2509 // TRUE, FALSE explicitly making the entire axis, including tick marks
2510 // and labels visible or not without having to zero the tick count, add
2511 // dummy curve to the axis, etc. to control axis visibility is needed.
2512 if (XTICKS_ID == ticksId || // x, y ticks are drawn even
2513 YTICKS_ID == ticksId || // if no curves are on these axes
2514 0 < getNCurvesVisibleOnAxis()) {
2515 AxisLimits l = getAxisLimits();
2516 for (int i = 0; i < tickCount; i++) {
2517 // linear interpolation between min and max
2518 double position =(tickCount == 1) ? l.max :
2519 (l.min * ((tickCount-1)-i) + i * l.max)/(tickCount-1.0);
2520 addTickAsPoint(position,
2521 (0 == i % ticksPerLabel) ?
2522 formatAsTickLabel(position) : null, null,
2523 GChart.NAI, GChart.NAI);
2524 }
2525 }
2526 }
2527
2528
2529 // fills in the gridlines; ticks are assumed already populated
2530 void populateGridlines() {
2531 Curve cTicks = getSystemCurve(ticksId);
2532 Curve cGridlines = getSystemCurve(gridlinesId);
2533 cGridlines.clearPoints();
2534 int nTicks = cTicks.getNPoints();
2535 for (int iTick = 0; iTick < nTicks; iTick++) {
2536 if (hasGridlines && (iTick % ticksPerGridline) == 0) {
2537 Curve.Point p = cTicks.getPoint(iTick);
2538 cGridlines.addPoint(p.getX(), p.getY());
2539 }
2540 }
2541 }
2542
2543 protected void getAxisLimits(AxisLimits result) {
2544 // so we get 1-unit changes between adjacent ticks
2545 final int DEFAULT_AXIS_RANGE = DEFAULT_TICK_COUNT-1;
2546 double min = getAxisMin();
2547 double max = getAxisMax();
2548 // Adjust min/max so that special cases, like one-point
2549 // charts, do not have axes that shrink down to a point,
2550 // which would create numerical and visual difficulties.
2551 if ((min!=min) && (max!=max)) { // x!=x is a faster isNaN
2552 // e.g. no data and no explicitly specified ticks
2553 min = 0;
2554 max = min + DEFAULT_AXIS_RANGE;
2555 }
2556 else if ((min!=min) && !(max!=max)) { // x!=x is a faster isNaN
2557 // e.g. no data but only max explicitly set
2558 min = max - DEFAULT_AXIS_RANGE;
2559 }
2560 else if (!(min!=min) && (max!=max)) { // x!=x is a faster isNaN
2561 // e.g. no data but only min explicitly set
2562 max = min + DEFAULT_AXIS_RANGE;
2563 }
2564 else if (min == max) {
2565 // e.g one data point only, or they set min=max
2566 max = min + DEFAULT_AXIS_RANGE;
2567 }
2568 result.min = min;
2569 result.max = max;
2570 }
2571 AxisLimits getAxisLimits() {
2572 getAxisLimits(currentLimits);
2573 return currentLimits;
2574 }
2575
2576 void rememberLimits() {
2577 getAxisLimits(previousLimits);
2578 }
2579 boolean limitsChanged() {
2580 boolean result = !getAxisLimits().equals(previousLimits);
2581 return result;
2582 }
2583
2584
2585 /* similar to getTickText, except for the tick position */
2586 private double getTickPosition(Curve c, int iTick) {
2587 double result;
2588 if (isHorizontalAxis)
2589 result = c.getPoint(iTick).getX();
2590 else
2591 result = c.getPoint(iTick).getY();
2592 return result;
2593 }
2594
2595 // returns the largest, explicitly specified, tick position
2596 private double getTickMax() {
2597 double result = -Double.MAX_VALUE;
2598 Curve c = getSystemCurve(ticksId);
2599 int nTicks = c.getNPoints();
2600 for (int i = 0; i < nTicks; i++)
2601 result = Math.max(result, getTickPosition(c, i));
2602 return result;
2603 }
2604
2605 // returns the smallest, explicitly specified, tick position
2606 private double getTickMin() {
2607 double result = Double.MAX_VALUE;
2608 Curve c = getSystemCurve(ticksId);
2609 int nTicks = c.getNPoints();
2610 for (int i = 0; i < nTicks; i++)
2611 result = Math.min(result, getTickPosition(c, i));
2612 return result;
2613 }
2614
2615
2616 // Same as max, except treats NaN/MAX_VALUE values as "not there"
2617 protected double maxIgnoreNaNAndMaxValue(double x1, double x2) {
2618 double result;
2619 if ((x1!=x1) ||
2620 Double.MAX_VALUE == x1 ||
2621 -Double.MAX_VALUE == x1) // x!=x is a faster isNaN
2622 result = x2;
2623 else if ((x2!=x2) ||
2624 Double.MAX_VALUE == x2 ||
2625 -Double.MAX_VALUE == x2)
2626 result = x1;
2627 else
2628 result = Math.max(x1, x2);
2629 return result;
2630 }
2631 // Same as Math.min, except treats NaN/MAX_VALUE values as "not there"
2632 protected double minIgnoreNaNAndMaxValue(double x1, double x2) {
2633 double result;
2634 if ((x1!=x1) ||
2635 Double.MAX_VALUE == x1 ||
2636 -Double.MAX_VALUE == x1 ) // x!=x is a faster isNaN
2637 result = x2;
2638 else if ((x2!=x2) ||
2639 Double.MAX_VALUE == x2 ||
2640 -Double.MAX_VALUE == x2)
2641 result = x1;
2642 else
2643 result = Math.min(x1, x2);
2644 return result;
2645 }
2646 // does a dummy set of any dynamically determined axis
2647 // limit, so, for update purposes, they are considered
2648 // to have changed.
2649 void invalidateDynamicAxisLimits() {
2650 // x!=x is a faster isNaN
2651 if ((axisMin!=axisMin)) setAxisMin(axisMin);
2652 if ((axisMax!=axisMax)) setAxisMax(axisMax);
2653 }
2654
2655
2656
2657 } // end of class Axis
2658
2659 // creates canvas Widgets GChart needs for *_CANVAS symbol types.
2660 private static GChartCanvasFactory canvasFactory = null;
2661 /**
2662 *
2663 * Tells GChart how to create the canvas widgets it needs
2664 * (specifically, widgets that implement GChart's
2665 * <tt>GChartCanvasLite</tt> interface) to render your
2666 * charts using an external vector graphics library. <p>
2667 *
2668 * You must define a class that implements
2669 * <tt>GChartCanvasFactory</tt> and then pass an instance of that
2670 * class to this method, if you want to have the fast, crisply drawn
2671 * connecting lines, polygonal areas, and 2-D pie slices that only a
2672 * vector graphics library can provide.
2673 * <p>
2674 *
2675 * <small>
2676 * <i>Note:</i> If all of your charts only have
2677 * rectangular elements (e.g. bar charts) GChart will continue
2678 * to render those charts using HTML elements, even if a
2679 * canvas factory is provided. Thus, there is no point
2680 * to defining a canvas factory if all you use GChart for
2681 * is bar charts, scatter plots without connecting lines
2682 * between each point, banded-fill pie slices, and so on.
2683 * <p>
2684 *
2685 * On the other hand, if you need continously connected lines,
2686 * solid-fill pie slices, or solid-fill area charts (all of which
2687 * will also need a <tt>setFillSpacing(0)</tt> to specify continuous
2688 * filling) you will gain substantial performance/quality
2689 * improvements if you add a canvas factory. <p>
2690 * </small>
2691 *
2692 * In detail, to exploit browser-based vector graphics (Mozilla's
2693 * canvas, IE's VML, etc.) rendering in your charts, you must:
2694 *
2695 * <ol>
2696 *
2697 * <li>Import an external GWT canvas library, such
2698 * as provided by the
2699 * <a href="http://code.google.com/p/google-web-toolkit-incubator/">
2700 GWT incubator project's</a> <tt>GWTCanvas</tt> class, into your project.
2701 * For example, our GChart test application uses
2702 * <tt>GWTCanvas</tt>
2703 * and imports it by adding this line to its
2704 * <tt>.gwt.xml</tt> file:
2705 * <p>
2706 * <pre>
2707 * <inherits name='com.google.gwt.widgetideas.GWTCanvas' />
2708 * </pre>
2709 *
2710 * To make this work, we also had to add the
2711 * <a href="http://code.google.com/p/google-web-toolkit-incubator/">
2712 * gwt-incubator.jar</a> file (which contains
2713 * <tt>GWTCanvas</tt>) to our build path via Eclipse's
2714 * "Configure Build Path..." command and to the libraries
2715 * listed in the
2716 * <tt>classpath=...</tt> line of our ant build script's
2717 * <tt>java</tt> task.
2718 *
2719 * <p>
2720 *
2721 * <small>For more on the <tt>GWTCanvas</tt> widget, see the <a
2722 * href="http://code.google.com/p/google-web-toolkit-incubator/wiki/GWTCanvas">
2723 * GWTCanvas Wiki page</a> within the GWT
2724 * incubator site.</small>
2725 *
2726 * <p>
2727 *
2728 * <li>Implement a class that extends <tt>Widget</tt> and
2729 * implements the <tt>GChartCanvasLite</tt> interface that GChart
2730 * expects. Again, in GChart's <tt>GWTCanvas</tt>-based test
2731 * application we use:
2732 *
2733 * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasLite.java}
2734 *
2735 * <li>Create a <tt>GChartCanvasFactory</tt> class that has a
2736 * single <tt>create</tt> method that returns new
2737 * instances of your <tt>GChartCanvasLite</tt> Widget.
2738 * GChart's test application uses:
2739 *
2740 *
2741 * {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GWTCanvasBasedCanvasFactory.java}
2742 *
2743 * <li>Finally pass an instance of that factory to
2744 * GChart via a single invocation of
2745 * <tt>setCanvasFactory</tt>:
2746 *
2747 * <pre>
2748 * static {
2749 * GChart.setCanvasFactory(new GWTCanvasBasedCanvasFactory());
2750 * }
2751 * </pre>
2752 * <p>
2753 *
2754 * <small>Because the above line essentially completes the definition
2755 * of the GChart class (and thus should only be executed once per
2756 * application) a good home for it is in a static initializer, as
2757 * shown above. I recommend placing that initializer within your
2758 * application's <tt>EntryPoint</tt> class.</small>
2759 *
2760 * </ol>
2761 *
2762 * <p>
2763 *
2764 *
2765 * <i>Note:</i> To make things a bit simpler for you, the following
2766 * code combines steps 2, 3, and 4 into a single chunk of easily pasted
2767 * boilerplate:
2768 *
2769 * <p>
2770 * <small><small>
2771 * {@code.sample
2772 * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#1}
2773 * {@code.sample
2774 * ..\..\..\..\..\..\gchartdemoapp\src\com\googlecode\gchart\gchartdemoapp\client\GChartDemoApp.java#2}
2775 * </small></small>
2776 *
2777 * <p>
2778 *
2779 * To see the above boilerplate within a working example application,
2780 * follow the link at the bottom of GChart's
2781 * <a href="http://gchart.googlecode.com/svn/trunk/live-demo/v2_6/com.googlecode.gchart.gchartdemoapp.GChartDemoApp/GChartDemoApp.html">
2782 * live demo page</a> to examine its <tt>EntryPoint</tt> class' code.
2783 *
2784 * <p>
2785 *
2786 * <i>GChart's mixed canvas and HTML rendering</i>
2787 *
2788 * <small><blockquote>
2789 *
2790 * Even with an external canvas factory enabled, many aspects
2791 * of your chart will remain HTML-rendered. In general, GChart
2792 * only renders non-rectangular, "continuously filled" aspects
2793 * of your chart using canvas. Other aspects, such as the bars
2794 * on a bar chart, or the individual rectangular point markers
2795 * on a line chart, as well as all chart text, will remain HTML
2796 * rendered.
2797 * <p>
2798 *
2799 * The rendering mode (HTML-only or HTML+canvas) for each curve is
2800 * defined by if a canvas factory is available and if that curve uses
2801 * the special <tt>setFillSpacing(0)</tt> setting (meaning:
2802 * "continuously filled"). So, to force any curve to be only HTML
2803 * rendered, simply set this fill spacing to a value greater than
2804 * <tt>0</tt>. <p>
2805 *
2806 * HTML rendering offers some features not available with canvas
2807 * rendering. For example, only HTML-rendered curves can overwrite
2808 * the enclosing page without monopolizing mouse events within that
2809 * curve's bounding rectangle, and only HTML-rendered curves can
2810 * define their "fill" via an image. HTML rendered curves can
2811 * sometimes even be more memory-efficent, provided the number of
2812 * pixels greatly exceeds the number of HTML elements. And the HTML
2813 * only rendering option provides a useful least common
2814 * denominator/fall-back, supported by even the most obscure
2815 * browsers.<p>
2816 *
2817 * Note that the internal curves that GChart uses to render tick
2818 * marks, gridlines, etc. are never canvas rendered, because they
2819 * only involve vertical or horizontal rectangles, which GChart
2820 * always renders with HTML.
2821 *
2822 * </blockquote></small>
2823 *
2824 * <p>
2825 *
2826 * Finally, if you are content with GChart's built-in HTML-based
2827 * rendering, or if your charts only use rectangular elements (e.g.
2828 * bar charts) you don't need to bother with any of the steps listed
2829 * above. Your charts will then only depend on the standard GWT
2830 * distribution and the 3,000 or so lines of pure GWT Java that
2831 * implement GChart. <p>
2832 *
2833 * <small> <i>Important</i>: GChart only uses your external
2834 * canvas facility to draw a chart's non-rectangular aspects.
2835 * Given how GChart works, a curve can only have
2836 * non-rectangular aspects if <tt>setFillSpacing</tt> has been
2837 * set to <tt>0</tt> (which implies "continuous filling"), and
2838 * <tt>setFillThickness</tt> has been set to a value <tt>>
2839 * 0</tt>. If you are not seeing crisp, canvas-rendered area,
2840 * line, or pie charts, be sure to check these two settings on
2841 * the curves in question. </small>
2842 *
2843 * @see GChartCanvasFactory GChartCanvasFactory
2844 * @see GChartCanvasLite GChartCanvasLite
2845 * @see #getCanvasFactory getCanvasFactory
2846 * @see GChart.Symbol#setFillSpacing setFillSpacing
2847 * @see GChart.Symbol#setFillThickness setFillThickness
2848 *
2849 */
2850 public static void setCanvasFactory(GChartCanvasFactory factory) {
2851 canvasFactory = factory;
2852 }
2853
2854 /**
2855 * Returns the GChart class' canvas factory, or <tt>null</tt>
2856 * if no canvas factory has been specified.
2857 *
2858 * @return the previously specified canvas factory
2859 *
2860 * @see #setCanvasFactory setCanvasFactory
2861 *
2862 */
2863 public static GChartCanvasFactory getCanvasFactory() {
2864 return canvasFactory;
2865 }
2866
2867 /**
2868 * Represents a curve on a chart, which includes
2869 * information such as the x,y coordinates of each point,
2870 * the symbol used to represent points on the curve, etc.
2871 * <p>
2872 * To create a new curve, use the <tt>GChart.addCurve</tt>
2873 * method.
2874 *
2875 * @see GChart#addCurve() addCurve()
2876 *
2877 */
2878 public class Curve {
2879 private boolean isVisible = true;
2880 private String legendHTML = null;
2881 private ArrayList<Point> points = new ArrayList<Point>();
2882 // symbol defines how every point on this curve is rendered
2883 private Symbol symbol = new Symbol(this);
2884
2885 private YAxisId yAxisId = Y_AXIS;
2886
2887 private boolean isValidated = false;
2888 boolean isValidated() { return isValidated; }
2889 /*
2890 * TestGChart14d revealed that curves.indexOf(curve) could, due to its
2891 * sequential search, create a performance bug if the chart had
2892 * over 100 curves (e.g. the 160 pie chart slices/curves of TestGChart14d)
2893 * <p>
2894 *
2895 * With a little extra bookkeeping during add/remove curve to call these
2896 * methods (and the extra int) this problem was corrected.
2897 *
2898 *
2899 */
2900 private int indexOf = GChart.NAI;
2901 void incrementIndex() {indexOf++;}
2902 void decrementIndex() {indexOf--;}
2903 void clearIndex() {indexOf = GChart.NAI;}
2904 int getIndexOf() {return indexOf;}
2905 // private void assertCurveNotRemoved() {
2906 // if (indexOf == GChart.NAI)
2907 // throw new IllegalStateException(
2908 // "Removed curves should not be modified. " +
2909 // "You removed a curve, but retained a reference " +
2910 // "to that curve, and then tried to modify one of " +
2911 // "its properties after you removed it.");
2912 // }
2913
2914 /*
2915 * No public constructor because curves are always
2916 * contained within, and managed by, their containing
2917 * GChart via its addCurve, removeCurve, and related
2918 * methods.
2919 *
2920 */
2921 Curve(int indexOf) {
2922 super();
2923 this.indexOf = indexOf;
2924 }
2925 /**
2926 * Adds a new point to the curve, at the end of the current
2927 * list of points, with the specified
2928 * coordinates in model-units (arbitrary, application-specific,
2929 * units).
2930 * <p>
2931 *
2932 * GChart gives a special interpretation to the following values:
2933 * <p>
2934 *
2935 * <ol>
2936 *
2937 * <li> If <tt>-Double.MAX_VALUE</tt> is specified for either x or y,
2938 * the point acts as if it were placed at the minimum visible
2939 * x or y position within the plot area.
2940 * <p>
2941 *
2942 * <li> Similarly, if <tt>Double.MAX_VALUE</tt> is specified for
2943 * either x or y, the point acts as if it were placed at the
2944 * maximum visible x or y position within the plot area. <p>
2945 *
2946 * <p>
2947 * <li>If <tt>Double.NaN</tt> is specified for either x or y, the
2948 * point is created, but it will not be visible in the
2949 * charting region.
2950 *
2951 * <p>
2952 * <i>Tip:</i> Connecting lines to/from such
2953 * <tt>Double.NaN</tt> points are elided, so you can use such a
2954 * point to create a break in an otherwise connected curve.
2955 *
2956 * </ol>
2957 *
2958 *
2959 * @param x the x-coordinate of the new point
2960 * @param y the y-coordinate of the new point
2961 *
2962 * @see #getPoint getPoint
2963 * @see #addPoint(int,double,double) addPoint(int,double,double)
2964 * @see #removePoint removePoint
2965 * @see #clearPoints clearPoints
2966 * @see #getNPoints getNPoints
2967 */
2968 public void addPoint(double x, double y) {
2969 invalidate();
2970 points.add(new Point(x, y));
2971 }
2972
2973 /**
2974 * Adds a new point at the specified position in the point
2975 * sequence, increasing the indexes of existing points at or after
2976 * the specified position by 1.
2977 *
2978 * @param iPoint the position that the new point will occupy
2979 * @param x the x-coordinate of the new point (model units)
2980 * @param y the y-coordinate of the new point (model units)
2981 *
2982 * @see #getPoint getPoint
2983 * @see #addPoint(double, double) addPoint(double,double)
2984 * @see #removePoint removePoint
2985 * @see #clearPoints clearPoints
2986 * @see #getNPoints getNPoints
2987 */
2988 public void addPoint(int iPoint, double x, double y) {
2989 invalidate();
2990 points.add(iPoint, new Point(x, y));
2991 }
2992
2993 /**
2994 * Removes every point this curve contains.
2995 *
2996 * @see Point Point
2997 * @see #getPoint getPoint
2998 * @see #addPoint(double, double) addPoint(double,double)
2999 * @see #addPoint(int,double,double) addPoint(int,double,double)
3000 * @see #removePoint removePoint
3001 * @see #getNPoints getNPoints
3002 */
3003 public void clearPoints() {
3004 if (this == getTouchedCurve())
3005 plotPanel.touch(null);
3006 invalidate();
3007 points.clear();
3008 }
3009
3010
3011 /*
3012 * Locates index of vertical or horizontal hit-testing band
3013 * that the given point appears in. The first and last
3014 * "pseudo-bands" are devoted to holding all points that fall
3015 * either to the left of or to the right of (or above or below
3016 * for horizontal banding) the first or last "normal" band
3017 * covering the decorated chart's containing "box".
3018 * <p>
3019 *
3020 * Note that some points just slightly off the right or bottom edge
3021 * may not end up in a pseudo-band, due to the fact that the chart
3022 * width (or height) need not be an even multiple of the (fixed) band
3023 * thickness (the "last band sticking out a bit" effect).
3024 *
3025 */
3026 private int getBand(int iPoint, double bandThickness) {
3027 int result = GChart.NAI;
3028 SymbolType symType = getSymbol().getSymbolType();
3029 double xPx = symType.getCenterX(plotPanel, getSymbol(), iPoint);
3030 if (xPx!=xPx) return result; // NaN points not in any band
3031 double yPx = symType.getCenterY(
3032 plotPanel, getSymbol(), iPoint, onY2());
3033 if (yPx!=yPx) return result; // NaN points not in any band
3034
3035 // now, we've got a point with x,y values in some sort of band
3036
3037 if (getSymbol().isHorizontallyBanded()) {
3038 if (yPx < 0)
3039 result = 0; // off-chart point above chart
3040 else if (yPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
3041 result = bandList.length-1; // off-chart point below chart
3042 else
3043 // inside a normal, chart-covering, band
3044 result = 1 + (int) Math.floor(yPx/bandThickness);
3045 }
3046 else { // vertically banded
3047 if (xPx < 0)
3048 result = 0; // off-chart point to the left
3049 else if (xPx >= (bandList.length-EXTRA_BANDS)*bandThickness)
3050 result = bandList.length-1; // off-chart point to the right
3051 else
3052 // within one of the real bands covering the chart
3053 result = 1 + (int) Math.floor(xPx/bandThickness);
3054 }
3055 return result;
3056 }
3057
3058 /*
3059 * Number of hit-test bands for this curve, for a given band
3060 * thickness.
3061 *
3062 */
3063 private int EXTRA_BANDS = 2; // far left, right (top, bottom) bands
3064 private int getNBands(double bandThickness) {
3065 int result = EXTRA_BANDS;
3066 if (getSymbol().isHorizontallyBanded())
3067 result += (int) Math.ceil(getYChartSize()/bandThickness);
3068 else
3069 result += (int) Math.ceil(getXChartSize()/bandThickness);
3070 return result;
3071 }
3072
3073 /*
3074 * Separates points on this curve into bins associated with
3075 * successive vertical (or horizontal) bands across the entire
3076 * decorated chart.
3077 * <p>
3078 *
3079 * Because busy charts typically distribute points evenly
3080 * across the chart, by jumping to the appropriate band's
3081 * list, we can (usually) greatly accelerate worst case mouse
3082 * hit testing. And because the bin organizing step only
3083 * requires a single pass over all the points (and less than a
3084 * two int memory overhead per point) it should almost always
3085 * be a "good deal" performance-wise (compared to a simple
3086 * full point-list scan with every hit test).<p>
3087 *
3088 * Points are placed into bins based on the (pixel) position
3089 * of the x (or, with horizontal bands, y) at the center of
3090 * the rendered symbol. We choose bin size to guarantee that
3091 * bins are at least as wide (or high, for horizontally banded
3092 * hit testing) as the rendered symbols on this curve. This
3093 * simplifies hit testing, since bins are big enough to assure
3094 * that a single symbol straddles at most two adjacent bands.
3095 * Exploits fact that all symbols on the same curve have the
3096 * same size, and that curves with many points on them tend to
3097 * have smaller sized symbols.
3098 *
3099 * <p>
3100 *
3101 * Note that, for bin placement purposes, pie slices are
3102 * considered to have a "center" equal to the center of the
3103 * pie that contains them, and to have a width and height
3104 * equal to the diameter of the pie containing the slice
3105 * (the "worst-case slice").
3106 *
3107 * <p>
3108 *
3109 * Also note that a symbol whose center is in the right side
3110 * of a vertical band may overlap into the following band, and
3111 * one in the left side may overlap the preceding band. Thus
3112 * during hit testing, we must check not only the lists of
3113 * points in the bands "touched" by the mouse-cursor-centered
3114 * brush, but also 1) the band to the immediate left of the
3115 * leftmost touched band, whenever a left-side sub-band of that band
3116 * is touched by the brush and 2) the band to the immediate
3117 * right of the rightmost touched band, whenever a right-side
3118 * sub-band of that band is touched. The thickness of these left
3119 * and right side sub-bands equals half the symbol width.
3120 * Expanding the brush a half-symbol width on either edge
3121 * is the easiest way to apply these rules. Exactly
3122 * analogous statements apply to horizontal bands. <p>
3123 *
3124 * A minimum band size is enforced to prevent the number of
3125 * bands from growing too large with small symbols. Each
3126 * symbol type defines if vertical or horizontal banding is
3127 * more appropriate, or if brush shape should determine
3128 * banding strategy (as of this writing, only horizontal bar
3129 * symbol types require horizontal hit-test bands). Exploits the
3130 * fact that all symbols have a fixed size for at least one of
3131 * their dimensions (for example, vertical bars have variable
3132 * height but fixed width).<p>
3133 *
3134 * Note: this method must be called after the curve is
3135 * rendered during an update(), to assure that hit-test-bins
3136 * are consistent with rendered curves, and ready for use
3137 * before the first mouse hit testing is done.
3138 * <p>
3139 *
3140 * After running this method, points on this curve within a given
3141 * band can be enumerated as in the following code:
3142 * <p>
3143 *
3144 * <pre>
3145 * Point p = null;
3146 * for (int iPoint = bandList[iBand];
3147 * iPoint != GChart.NAI;
3148 * iPoint = p.getINextInBand()) {
3149 * p = getPoint(iPoint);
3150 * // do something requiring points in a given band...
3151 * }
3152 *
3153 * </pre>
3154 *
3155 */
3156 private int[] bandList = null; // index of first point in each band
3157 private double bandThickness = Double.NaN;
3158 void clearBandList() {bandList = null;}
3159 void bandSeparatePoints() {
3160 bandThickness =
3161 getSymbol().getSymbolType().getBandThickness(plotPanel,
3162 getSymbol(),onY2());
3163 int nBands = getNBands(bandThickness);
3164
3165 if (bandList == null || bandList.length != nBands)
3166 bandList = new int[nBands];
3167 // else bandList already has required length, reuse it.
3168
3169 // all bands contain NAI terminated, empty lists to start with
3170 for (int i = 0; i < bandList.length; i++)
3171 bandList[i] = GChart.NAI;
3172
3173 for (int iPoint = 0; iPoint < getNPoints(); iPoint++) {
3174 int iBand = getBand(iPoint, bandThickness);
3175 Point p = getPoint(iPoint);
3176 if (GChart.NAI == iBand) {
3177 // point isn't rendered at all, so isn't in a band (a next
3178 // link pointing to self means "I'm not in any band"). To let
3179 // us skip over these points quickly during rendering.
3180 p.setINextInBand(iPoint);
3181 }
3182 else {
3183 // Add point to front of list for whatever band it's in
3184 // (note that point order therefore gets reversed).
3185 p.setINextInBand(bandList[iBand]);
3186 bandList[iBand] = iPoint;
3187 }
3188
3189 }
3190 }
3191 /*
3192 * Returns the index of the point on this curve whose rendered
3193 * symbol intersects a rectangle with the specified width and
3194 * height centered on the specified point (this rectangle is
3195 * typically a point selection "brush", centered on the mouse
3196 * cursor). <p>
3197 *
3198 * In the event that more than one point's rendered symbol
3199 * intersects with the specified rectangle, the point whose
3200 * center is closest to the specified rectangle's center is
3201 * returned. In the event of a tie, the point with the largest
3202 * point index is returned. If no point "touches" the rectangle,
3203 * <tt>GChart.NAI</tt> is returned. <p>
3204 *
3205 * Assumes/requires up-to-date <tt>bandList</tt> array and
3206 * related <tt>iNextInBand</tt> indexes (these get defined within
3207 * the <tt>bandSeparatePoints</tt> method).
3208 *
3209 */
3210
3211 int getClosestTouchingPoint(int xBrush, int yBrush) {
3212
3213 int result = GChart.NAI;
3214 // ANCHOR_MOUSE symbol type curves not band separated/hit tested
3215 if (null == bandList) return result;
3216 SymbolType symType = getSymbol().getSymbolType();
3217 double dBest = Double.MAX_VALUE; // closest touching pt's distance^2
3218
3219 int iBandFirst;
3220 int iBandLast;
3221
3222 int brushWidth = symType.getBrushWidth(getSymbol());
3223 /*
3224 * In every tested browser EXCEPT FF3, we don't need the +1 below to
3225 * select a 1px tall, off-chart, symbol with a 1x1 px brush
3226 * (specifically, to select the leftmost vertical bar on TestGChart28).
3227 * The +1 below in effect adds 1 px to the height of the brush to
3228 * workaround this problem.
3229 *
3230 */
3231 int brushHeight = symType.getBrushHeight(getSymbol()) + 1;
3232 AnnotationLocation brushLocation = symType.getBrushLocation(
3233 getSymbol());
3234 int nBands = bandList.length;
3235
3236 // Determine range of bands touched by brush, taking into
3237 // account potential for symbols whose centers are in one
3238 // band to "stick out" into an adjacent band by half-band
3239 // thickening of either end of the brush.
3240 //
3241 // Note that the 0th and (nBand-1)th bands represent
3242 // "pseudo-bands" that hold all points that fall to the left
3243 // or right (or above or below if horizontally banded) the
3244 // rectangle occupied by the decorated chart. The tacit
3245 // assumption is that such completely off-the-chart points
3246 // are rare, so it's OK to bunch them up into just 2 bands.
3247 if (getSymbol().isHorizontallyBanded()) {
3248 // horizontal bars and some curves with "wider than high" brushes
3249 double top = brushLocation.getUpperLeftY(yBrush, brushHeight, 0);
3250 double bottom = top + brushHeight;
3251 top -= bandThickness/2.;
3252 bottom += bandThickness/2.;
3253 iBandFirst = (int) Math.max(0, Math.min(nBands-1,1+Math.floor(
3254 top / bandThickness)));
3255 iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3256 bottom / bandThickness)));
3257 }
3258 else { // vertical bars, some curves with "tall or square" brushes
3259 double left = brushLocation.getUpperLeftX(xBrush, brushWidth, 0);
3260 double right = left + brushWidth;
3261 left -= bandThickness/2.0;
3262 right += bandThickness/2.0;
3263 iBandFirst = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3264 left / bandThickness)));
3265 iBandLast = (int) Math.max(0, Math.min(nBands-1, 1+Math.floor(
3266 right / bandThickness)));
3267 }
3268
3269 // Every point whose symbol touches the brush must be in one
3270 // of these bands. Search them to find closest touching point.
3271 for (int iBand = iBandFirst; iBand <= iBandLast; iBand++) {
3272 Point p = null;
3273 for (int iPoint = bandList[iBand];
3274 iPoint != GChart.NAI;
3275 iPoint=p.getINextInBand()) {
3276 if (iPoint < 0 || iPoint >= getNPoints())
3277 throw new IllegalStateException(
3278 "Inappropriately terminated band-point-list, GChart bug likely. " +
3279 "iPoint=" + iPoint + " nPoints=" + getNPoints() +
3280 " iBand="+iBand+" iBandFirst="+iBandFirst+" iBandLast="+iBandLast +
3281 " xBrush="+xBrush+" yBrush="+yBrush+" brushWidth="+brushWidth +
3282 " brushHeight=" +brushHeight + " bandThickness=" + bandThickness);
3283 p = getPoint(iPoint);
3284 if (symType.isIntersecting(plotPanel, getSymbol(),
3285 iPoint, onY2(),
3286 xBrush, yBrush,
3287 brushWidth, brushHeight)) {
3288 // this point touches the brush (keep it if closest)
3289 double xPoint = symType.getCenterX(plotPanel,
3290 getSymbol(), iPoint);
3291 double yPoint = symType.getCenterY(plotPanel,
3292 getSymbol(), iPoint, onY2());
3293 double dx = getSymbol().xScaleFactor*(xPoint-xBrush);
3294 double dy = getSymbol().yScaleFactor*(yPoint-yBrush);
3295 double d = dx*dx + dy*dy;
3296 if (d < dBest) {
3297 result = iPoint;
3298 dBest = d;
3299 }
3300 else if (d == dBest && iPoint > result) {
3301 // in the case of ties, choose largest point index
3302 // (highest "z-order" -- the one "on top")
3303 result = iPoint;
3304 dBest = d;
3305 }
3306
3307 }
3308 }
3309 }
3310
3311 return result;
3312
3313 }
3314
3315 /**
3316 * @deprecated
3317 *
3318 * This method is equivalent to:
3319 * <p>
3320 *<tt>getSymbol().getHovertextTemplate()</tt>
3321 * <p>
3322 *
3323 * It is retained only for GChart 1.1 compatibility purposes.
3324 *
3325 * @see Symbol#getHovertextTemplate() Symbol.getHovertextTemplate
3326 *
3327 */
3328 public String getHovertextTemplate() {
3329 return symbol.getHovertextTemplate();
3330 }
3331
3332 /**
3333 ** Returns the HTML defining this curve's legend label.
3334 **
3335 ** @return the legend label HTML for this curve
3336 **
3337 ** @see #setLegendLabel setLegendLabel
3338 **
3339 **/
3340 public String getLegendLabel() {
3341 return legendHTML;
3342 }
3343
3344 /**
3345 * Returns the number of points this curve contains.
3346 *
3347 * @return the number of points this curve contains.
3348 *
3349 * @see #getPoint getPoint
3350 * @see #addPoint(double, double) addPoint(double,double)
3351 * @see #addPoint(int,double,double) addPoint(int,double,double)
3352 * @see #removePoint removePoint
3353 * @see #clearPoints clearPoints
3354 */
3355 public int getNPoints() {
3356 return points.size();
3357 }
3358
3359 /**
3360 * Returns a reference to the GChart that contains this
3361 * curve.
3362 *
3363 * @return GChart that contains this curve--its "parent".
3364 *
3365 */
3366 public GChart getParent() {return GChart.this; }
3367
3368 /**
3369 * Convenience method equivalent to <tt>getPoint(getNPoints()-1)</tt>.
3370 * <p>
3371 * This method makes code more readable for the common case when
3372 * you first add a point to the end of a curve, and then modify that
3373 * point's attributes, as illustrated below:
3374 * <p>
3375 * <pre>
3376 * class MyChart extends GChart {
3377 * public MyChart() {
3378 * addCurve();
3379 * for (int i=0; i < 10; i++) {
3380 * getCurve().addPoint(i,i);
3381 * getCurve().getPoint().setAnnotationText("Point " + i);
3382 * }
3383 * update();
3384 * }
3385 * }
3386 * </pre>
3387 *
3388 * @return the point on the curve with the highest integer index
3389 *
3390 * @see #getPoint(int) getPoint(int)
3391 * @see #getNPoints() getNPoints()
3392 *
3393 */
3394 public Point getPoint() {
3395 Point result = getPoint(getNPoints()-1);
3396 return result;
3397 }
3398
3399 /**
3400 * Returns a reference to the point at the specified
3401 * index. The returned reference can be used to modify
3402 * various properties of the point, such as
3403 * its optional annotation (text label).
3404 *
3405 * <p>
3406 * @param iPoint the index of the point to be returned.
3407 * @return a reference to the Point at the specified index.
3408 *
3409 * @see #addPoint(double, double) addPoint(double,double)
3410 * @see #addPoint(int,double,double) addPoint(int,double,double)
3411 * @see #removePoint removePoint
3412 * @see #clearPoints clearPoints
3413 * @see #getNPoints getNPoints
3414 */
3415 public Point getPoint(int iPoint) {
3416 if (iPoint < 0 || iPoint >= points.size())
3417 throw new IllegalArgumentException(
3418 "Point index iPoint=" + iPoint + ". " +
3419 "is either < 0 or >= the number of points on the curve.");
3420 Point result = points.get(iPoint);
3421 return result;
3422 }
3423
3424
3425 /**
3426 * Returns the positional index (within this curve's list of
3427 * points) of the specified point.
3428 * <p>
3429 *
3430 * Returns <tt>GChart.NAI</tt> if the specified point is not found on
3431 * this curve's point list.
3432 *
3433 * <p>
3434 * @param point point whose list position is to be retrieved
3435 * @return position of point on this curve's point list, or
3436 * <tt>GChart.NAI</tt>
3437 * if not on the list.
3438 *
3439 * @see #getPoint() getPoint()
3440 * @see #getPoint(int) getPoint(int)
3441 * @see #addPoint addPoint
3442 * @see #removePoint removePoint
3443 * @see #clearPoints clearPoints
3444 * @see #getNPoints getNPoints
3445 */
3446 public int getPointIndex(Point point) {
3447 int result = points.indexOf(point);
3448 if (-1 == result) result = GChart.NAI;
3449 return result;
3450 }
3451 /**
3452 ** Returns the symbol associated with this curve.
3453 ** <p>
3454 **
3455 ** Though you cannot set the symbol itself (there is no
3456 ** <tt>setSymbol</tt> method) you can have essentially
3457 ** the same effect by setting the <tt>SymbolType</tt> (to get
3458 ** qualitatively different kinds of symbols, e.g.
3459 ** bar-chart bars vs. boxes) and by changing symbol
3460 ** attributes such as background color, height, and
3461 ** width.
3462 **
3463 ** @return the symbol used to represent points on this curve
3464 **
3465 ** @see Symbol#setSymbolType Symbol.setSymbolType
3466 ** @see Symbol#setBackgroundColor Symbol.setBackgroundColor
3467 ** @see Symbol#setBorderWidth Symbol.setBorderWidth
3468 ** @see Symbol#setBorderStyle Symbol.setBorderStyle
3469 ** @see Symbol#setWidth Symbol.setWidth
3470 ** @see Symbol#setHeight Symbol.setHeight
3471 ** @see Symbol#setModelWidth Symbol.setModelWidth
3472 ** @see Symbol#setModelHeight Symbol.setModelHeight
3473 **
3474 **/
3475 public Symbol getSymbol() {
3476 return symbol;
3477 }
3478
3479 /**
3480 * Returns the y-axis (Y_AXIS or Y2_AXIS) this curve is
3481 * plotted on.
3482 *
3483 * @return an identifier, either Y_AXIS, or Y2_AXIS, indicating
3484 * if this curve is plotted on the left (y) or right (y2) y-axis
3485 *
3486 ** @see #setYAxis setYAxis
3487 ** @see GChart#Y_AXIS Y_AXIS
3488 ** @see GChart#Y2_AXIS Y2_AXIS
3489 **
3490 */
3491 public YAxisId getYAxis() {
3492 return yAxisId;
3493 }
3494
3495 /** Is this curve visible on the chart and legend key,
3496 ** or is it hidden from view.
3497 **
3498 ** @return true if the curve is visible, false otherwise.
3499 **
3500 ** @see #setVisible setVisible
3501 **/
3502 public boolean isVisible() {return isVisible;}
3503
3504 /** Convenience method equivalent to <tt>getYAxis()==Y2_AXIS</tt>.
3505 *
3506 * @return true if curve is on second y-axis, else false
3507 *
3508 * @see #getYAxis getYAxis
3509 */
3510 public boolean onY2() {
3511 return yAxisId == Y2_AXIS;
3512 }
3513 /**
3514 * Removes the point at the specified index.
3515 *
3516 * @param iPoint index of point to be removed.
3517 *
3518 * @see #getPoint getPoint
3519 * @see #addPoint(double, double) addPoint(double,double)
3520 * @see #addPoint(int,double,double) addPoint(int,double,double)
3521 * @see #clearPoints clearPoints
3522 * @see #getNPoints getNPoints
3523 */
3524 public void removePoint(int iPoint) {
3525 if (iPoint < 0 || iPoint >= getNPoints())
3526 throw new IllegalArgumentException(
3527 "iPoint=" + iPoint + " iPoint arg must be >= 0 and < " +
3528 getNPoints() + ", the number of points on the curve.");
3529 invalidate();
3530
3531 // simulate user moving away from point before it is deleted
3532 // (this assures that any required hoverCleanup gets called,
3533 // and clears the otherwise dangling reference to the point)
3534 if (plotPanel.touchedPoint == getPoint(iPoint))
3535 plotPanel.touch(null);
3536
3537 points.remove(iPoint);
3538 }
3539
3540 /**
3541 * Removes the given point from this curve.
3542 * <p>
3543 *
3544 * If the given point is not on this curve, or is
3545 * <tt>null</tt>, an exception is thrown.
3546 *
3547 * @param p the point to be removed.
3548 *
3549 *
3550 */
3551 public void removePoint(Point p) {
3552 if (null == p)
3553 throw new IllegalArgumentException("p cannot be null.");
3554 int index = getPointIndex(p);
3555 if (GChart.NAI == index)
3556 throw new IllegalArgumentException("p must be a point on this curve " +
3557 "(whose curveIndex is " + getParent().getCurveIndex(this) + ")");
3558 removePoint(index);
3559 }
3560 /**
3561 ** @deprecated
3562 **
3563 ** This method is equivalent to:
3564 ** <p>
3565 ** <tt>getSymbol().setHovertextTemplate(hovertextTemplate)</tt>
3566 ** <p>
3567 ** It is retained only for GChart 1.1 compatibility purposes.
3568 **
3569 ** @see Symbol#setHovertextTemplate Symbol.setHovertextTemplate
3570 **/
3571 public void setHovertextTemplate(String hovertextTemplate) {
3572 symbol.setHovertextTemplate(hovertextTemplate);
3573 }
3574 /**
3575 ** Sets the HTML that defines the label shown to the
3576 ** right of the icon representing the curve's symbol in
3577 ** the chart's legend.
3578 **
3579 ** <p>
3580 ** Setting the legend label to <tt>null</tt> removes the
3581 ** entire row (the label and the icon) associated with
3582 ** this curve from the chart key.
3583 ** <p>
3584 ** Note that, since <tt>null</tt> is the default, unless
3585 ** you set at least one legend label, no chart key will
3586 ** appear at all.
3587 **
3588 ** @param legendHTML the HTML defining this curve's legend label.
3589 ** or <tt>null</tt> to remove the curve from
3590 ** the legend entirely.
3591 **
3592 ** @see #getLegendLabel getLegendLabel
3593 ** @see GChart#setLegendThickness setLegendThickness
3594 **
3595 **/
3596 public void setLegendLabel(String legendHTML) {
3597 chartDecorationsChanged = true;
3598 this.legendHTML = legendHTML;
3599 }
3600 /**
3601 ** Defines if this curve is visible both in the plotting
3602 ** region and on the legend key.
3603 ** <p>
3604 **
3605 ** <i>Notes:</i>
3606 **
3607 ** <ol>
3608 ** <li>A curve must also have a non-<tt>null</tt> legend label
3609 ** if it is to appear on the legend key.
3610 ** <p>
3611 **
3612 ** <li>Hidden curves are excluded from the computation
3613 ** of any auto-computed axis limits.
3614 **
3615 ** </ol>
3616 **
3617 ** @param isVisible false to hide curve, true to reveal it.
3618 **
3619 ** @see #isVisible() isVisible
3620 ** @see #setLegendLabel setLegendLabel
3621 **
3622 **/
3623 public void setVisible(boolean isVisible) {
3624 /* Axis curve count bookkeeping requires that curve be on the
3625 * list of curves.
3626 * <p>
3627 *
3628 * Developer refs to removed curves could screw up this
3629 * bookkeeping.
3630 * <p>
3631 *
3632 * Though often this would be a developer error, best not to
3633 * throw an exception because some developers could use deleted
3634 * curves as repositories for curve state data, or an event
3635 * sequence might produce setVisible calls through a dangling
3636 * curve reference after a curve had been removed, and best to
3637 * let developer get away with that kind of thing. <p>
3638 *
3639 * No point in invalidating since removed curves can never
3640 * become part of a rendered GChart again.
3641 *
3642 */
3643
3644 if (getIndexOf() == GChart.NAI) {
3645 this.isVisible = isVisible;
3646 return;
3647 }
3648
3649 invalidate();
3650
3651 // hover selection feedback curves (which are system curves)
3652 // never impact curve counts, need to refresh decorations, etc.
3653 if (isSystemCurve()) {
3654 this.isVisible = isVisible;
3655 return;
3656 }
3657
3658 if (this.isVisible != isVisible) {
3659 Axis yaxis = (getYAxis() == Y_AXIS) ?
3660 GChart.this.getYAxis() :
3661 GChart.this.getY2Axis();
3662 boolean axisCreatedOrDestroyed;
3663 if (isVisible) {
3664 axisCreatedOrDestroyed =
3665 (yaxis.getNCurvesVisibleOnAxis() == 0);
3666 getXAxis().incrementCurves();
3667 yaxis.incrementCurves();
3668 }
3669 else {
3670 getXAxis().decrementCurves();
3671 yaxis.decrementCurves();
3672 axisCreatedOrDestroyed =
3673 (yaxis.getNCurvesVisibleOnAxis() == 0);
3674 }
3675
3676 if ((null != getLegendLabel() && isLegendVisible()) ||
3677 axisCreatedOrDestroyed)
3678 chartDecorationsChanged = true;
3679
3680 this.isVisible = isVisible;
3681 }
3682 }
3683
3684 /** Sets the y-axis that this curve is plotted on.
3685 ** <p>
3686 ** @param axisId must be either GChart.Y_AXIS or
3687 ** GChart.Y2_AXIS
3688 **
3689 ** @see #getYAxis getYAxis
3690 ** @see GChart#Y_AXIS Y_AXIS
3691 ** @see GChart#Y2_AXIS Y2_AXIS
3692 **
3693 **
3694 **/
3695 public void setYAxis(YAxisId axisId) {
3696 invalidate();
3697 if (isSystemCurve()) {
3698 yAxisId = axisId;
3699 }
3700 else if (axisId != yAxisId) {
3701 if (axisId == Y2_AXIS) { // from Y to Y2
3702 GChart.this.getYAxis().decrementCurves();
3703 GChart.this.getY2Axis().incrementCurves();
3704 }
3705 else { // from Y2 to Y
3706 GChart.this.getY2Axis().decrementCurves();
3707 GChart.this.getYAxis().incrementCurves();
3708 }
3709 yAxisId = axisId;
3710 }
3711 }
3712
3713 // Is this specific curve actually clipped to the plot area?
3714 private boolean getActuallyClippedToPlotArea() {
3715 boolean result = getClipToPlotArea();
3716 if (result) {
3717 // decorative, hover feedback curves are never clipped
3718 int rpIndex = getRenderingPanelIndex(getIndexOf());
3719 if (PlotPanel.DECORATIVE_RENDERING_PANEL_INDEX == rpIndex ||
3720 isHoverFeedbackRenderingPanel(rpIndex))
3721 result = false;
3722 }
3723 return result;
3724 }
3725
3726 /*
3727 * Is this curve one of GChart's special, internally created, system
3728 * curves? These curves can't be directly accessed by users, and are
3729 * used by GChart to render special features of the chart, such as
3730 * the hover selection cursors, titles, footnotes, etc.
3731 *
3732 */
3733 private boolean isSystemCurve() {
3734 // negative curve indexes are reserved for system curves
3735 boolean result = (indexOf != GChart.NAI) &&
3736 externalCurveIndex(indexOf) < 0;
3737 return result;
3738 }
3739 // renders the specified point of this curve on the given panel
3740 void realizePoint(PlotPanel pp,
3741 GraphicsRenderingPanel grp,
3742 AnnotationRenderingPanel arp,
3743 int iPoint) {
3744 Point p = points.get(iPoint);
3745 double x = p.getX();
3746 double y = p.getY();
3747 // skip points at undefined locations
3748 if ((x!=x) || (y!=y)) return; // x!=x is a faster isNaN
3749 double prevX = Double.NaN;
3750 double prevY = Double.NaN;
3751 if (iPoint > 0) {
3752 Point prevP = points.get(iPoint-1);
3753 prevX = prevP.getX();
3754 prevY = prevP.getY();
3755 }
3756 double nextX = Double.NaN;
3757 double nextY = Double.NaN;
3758 Point nextP = null;
3759 if (iPoint < getNPoints()-1) {
3760 nextP = points.get(iPoint+1);
3761 nextX = nextP.getX();
3762 nextY = nextP.getY();
3763 }
3764
3765 // if point was not assigned to any band, it's not drawn
3766 // at all (undefined x or y, or off chart entirely)
3767 boolean drawMainSymbol = (p.getINextInBand() != iPoint);
3768
3769 getSymbol().realizeSymbol(pp, grp, arp, p.getAnnotation(), onY2(),
3770 getActuallyClippedToPlotArea(),
3771 getClipToDecoratedChart(),
3772 drawMainSymbol,
3773 x, y, prevX, prevY, nextX, nextY);
3774 // }
3775 }
3776 /**
3777 ** Represents a single point on one of the chart's
3778 ** curves. This includes the x, y values of the point in
3779 ** "model coordinates" (arbitrary, application-specific,
3780 ** units), as well as an optional annotation (text label)
3781 ** for the point.
3782 ** <p>
3783 ** To create points, use a curve's <tt>addPoint</tt> method.
3784 **
3785 ** @see Curve#addPoint addPoint
3786 **/
3787 public class Point {
3788
3789 // x, y location (user coordinates) of point
3790 // (points are drawn using the containing curve's symbol)
3791 private double x;
3792 private double y;
3793 Annotation annotation = null;
3794 // Points to index of next point in a vertical or horizontal
3795 // band (used by the <tt>bandSeparatePoints</tt> method).
3796 private int iNextInBand = GChart.NAI;
3797 int getINextInBand() { return iNextInBand;}
3798 void setINextInBand(int iNext) {iNextInBand = iNext;}
3799 Point(double x, double y) {
3800 this.x = x;
3801 this.y = y;
3802 }
3803
3804 /**
3805 ** Returns true if annotation will be rendered in a bold,
3806 ** or false if in normal, weight font.
3807 **
3808 ** @return if this annotation is in bold or not.
3809 **
3810 ** @see #setAnnotationFontWeight setAnnotationFontWeight
3811 **/
3812 public String getAnnotationFontWeight() {
3813 if (null == annotation) annotation = new Annotation();
3814 return annotation.getFontWeight();
3815 }
3816
3817 /**
3818 ** Returns the color of the font used to display the point's
3819 ** annotation text.
3820 **
3821 ** @return CSS color string defining the annotation's color
3822 **
3823 ** @see #setAnnotationFontColor setAnnotationFontColor
3824 **/
3825 public String getAnnotationFontColor() {
3826 if (null == annotation) annotation = new Annotation();
3827 return annotation.getFontColor();
3828 }
3829
3830
3831 /**
3832 ** Returns the CSS font-style in which the text of this
3833 ** annotation will be rendered.
3834 **
3835 ** @return the font-style used by this annotation (italic,
3836 ** normal, etc.)
3837 **
3838 ** @see #setAnnotationFontStyle setAnnotationFontStyle
3839 **/
3840 public String getAnnotationFontStyle() {
3841 if (null == annotation) annotation = new Annotation();
3842 return annotation.getFontStyle();
3843 }
3844
3845 /**
3846 ** Returns the CSS font size of this point's annotation
3847 ** (text label), in pixels.
3848 **
3849 ** @return the font size of this point's annotation.
3850 **
3851 ** @see #setAnnotationFontSize setAnnotationFontSize
3852 **/
3853 public int getAnnotationFontSize() {
3854 if (null == annotation) annotation = new Annotation();
3855 return annotation.getFontSize();
3856 }
3857
3858
3859
3860 /** Returns the previously specified location, relative
3861 ** to the symbol representing the point, of the
3862 ** annotation (text label) associated with this point.
3863 **
3864 ** @return relative location of the point's annotation
3865 **
3866 ** @see #setAnnotationLocation setAnnotationLocation
3867 **
3868 **/
3869 public AnnotationLocation getAnnotationLocation() {
3870 if (null == annotation) annotation = new Annotation();
3871 AnnotationLocation result = annotation.getLocation();
3872 if (null == result) result =
3873 getParent().getSymbol().getSymbolType().defaultAnnotationLocation();
3874 return result;
3875 }
3876 /**
3877 ** Returns the text of this point's annotation.
3878 **
3879 ** @return the text of the annotation, or <tt>null</tt> if this
3880 ** point either lacks an annotation or uses a widget-based
3881 ** annotation.
3882 **
3883 ** @see #setAnnotationText setAnnotationText
3884 **
3885 **/
3886 public String getAnnotationText() {
3887 if (null == annotation) annotation = new Annotation();
3888 return annotation.getText();
3889 }
3890
3891 /**
3892 * Returns the widget reference that defines this point's
3893 * annotation as previously specified by
3894 * <tt>setAnnotationWidget</tt>. Returns <tt>null</tt> if
3895 * the annotation has not yet been specified, or if it was
3896 * defined via <tt>setAnnotationText</tt>.
3897 *
3898 * @return reference to the widget defining this point's
3899 * annotation, or <tt>null</tt> if none.
3900 *
3901 * @see #setAnnotationWidget setAnnotationWidget
3902 * @see #setAnnotationText setAnnotationText
3903 *
3904 */
3905
3906 public Widget getAnnotationWidget() {
3907 if (null == annotation) annotation = new Annotation();
3908 return annotation.getWidget();
3909 }
3910
3911 /**
3912 ** Returns true is the point's annotation is visible, false
3913 ** otherwise
3914 **
3915 ** @return if the a annotation defined for this point will
3916 ** be visible or not after the next update.
3917 **
3918 ** @see #setAnnotationVisible setAnnotationVisible
3919 **/
3920 public boolean getAnnotationVisible() {
3921 if (null == annotation) annotation = new Annotation();
3922 return annotation.getVisible();
3923 }
3924
3925 /**
3926 ** Returns the distance, in pixels, that this annotation
3927 ** will be shifted along the x-axis from it's default
3928 ** location. <p>
3929 **
3930 ** @return amount annotation will be shifted along the x-axis,
3931 ** in pixels.
3932 **
3933 ** @see #setAnnotationXShift setAnnotationXShift
3934 **/
3935 public int getAnnotationXShift() {
3936 if (null == annotation) annotation = new Annotation();
3937 return annotation.getXShift();
3938 }
3939
3940 /**
3941 ** Returns the distance, in pixels, that this annotation
3942 ** will be shifted along the y-axis from it's default
3943 ** location. <p>
3944 **
3945 ** @return amount annotation will be shifted along the y-axis,
3946 ** in pixels.
3947 **
3948 ** @see #setAnnotationYShift setAnnotationYShift
3949 **/
3950 public int getAnnotationYShift() {
3951 if (null == annotation) annotation = new Annotation();
3952 return annotation.getYShift();
3953 }
3954
3955 /** Returns the <tt>Curve</tt> that this point was added to.
3956 **
3957 ** @return a reference to the <tt>Curve</tt> that contains
3958 ** this point (its "parent").
3959 **
3960 **/
3961 public Curve getParent() {return Curve.this;}
3962
3963 /** Returns the x-coordinate of this point in "model units"
3964 ** (arbitrary, application-specific, units).
3965 **
3966 ** @return the x-coordinate, in model units
3967 **
3968 ** @see #setX setX
3969 ** @see #setY setY
3970 ** @see #getY getY
3971 **
3972 **/
3973 public double getX() {
3974 return x;
3975 }
3976 /** Returns the y-coordinate of this point in "model units"
3977 ** (arbitrary, application-specific, units).
3978 **
3979 ** @return the y-coordinate, in model units
3980 **
3981 ** @see #getX getX
3982 ** @see #setX setX
3983 ** @see #setY setY
3984 **
3985 **/
3986 public double getY() {
3987 return y;
3988 }
3989
3990 /**
3991 ** Specifies the weight of the font that will be used
3992 ** to render the text of this point's annotation.
3993 ** <p>
3994 **
3995 ** @param cssWeight A standard CSS font-weight
3996 ** specification such as normal, bold, bolder, lighter,
3997 ** 100, 200, ... 900, or inherit
3998 **
3999 ** @see #getAnnotationFontWeight getAnnotationFontWeight
4000 ** @see #setAnnotationFontColor setAnnotationFontColor
4001 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4002 ** @see #setAnnotationFontSize setAnnotationFontSize
4003 ** @see #setAnnotationXShift setAnnotationXShift
4004 ** @see #setAnnotationYShift setAnnotationYShift
4005 ** @see #setAnnotationText setAnnotationText
4006 ** @see #setAnnotationVisible setAnnotationVisible
4007 **/
4008 public void setAnnotationFontWeight(String cssWeight) {
4009 getParent().invalidate();
4010 if (null == annotation) annotation = new Annotation();
4011 annotation.setFontWeight(cssWeight);
4012 }
4013 /**
4014 ** Specifies the color of the annotation's font.
4015 **
4016 **
4017 ** <p>
4018 ** For more information on standard CSS color
4019 ** specifications see the discussion in
4020 ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
4021 ** <p>
4022 **
4023 ** @param cssColor color of the font used to display this
4024 ** point's annotation message.
4025 **
4026 ** @see #getAnnotationFontColor getAnnotationFontColor
4027 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4028 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4029 ** @see #setAnnotationFontSize setAnnotationFontSize
4030 ** @see #setAnnotationXShift setAnnotationXShift
4031 ** @see #setAnnotationYShift setAnnotationYShift
4032 ** @see #setAnnotationText setAnnotationText
4033 ** @see #setAnnotationVisible setAnnotationVisible
4034 **/
4035 public void setAnnotationFontColor(String cssColor) {
4036 getParent().invalidate();
4037 if (null == annotation) annotation = new Annotation();
4038 annotation.setFontColor(cssColor);
4039 }
4040
4041
4042 /**
4043 ** Specifies the CSS font-style used by this point's annotation
4044 ** message.
4045 **
4046 ** @param cssStyle any valid CSS font-style, namely,
4047 ** normal, italic, oblique, or inherit.
4048 **
4049 ** @see #getAnnotationFontStyle getAnnotationFontStyle
4050 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4051 ** @see #setAnnotationFontColor setAnnotationFontColor
4052 ** @see #setAnnotationFontSize setAnnotationFontSize
4053 ** @see #setAnnotationXShift setAnnotationXShift
4054 ** @see #setAnnotationYShift setAnnotationYShift
4055 ** @see #setAnnotationText setAnnotationText
4056 ** @see #setAnnotationVisible setAnnotationVisible
4057 **/
4058 public void setAnnotationFontStyle(String cssStyle) {
4059 getParent().invalidate();
4060 if (null == annotation) annotation = new Annotation();
4061 annotation.setFontStyle(cssStyle);
4062 }
4063 /**
4064 ** Specifies the CSS font size of this point's annotation, in
4065 ** pixels.
4066 **
4067 ** @param fontSize the font size of this point's annotation, in
4068 ** pixels.
4069 **
4070 ** @see #getAnnotationFontSize getAnnotationFontSize
4071 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4072 ** @see #setAnnotationFontColor setAnnotationFontColor
4073 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4074 ** @see #setAnnotationXShift setAnnotationXShift
4075 ** @see #setAnnotationYShift setAnnotationYShift
4076 ** @see #setAnnotationText setAnnotationText
4077 ** @see #setAnnotationVisible setAnnotationVisible
4078 **/
4079 public void setAnnotationFontSize(int fontSize) {
4080 getParent().invalidate();
4081 if (null == annotation) annotation = new Annotation();
4082 annotation.setFontSize(fontSize);
4083 }
4084 /**
4085 ** Specifies the location, relative to this point's symbol,
4086 ** of this point's annotation (text label).
4087 ** <p>
4088 **
4089 ** You can further adjust the position of a point's
4090 ** annotation by specifying non-zero positional shifts via
4091 ** the <tt>setAnnotationXShift</tt> and
4092 ** <tt>setAnnotationYShift</tt> methods.
4093 **
4094 **
4095 ** @param annotationLocation the relative location of
4096 ** the annotation
4097 **
4098 ** @see #getAnnotationLocation getAnnotationLocation
4099 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4100 ** @see #setAnnotationFontColor setAnnotationFontColor
4101 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4102 ** @see #setAnnotationFontSize setAnnotationFontSize
4103 ** @see #setAnnotationText setAnnotationText
4104 ** @see #setAnnotationXShift setAnnotationXShift
4105 ** @see #setAnnotationYShift setAnnotationYShift
4106 ** @see #setAnnotationVisible setAnnotationVisible
4107 **
4108 **/
4109 public void setAnnotationLocation(AnnotationLocation
4110 annotationLocation) {
4111 getParent().invalidate();
4112 if (null == annotation) annotation = new Annotation();
4113 annotation.setLocation(annotationLocation);
4114 }
4115
4116 /**
4117 ** Specifies the text of this point's annotation
4118 ** (label).
4119 ** <p>
4120 **
4121 ** <p>By default text is plain text, though
4122 ** you can change the size, weight, style, and color of
4123 ** the text via the <tt>setAnnotationFont*</tt>
4124 ** family of methods.
4125 **
4126 ** <p>
4127 **
4128 ** <b>To use HTML, <i>your text must begin with</i>
4129 ** <tt><html></tt></b> (otherwise, GChart will treat
4130 ** it as plain text). Note that the leading
4131 ** <tt><html></tt> is stripped off by GChart before
4132 ** your HTML gets to the browser. Since it's just a flag
4133 ** for GChart, not a real HTML tag, you should <i>not</i>
4134 ** use a closing <tt></html></tt> at the end.
4135 **
4136 ** <p> <small> The idea for adding HTML support (only plain
4137 ** text was supported originally) came from <a
4138 ** href="http://groups.google.com/group/Google-Web-Toolkit/msg/cb89003dad2416fe">
4139 ** this GWT forum post by Malcolm Gorman</a>. The current
4140 ** HTML support (and, it's natural extension, Widget
4141 ** support) in tick labels and annotations, which seems so
4142 ** obvious in hindsight, might never have been added had it
4143 ** not been for this post. Thanks!</small>
4144 **
4145 ** <p>
4146 **
4147 ** <small><b>How to use the width and height upperbounds:</b>
4148 ** </small>
4149 **
4150 ** <p>
4151 **
4152 ** <blockquote><small>
4153 **
4154 **
4155 ** In most cases, you can safely ignore these two
4156 ** parameters, simply calling the {@link
4157 ** #setAnnotationText(String) 1-arg convenience method}
4158 ** and getting GChart to estimate them for you.
4159 ** <p>
4160 **
4161 ** The width and height upper-bounds define an invisible
4162 ** bounding box (a 1x1 GWT Grid, actualy) that is used to
4163 ** properly align and center your annotation.
4164 ** <p>
4165 **
4166 ** <p> Annotations can
4167 ** become misaligned if, say, due to the user zooming up
4168 ** their font size, an annotation's size exceeds these
4169 ** upperbounds. This misalignment problem can be fixed by
4170 ** specifying a larger width and/or height upperbound.
4171 ** But, larger upperbounds slow chart updates a bit.
4172 ** The defaults try to balance the performance and
4173 ** alignment tradeoff.
4174 ** <p>
4175 **
4176 ** There is one annoying but generally harmless side effect of
4177 ** using very large upperbounds: most browsers will extend their
4178 ** scroll regions to the right (and presumably below) the real
4179 ** page content so as to include the invisible bounding box. When
4180 ** this happens, it looks to the user as if there is a bunch of
4181 ** blank space on, say, the right edge of the page. In practice,
4182 ** I've always been able to prevent this problem simply by
4183 ** choosing at-least-somewhat-reasonably-tight upper bounds,
4184 ** though a little blank space may be unavoidable in some special
4185 ** cases.
4186 **
4187 ** </blockquote></small>
4188 **
4189 ** @param annotationText the text or (<tt><html></tt>
4190 ** prefixed) HTML of this point's
4191 ** annotation, or <tt>null</tt> to remove all annotation.
4192 **
4193 ** @param widthUpperBound an upper bound on the width of
4194 ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
4195 ** get GChart to estimate this width using a heuristic
4196 ** that works fine most of the time.
4197 **
4198 ** @param heightUpperBound an upper bound on the height of
4199 ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
4200 ** get GChart to estimate this height using a heuristic
4201 ** that works fine most of the time.
4202 **
4203 ** @see #getAnnotationText getAnnotationText
4204 ** @see #setAnnotationText(String) setAnnotationText(String)
4205 ** @see #setAnnotationLocation setAnnotationLocation
4206 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4207 ** @see #setAnnotationFontColor setAnnotationFontColor
4208 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4209 ** @see #setAnnotationFontSize setAnnotationFontSize
4210 ** @see #setAnnotationWidget setAnnotationWidget
4211 ** @see #setAnnotationXShift setAnnotationXShift
4212 ** @see #setAnnotationYShift setAnnotationYShift
4213 ** @see #setAnnotationVisible setAnnotationVisible
4214 ** @see GChart.Axis#addTick(double,String,int,int) addTick
4215 **
4216 **/
4217 public void setAnnotationText(String annotationText,
4218 int widthUpperBound,
4219 int heightUpperBound) {
4220 getParent().invalidate();
4221 if (null == annotation) annotation = new Annotation();
4222 annotation.setText(annotationText,
4223 widthUpperBound,
4224 heightUpperBound);
4225 }
4226 /**
4227 * Sets the text of an annotation.
4228 * <p>
4229 * This is a convenience method equivalent to
4230 * <tt>setAnnotationText(annotationText, GChart.NAI, GChart.NAI)</tt>. See
4231 * that method for further details.
4232 * <p>
4233 *
4234 ** @param annotationText the text or
4235 ** (<tt><html></tt>-prefixed) HTML of this point's
4236 ** annotation, or <tt>null</tt> to remove all annotation.
4237 *
4238 * @see #setAnnotationText(String, int, int)
4239 * setAnnotationText(String,int,int)
4240 *
4241 */
4242 public void setAnnotationText(String annotationText) {
4243 setAnnotationText(annotationText, GChart.NAI, GChart.NAI);
4244 }
4245
4246 /**
4247 ** Specifies a widget defining this point's annotation
4248 ** <p>
4249 ** This method is similar to <tt>setAnnotationText</tt>
4250 ** except that it uses a widget, rather than a string
4251 ** to define this point's annotation.
4252 ** Although the string based method is faster
4253 ** on first chart rendering, and uses less memory, the
4254 ** widget-based method allows you to change the annotation
4255 ** independently of the chart--potentially bypassing (or
4256 ** at least speeding up) expensive chart updates later on.
4257 ** <p>
4258 **
4259 ** You might use a widget-based annotation to pop-up a
4260 ** message whenever the user clicks on a button underneath
4261 ** a particular data point on the chart, to include a small
4262 ** GWT <tt>Grid</tt> as a table embedded in the upper left
4263 ** hand corner of the chart, to trigger mouse-over events
4264 ** when the user hovers over a transparent image-based
4265 ** annotation centered on a particular point, etc.
4266 ** <p>
4267 **
4268 ** <i>Tip:</i>If you need to instrument a chart using
4269 ** widgets precisely positioned on the chart, but not
4270 ** associated with any visible curve, add a curve just to
4271 ** hold these annotations, with one point per annotation,
4272 ** and set that curve's symbol type
4273 ** to <tt>SymbolType.NONE</tt>.
4274 ** <p>
4275 **
4276 ** <b><i>Warning:</i></b> If you use the exact same widget
4277 ** reference to define two different annotations, GChart
4278 ** will render only one of them, and <i>there is no easy
4279 ** rule</i> that lets you reliably determine which one. So,
4280 ** don't do that. Instead, if you want to use the same
4281 ** widget for two different annotations, use two identical
4282 ** but distinct copies of that widget. Similarly, if you
4283 ** want to move a single widget annotation from one point
4284 ** to another, be sure to "<tt>null</tt> out" the first
4285 ** point's annotation (e.g. via
4286 ** <tt>setAnnotationWidget(null)</tt>) or the widget may
4287 ** not be rendered where you expect. You should also
4288 ** <tt>null</tt> out the annotation widget reference before
4289 ** moving the annotation widget to a position in the DOM
4290 ** completely outside of the GChart. A little extra
4291 ** bookkeeping on your part makes it possible to
4292 ** significantly simplify and streamline GChart's rendering
4293 ** algorithms.
4294 **
4295 * @param annotationWidget the GWT Widget that defines this
4296 * point's annotation.
4297 *
4298 * @param widthUpperBound an upper bound on the width of
4299 * the Widget, in pixels. If this and the next
4300 * parameter are omitted, GChart will use
4301 * <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
4302 *
4303 * @param heightUpperBound an upper bound on the height of
4304 * the Widget, in pixels. If this and the previous
4305 * parameter are omitted, GChart will use <tt>
4306 * DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
4307 *
4308 *
4309 * @see #getAnnotationWidget getAnnotationWidget
4310 * @see #setAnnotationText(String, int, int)
4311 * setAnnotationText(String,int,int)
4312 * @see #setAnnotationWidget(Widget)
4313 * setAnnotationWidget(Widget)
4314 * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
4315 * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
4316 * @see SymbolType#NONE SymbolType.NONE
4317 *
4318 **/
4319 public void setAnnotationWidget(Widget annotationWidget,
4320 int widthUpperBound,
4321 int heightUpperBound) {
4322 getParent().invalidate();
4323 if (null == annotation) annotation = new Annotation();
4324 // accept "Not an Integer" (because setAnnotationText does)
4325 if (widthUpperBound == GChart.NAI)
4326 widthUpperBound = DEFAULT_WIDGET_WIDTH_UPPERBOUND;
4327 if (heightUpperBound == GChart.NAI)
4328 heightUpperBound = DEFAULT_WIDGET_HEIGHT_UPPERBOUND;
4329 annotation.setWidget(annotationWidget,
4330 widthUpperBound,
4331 heightUpperBound);
4332 }
4333
4334 /**
4335 * Specifies a widget defining this point's annotation.
4336 * <p>
4337 * A convenience method equivalent to
4338 * <tt>setAnnotationWidget(annotationWidget,
4339 * DEFAULT_WIDGET_WIDTH_UPPERBOUND,
4340 * DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>
4341 *
4342 * @param annotationWidget the GWT Widget that defines this
4343 * point's annotation.
4344 *
4345 * @see #setAnnotationWidget(Widget,int,int)
4346 * setAnnotationWidget(Widget,int,int)
4347 * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
4348 * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
4349 *
4350 */
4351 public void setAnnotationWidget(Widget annotationWidget) {
4352 setAnnotationWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
4353 DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
4354 }
4355
4356
4357 /**
4358 ** Specifies if this point's annotation
4359 ** (label) is visible or not.
4360 ** <p>
4361 **
4362 ** @param isVisible use true to make the annotation
4363 ** visible, or false to hide it.
4364 **
4365 ** @see #getAnnotationVisible getAnnotationVisible
4366 ** @see #setAnnotationLocation setAnnotationLocation
4367 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4368 ** @see #setAnnotationFontColor setAnnotationFontColor
4369 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4370 ** @see #setAnnotationFontSize setAnnotationFontSize
4371 ** @see #setAnnotationXShift setAnnotationXShift
4372 ** @see #setAnnotationYShift setAnnotationYShift
4373 ** @see #setAnnotationText setAnnotationText
4374 **/
4375 public void setAnnotationVisible(boolean isVisible) {
4376 getParent().invalidate();
4377 if (null == annotation) annotation = new Annotation();
4378 annotation.setVisible(isVisible);
4379 }
4380
4381
4382
4383 /**
4384 ** Specifies the number of pixels (along the x-axis) to
4385 ** move this point's annotation from its default,
4386 ** <tt>AnnotationLocation</tt>-defined, position. Negative
4387 ** values move the annotation in the negative x direction.
4388 **
4389 ** <p> For example, with the default <tt>xShift</tt> of 0,
4390 ** annotations with an <tt>AnnotationLocation</tt> of
4391 ** <tt>EAST</tt> will have their left edges flush against
4392 ** the right edge of, say, a box symbol representing the
4393 ** annotated point. You could use an <tt>xShift</tt>
4394 ** setting of 10 to move the annotation 10 pixels to the
4395 ** right and thus introduce some space between the
4396 ** annotation and the box.
4397 ** <p>
4398 **
4399 ** <i>Special convention for pie slices:</i>
4400 ** Points on curves whose symbols represent pie
4401 ** slices always have the positive x-axis associated with
4402 ** the shifts specified by this method aligned with the
4403 ** outward-pointing pie radius that bisects the pie slice. This
4404 ** convention makes it easy to move pie slice annotations
4405 ** radially outward (via <tt>xShift > 0</tt>) or
4406 ** radially inward (via <tt>xShift < 0</tt>). For those
4407 ** rare situations where you may need to move a pie
4408 ** annotation perpendicularly to this radius, use
4409 ** <tt>setAnnotationYShift</tt>.
4410 **
4411 ** @param xShift number of pixels to move annotation
4412 ** along the x axis from
4413 ** it's default, <tt>AnnotationLocation</tt>-defined,
4414 ** location.
4415 **
4416 ** @see #setAnnotationYShift setAnnotationYShift
4417 ** @see #setAnnotationLocation setAnnotationLocation
4418 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4419 ** @see #setAnnotationFontColor setAnnotationFontColor
4420 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4421 ** @see #setAnnotationFontSize setAnnotationFontSize
4422 ** @see #setAnnotationText setAnnotationText
4423 ** @see #setAnnotationVisible setAnnotationVisible
4424 ** @see #getAnnotationXShift getAnnotationXShift
4425 **/
4426 public void setAnnotationXShift(int xShift) {
4427 getParent().invalidate();
4428 if (null == annotation) annotation = new Annotation();
4429 annotation.setXShift(xShift);
4430 }
4431 /**
4432 ** Specifies the number of pixels (along the y-axis) to
4433 ** move this point's annotation from its default,
4434 ** <tt>AnnotationLocation</tt>-defined, position. Negative
4435 ** values move the annotation in the negative y direction.
4436 **
4437 ** <p> For example, with the default <tt>yShift</tt> of 0,
4438 ** annotations with an <tt>AnnotationLocation</tt> of
4439 ** <tt>SOUTH</tt> will have their top edges flush against
4440 ** the bottom edge of, say, a box symbol representing the
4441 ** annotated point. You could use a <tt>yShift</tt>
4442 ** setting of -10 to move the annotation down 10 pixels and
4443 ** thus introduce some spacing between the annotation and
4444 ** the box.
4445 ** <p>
4446 **
4447 ** <i>Special convention for pie slices:</i> The positive
4448 ** y-axis for pie slices always points one 90 degree
4449 ** counter-clockwise rotation from the direction of the
4450 ** outward-pointing pie radius that bisects the pie slice.
4451 ** This convention means that <tt>yShift</tt> moves pie
4452 ** slice annotations along a line <i>perpendicular to</i>
4453 ** this bisecting pie radius. Use the companion method
4454 ** <tt>setAnnotationXShift</tt> for the more common
4455 ** operation of moving the annotation along this bisecting
4456 ** radius.
4457 **
4458 ** @param yShift number of pixels to move annotation along
4459 ** the y-axis from it's default,
4460 ** <tt>AnnotationLocation</tt>-defined, location.
4461 **
4462 ** @see #setAnnotationXShift setAnnotationXShift
4463 ** @see #setAnnotationLocation setAnnotationLocation
4464 ** @see #setAnnotationFontWeight setAnnotationFontWeight
4465 ** @see #setAnnotationFontColor setAnnotationFontColor
4466 ** @see #setAnnotationFontStyle setAnnotationFontStyle
4467 ** @see #setAnnotationFontSize setAnnotationFontSize
4468 ** @see #setAnnotationText setAnnotationText
4469 ** @see #setAnnotationVisible setAnnotationVisible
4470 ** @see #getAnnotationXShift getAnnotationXShift
4471 **/
4472 public void setAnnotationYShift(int yShift) {
4473 getParent().invalidate();
4474 if (null == annotation) annotation = new Annotation();
4475 annotation.setYShift(yShift);
4476 }
4477
4478 /**
4479 * Defines the x-coordinate of this point in "model units"
4480 * (arbitrary, application-specific, units mapped to the
4481 * horizontal dimension of the plot area).
4482 * <p>
4483 *
4484 * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
4485 * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
4486 * <tt>addPoint</tt> method for details.
4487 *
4488 *
4489 * @param x the x-coordinate of the point in model units.
4490 *
4491 ** @see #getX getX
4492 ** @see #setY setY
4493 ** @see #getY getY
4494 ** @see #addPoint addPoint
4495 */
4496 public void setX(double x) {
4497 getParent().invalidate();
4498 this.x = x;
4499 }
4500
4501 /**
4502 * Defines the y-coordinate of this point in "model units"
4503 * (arbitrary, application-specific, units mapped to the
4504 * vertical dimension of the plot area).
4505 * <p>
4506 *
4507 * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
4508 * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
4509 * <tt>addPoint</tt> method for details.
4510 *
4511 * @param y the y-coordinate of the point in model units.
4512 *
4513 ** @see #getX getX
4514 ** @see #setX setX
4515 ** @see #getY getY
4516 ** @see #addPoint addPoint
4517 **
4518 */
4519 public void setY(double y) {
4520 getParent().invalidate();
4521 this.y = y;
4522 }
4523
4524 Annotation getAnnotation() {
4525 if (annotation == null) annotation = new Annotation();
4526 return annotation;
4527 }
4528 /**
4529 * Retrieves the expanded hovertext associated with this
4530 * point.
4531 * <p>
4532 *
4533 * The expanded hovertext is obtained by replacing any
4534 * embedded parameters in the hovertext template with
4535 * their values as evaluated at this point. For example,
4536 * references to <tt>${x}</tt> and <tt>${y}</tt> in
4537 * the hovertext template are replaced with
4538 * appropriately formatted representations of this
4539 * point's x and y properties.
4540 * <p>
4541 *
4542 * By default, GChart will display this expanded hovertext
4543 * whenever the user "touches" a point with the
4544 * curve-specific, rectangular, mouse-centered, brush.
4545 * <p>
4546 *
4547 * <i>Tip:</i> To define your own custom parameter names
4548 * that can be embedded within hovertext templates and
4549 * will be interpreted/expanded relative to the touched point, use
4550 * the <tt>setHoverParameterInterpreter</tt> method.
4551 *
4552 * @return the expanded hover text associated with this point.
4553 *
4554 * @see Symbol#setHovertextTemplate setHovertextTemplate
4555 * @see #setHoverParameterInterpreter setHoverParameterInterpreter
4556 *
4557 */
4558 public String getHovertext() {
4559 String result = HovertextChunk.getHovertext(
4560 getParent().getSymbol().getHovertextChunks(), this);
4561 return result;
4562 }
4563
4564 } // end of class GChart.Curve.Point
4565
4566 /*
4567 * Declares that this curve's rendering panel (its DOM representation)
4568 * is inconsistent with current curve specifications.
4569 *
4570 * <p>
4571 *
4572 * Sets the flag <tt>update</tt> uses to determine if a curve needs
4573 * to be re-rendered.
4574 *
4575 */
4576 void invalidate() {
4577 // The guard isn't just for speed; it keeps us out of trouble
4578 // when the system curves are being added/configured initially
4579 if (isValidated) {
4580 isValidated = false;
4581 // for efficiency, all background curves use a single rendering
4582 // panel, so invalidating one background curve invalidates them all
4583 if (indexOf < N_PRE_SYSTEM_CURVES) {
4584 for (int i = 0; i < N_PRE_SYSTEM_CURVES; i++)
4585 curves.get(i).isValidated = false;
4586 }
4587 }
4588 }
4589
4590 /*
4591 * Smallest rectangle containing curve's graphics (ignoring
4592 * annotations)
4593 * <p>
4594 *
4595 * Each curve has it's own canvas to allow for fast, single
4596 * curve, updates (and we hope this rendering independence will
4597 * facilitate additional features in future releases). So, we
4598 * have to economize on canvas size. Moreover, because we
4599 * allow rendering outside of the decorated chart region, we
4600 * can't just set the size of the canvas to the size of the
4601 * plot area or decorated chart (even if we could afford to do
4602 * that, memory-wise).
4603 *
4604 */
4605 Rectangle getContainingRectangle(PlotPanel pp) {
4606 final Rectangle result = new Rectangle();
4607 if (getNPoints() == 0) {
4608 result.x = result.y = result.width = result.height = 0;
4609 return result;
4610 }
4611
4612 double minX = Double.MAX_VALUE;
4613 double maxX = -Double.MAX_VALUE;
4614 double minY = Double.MAX_VALUE;
4615 double maxY = -Double.MAX_VALUE;
4616 boolean pointAtXAxisMin = false; // do keyword positioned points
4617 boolean pointAtXAxisMax = false; // exist on this curve?
4618 boolean pointAtYAxisMin = false;
4619 boolean pointAtYAxisMax = false;
4620 boolean isClippedToDecoratedChart = getClipToDecoratedChart();
4621 boolean isClippedToPlotArea = getActuallyClippedToPlotArea();
4622 // Find min, max for x,y and record each keyword position used
4623 int nPoints = getNPoints();
4624 for (int i = 0; i < nPoints; i++) {
4625 Point p = getPoint(i);
4626 double x = p.getX();
4627 double y = p.getY();
4628 if (Double.MAX_VALUE == x)
4629 pointAtXAxisMax = true;
4630 else if (-Double.MAX_VALUE == x)
4631 pointAtXAxisMin = true;
4632 else {
4633 if (x < minX) minX = x;
4634 if (x > maxX) maxX = x;
4635 }
4636 if (Double.MAX_VALUE == y)
4637 pointAtYAxisMax = true;
4638 else if (-Double.MAX_VALUE == y)
4639 pointAtYAxisMin = true;
4640 else {
4641 if (y < minY) minY = y;
4642 if (y > maxY) maxY = y;
4643 }
4644 }
4645
4646 // apply "at min/max" keyword, clipping imposed limits
4647 if (pointAtXAxisMin)
4648 minX = Math.min(minX, pp.getXMin());
4649 if (isClippedToPlotArea)
4650 minX = Math.max(minX, pp.getXMin());
4651 else if (isClippedToDecoratedChart)
4652 minX = Math.max(minX, getXAxis().pixelToModel(0));
4653
4654 if (pointAtXAxisMax)
4655 maxX = Math.max(maxX, pp.getXMax());
4656 if (isClippedToPlotArea)
4657 maxX = Math.min(maxX, pp.getXMax());
4658 else if (isClippedToDecoratedChart)
4659 maxX = Math.min(maxX, getXAxis().pixelToModel(
4660 pp.getXChartSizeDecoratedQuickly()));
4661
4662 boolean onY2 = onY2();
4663 if (onY2) {
4664 if (pointAtYAxisMin)
4665 minY = Math.min(minY, pp.getY2Min());
4666 if (isClippedToPlotArea)
4667 minY = Math.max(minY, pp.getY2Min());
4668 else if (isClippedToDecoratedChart)
4669 minY = Math.max(minY, getY2Axis().pixelToModel(
4670 pp.getYChartSizeDecoratedQuickly()));
4671 if (pointAtYAxisMax)
4672 maxY = Math.max(maxY, pp.getY2Max());
4673 if (isClippedToPlotArea)
4674 maxY = Math.min(maxY, pp.getY2Max());
4675 else if (isClippedToDecoratedChart)
4676 maxY = Math.min(maxY, getY2Axis().pixelToModel(0));
4677 }
4678 else {
4679 if (pointAtYAxisMin)
4680 minY = Math.min(minY, pp.getYMin());
4681 if (isClippedToPlotArea)
4682 minY = Math.max(minY, pp.getYMin());
4683 else if (isClippedToDecoratedChart)
4684 minY = Math.max(minY, GChart.this.getYAxis().pixelToModel(
4685 pp.getYChartSizeDecoratedQuickly()));
4686 if (pointAtYAxisMax)
4687 maxY = Math.max(maxY, pp.getYMax());
4688 if (isClippedToPlotArea)
4689 maxY = Math.min(maxY, pp.getYMax());
4690 else if (isClippedToDecoratedChart)
4691 maxY = Math.min(maxY, GChart.this.getYAxis().pixelToModel(0));
4692 }
4693
4694 // finally, we need to convert to pixels while taking into account
4695 // the size of the rendered symbol itself (e.g. pies can stick
4696 // out from their x,y specified center point, etc.)
4697 Symbol sym = getSymbol();
4698 SymbolType symType = sym.getSymbolType();
4699 // in obscure cases, canvas could clip without this extra wiggle room
4700 int extraSpace = sym.getFillThickness();
4701 extraSpace += Math.abs(sym.getBorderWidth());
4702 double left0 = symType.getEdgeLeft(pp, sym, minX, onY2);
4703 double left1 = symType.getEdgeLeft(pp, sym, maxX, onY2);
4704 double right0 = symType.getEdgeRight(pp, sym, minX, onY2);
4705 double right1 = symType.getEdgeRight(pp, sym, maxX, onY2);
4706 double bottom0 = symType.getEdgeBottom(pp, sym, minY, onY2);
4707 double bottom1 = symType.getEdgeBottom(pp, sym, maxY, onY2);
4708 double top0 = symType.getEdgeTop(pp, sym, minY, onY2);
4709 double top1 = symType.getEdgeTop(pp, sym, maxY, onY2);
4710
4711 // baseline bars can flip order, so smallest x could be 'right', etc.
4712 double xPxMin = Math.min(Math.min(left0, left1),
4713 Math.min(right0, right1));
4714 double xPxMax = Math.max(Math.max(left0, left1),
4715 Math.max(right0, right1));
4716 double yPxMin = Math.min(Math.min(bottom0, bottom1),
4717 Math.min(top0, top1));
4718 double yPxMax = Math.max(Math.max(bottom0, bottom1),
4719 Math.max(top0, top1));
4720 result.x = xPxMin - extraSpace;
4721 result.y = yPxMin - extraSpace;
4722 result.width = xPxMax - xPxMin + 1 + 2*extraSpace;
4723 result.height = yPxMax - yPxMin + 1 + 2*extraSpace;
4724
4725 // result is (roughly) smallest rectangle that contains every
4726 // rendered symbol on this curve (ignoring annotations)
4727
4728 return result;
4729
4730 }
4731
4732 // keeps track of if last rendering was canvas-based or not
4733 private boolean wasCanvasRendered = false;
4734 void setWasCanvasRendered(boolean wasCanvasRendered) {
4735 this.wasCanvasRendered = wasCanvasRendered;
4736 }
4737 // is curve currently canvas rendered and up-to-date
4738 boolean isCanvasRendered() {
4739 return isValidated && wasCanvasRendered;
4740 }
4741
4742 } // end of class GChart.Curve
4743
4744
4745 // Allows hovertext templates to be parsed into "chunks"
4746 // so that they can be expanded into hovertext faster.
4747 static class HovertextChunk {
4748 final static int HOVERTEXT_PARAM_NONE = 0; // plain old text
4749 final static int HOVERTEXT_PARAM_X = 1; // ${x}
4750 final static int HOVERTEXT_PARAM_Y = 2; // ${y}
4751 final static int HOVERTEXT_PARAM_PIESLICESIZE = 3; // ${pieSlicePercent}
4752 final static int HOVERTEXT_PARAM_USERDEFINED = 4; // ${mySpecialParameter}
4753 int paramId; // id of substitution parameter
4754 String paramName; // name of substitution parameter
4755 String chunkText; // plain text that follows this parameter
4756 HovertextChunk(int id, String name, String text) {
4757 paramId = id;
4758 paramName = name;
4759 chunkText = text;
4760 }
4761 // returns array of "chunks" corresponding to the given
4762 // hovertext template
4763 static HovertextChunk[] parseHovertextTemplate(
4764 String htTemplate) {
4765 if (htTemplate.equals("")) return new HovertextChunk[0];
4766 // takes "x=${x}; y=${y}" into {"x=", "x}; y=", "y}"}
4767 // Thus, except for the first, chunks contain a
4768 // keyword like part, followed by a string literal.
4769 String[] sChunk = htTemplate.split("\\$\\{");
4770 HovertextChunk[] result = new HovertextChunk[sChunk.length];
4771
4772 for (int i = 0; i < sChunk.length; i++) {
4773 String sC = sChunk[i];
4774 if (0 == i)
4775 // leading (non-parametric) plain text chunk
4776 result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
4777 null, sC);
4778 else if (sC.startsWith("x}"))
4779 result[i] = new HovertextChunk(
4780 HOVERTEXT_PARAM_X, "x",
4781 sC.substring("x}".length()));
4782 else if (sC.startsWith("y}"))
4783 result[i] = new HovertextChunk(
4784 HOVERTEXT_PARAM_Y, "y",
4785 sC.substring("y}".length()));
4786 else if (sC.startsWith("pieSliceSize}"))
4787 result[i] = new HovertextChunk(
4788 HOVERTEXT_PARAM_PIESLICESIZE,
4789 "pieSliceSize",
4790 sC.substring("pieSliceSize}".length()));
4791 else if (sC.matches("[a-zA-Z][a-zA-Z0-9_]*\\}.*")) {
4792 // fits pattern for a user defined parameter
4793 int closeCurlyIndex = sC.indexOf("}");
4794 result[i] = new HovertextChunk(
4795 HOVERTEXT_PARAM_USERDEFINED,
4796 sC.substring(0, closeCurlyIndex),
4797 sC.substring(closeCurlyIndex+1));
4798 }
4799 else {
4800 // leading "${" without "paramName}". Likely a
4801 // typo, but output verbatim to give them a clue:
4802 result[i] = new HovertextChunk(HOVERTEXT_PARAM_NONE,
4803 null, "${" + sC);
4804 }
4805
4806 }
4807 return result;
4808 }
4809
4810 /* hovertext associated with parsed "chunks" for a given point */
4811 static String getHovertext(HovertextChunk[] htc,
4812 Curve.Point p) {
4813 String result = "";
4814 String xS = null;
4815 String yS = null;
4816 String pieSlicePercentS = null;
4817 HoverParameterInterpreter hpi =
4818 p.getParent().getParent().getHoverParameterInterpreter();
4819 for (int i = 0; i < htc.length; i++) {
4820 switch (htc[i].paramId) {
4821 case HovertextChunk.HOVERTEXT_PARAM_NONE:
4822 break;
4823 case HovertextChunk.HOVERTEXT_PARAM_X:
4824 if (null == xS) {
4825 String hoverParam = (null == hpi) ? null :
4826 hpi.getHoverParameter(htc[i].paramName, p);
4827 if (null != hoverParam)
4828 xS = hoverParam;
4829 else {
4830 Axis axis =
4831 p.getParent().getParent().getXAxis();
4832 xS = axis.formatAsTickLabel(p.getX());
4833 }
4834 }
4835 result += xS;
4836 break;
4837 case HovertextChunk.HOVERTEXT_PARAM_Y:
4838 if (null == yS) {
4839 String hoverParam = (null == hpi) ? null :
4840 hpi.getHoverParameter(htc[i].paramName, p);
4841 if (null != hoverParam)
4842 yS = hoverParam;
4843 else {
4844 Axis axis = p.getParent().onY2() ?
4845 p.getParent().getParent().getY2Axis() :
4846 p.getParent().getParent().getYAxis();
4847 yS = axis.formatAsTickLabel(p.getY());
4848 }
4849 }
4850 result+=yS;
4851 break;
4852
4853 case HovertextChunk.HOVERTEXT_PARAM_PIESLICESIZE:
4854 if (null == pieSlicePercentS) {
4855 String hoverParam = (null == hpi) ? null :
4856 hpi.getHoverParameter(htc[i].paramName, p);
4857 if (null != hoverParam)
4858 pieSlicePercentS = hoverParam;
4859 else {
4860 double pieSliceSize =
4861 p.getParent().getSymbol().getPieSliceSize();
4862 Axis axis = p.getParent().onY2() ?
4863 p.getParent().getParent().getY2Axis() :
4864 p.getParent().getParent().getYAxis();
4865 pieSlicePercentS =
4866 axis.formatAsTickLabel(100*pieSliceSize) + "%";
4867 }
4868 }
4869 result+=pieSlicePercentS;
4870 break;
4871
4872 case HovertextChunk.HOVERTEXT_PARAM_USERDEFINED:
4873
4874 String hoverParam = (null == hpi) ? null :
4875 hpi.getHoverParameter(htc[i].paramName, p);
4876 if (null == hoverParam)
4877 // null means "unrecognized parameter" - so
4878 // regenerate the original, unparsed, param spec
4879 // to clue them in that it was not processed.
4880 result += "${" + htc[i].paramName + "}";
4881 else
4882 result += hoverParam;
4883
4884 break;
4885 default:
4886 throw new IllegalStateException(
4887 "An illegal HOVERTEXT_PARAM_* id: " + htc[i].paramId +
4888 " was encountered. A GChart bug is likely to blame.");
4889 }
4890 result+=htc[i].chunkText;
4891 }
4892 return result;
4893 }
4894 }
4895
4896 /**
4897 ** Defines a chart curve symbol. Each point on a curve
4898 ** is represented on the chart by an appropriate
4899 ** rendering of the curve's symbol.
4900 **
4901 ** @see Curve#getSymbol Curve.getSymbol
4902 ** @see SymbolType SymbolType
4903 **
4904 **/
4905
4906 public class Symbol {
4907
4908 private Annotation annotation = null;
4909 private String backgroundColor = DEFAULT_SYMBOL_BACKGROUND_COLOR;
4910 // same as backgroundColor, but with extended RGBA collapsed to plain RGA
4911 private String backgroundColorCSS = DEFAULT_SYMBOL_BACKGROUND_COLOR;
4912 private double baseline = Double.NaN;
4913 private String borderColor = "black";
4914 private String borderColorCSS = "black";
4915 private String borderStyle = DEFAULT_SYMBOL_BORDER_STYLE;
4916 private int borderWidth = DEFAULT_SYMBOL_BORDER_WIDTH;
4917 private int brushHeight = DEFAULT_BRUSH_HEIGHT;
4918 private AnnotationLocation brushLocation = AnnotationLocation.CENTER;
4919 private int brushWidth = DEFAULT_BRUSH_WIDTH;
4920 private boolean fillHasHovertext = true;
4921 private double fillSpacing = Double.NaN;
4922 private int fillThickness = GChart.NAI;
4923 private int height = DEFAULT_SYMBOL_HEIGHT;
4924 private String hovertextTemplate=null;
4925 // holds specification for the hover annotation. Actual
4926 // hover annotation is generated on the fly when they hover
4927 private Annotation hoverAnnotation = null;
4928 private boolean hoverAnnotationEnabled = true;
4929 // allows hover annotation to use a different symbol type
4930 // than the symbol being hovered over. Main use expected to
4931 // be to place hover feedback at a fixed location on the chart (via
4932 // ANCHOR_* family of symbol types), for example, a status
4933 // bar message that changes depending on what the mouse
4934 // is touching.
4935 private SymbolType hoverAnnotationSymbolType = null;
4936 // encloses each symbol in a 1 px gray selection rectangle:
4937 private String hoverSelectionBackgroundColor = "transparent";
4938 private String hoverSelectionBorderColor = "gray";
4939 private String hoverSelectionBorderStyle = "solid";
4940 private int hoverSelectionBorderWidth = -1;
4941 private boolean hoverSelectionEnabled = true;
4942 private double hoverSelectionFillSpacing = Double.NaN;
4943 private int hoverSelectionFillThickness = GChart.NAI;
4944 private int hoverSelectionHeight = GChart.NAI;
4945 private String hoverSelectionImageURL = null;
4946 private int hoverSelectionWidth = GChart.NAI;
4947 private SymbolType hoverSelectionSymbolType = null;
4948
4949 private HovertextChunk[] hovertextChunks = null;
4950 private String imageURL = null;
4951 // XXX: Symbols are used independently of Curves by the
4952 // realizeTick method. But it's probably better to render ticks
4953 // via specialized system curves. If/when that's implemented,
4954 // Symbol should become an inner class of Curve, and this
4955 // explicit parent pointer will no longer be required.
4956 private Curve parent = null;
4957 // when specified, model width/height are in user-defined units.
4958 private double modelHeight = Double.NaN;
4959 private double modelWidth = Double.NaN;
4960 // NaN means "begin this slice where last slice left off, or at
4961 // initialPieSliceOrientation if it is the first slice to be rendered"
4962 private double pieSliceOrientation = Double.NaN;
4963 private double defaultPieSliceOrientation = 0.0;
4964 // slices, by default, fill the entire pie (useful for drawing disks)
4965 private double pieSliceSize = 1;
4966
4967 private SymbolType symbolType = DEFAULT_SYMBOL_TYPE;
4968
4969 private int width = DEFAULT_SYMBOL_WIDTH;
4970 double xScaleFactor = 1.0;
4971 double yScaleFactor = 1.0;
4972
4973 Symbol(Curve parent) {super(); this.parent = parent;}
4974
4975
4976
4977 /** Returns the CSS background color of all the rectangular
4978 ** elements used in rendering the symbol.
4979 **
4980 ** @return the CSS background color used to fill in the
4981 ** central (non-border) part of each rectangular element
4982 ** used to render a curve's symbol.
4983 **
4984 ** @see #setBackgroundColor(String) setBackgroundColor
4985 **/
4986 public String getBackgroundColor() {
4987 return backgroundColor;
4988 }
4989 String getBackgroundColorCSS() {
4990 return backgroundColorCSS;
4991 }
4992 /** Returns the baseline value for this symbol,
4993 ** previously specified via <tt>setBaseline</tt>
4994 **
4995 **
4996 ** @return the previously specified baseline value for
4997 ** this symbol.
4998 **
4999 ** @see #setBaseline setBaseline
5000 **/
5001 public double getBaseline() {
5002 return baseline;
5003 }
5004 /** Returns the CSS border color of all the rectangular
5005 ** elements used in rendering the symbol.
5006 **
5007 ** <p>
5008 ** @return the color of the border of the rectangular elements
5009 ** used to render the symbol, in standard CSS format
5010 **
5011 ** @see #setBorderColor setBorderColor
5012 **
5013 **/
5014 public String getBorderColor() {
5015 return borderColor;
5016 }
5017 String getBorderColorCSS() {
5018 return borderColorCSS;
5019 }
5020 /**
5021 ** Returns the border style of all of the rectangular
5022 ** elements from which this symbol is built.
5023 ** <p>
5024 ** @return the CSS borderStyle of this symbol's elements
5025 ** (dotted, dashed, solid, etc. )
5026 **
5027 ** @see #setBorderStyle setBorderStyle
5028 **/
5029 public String getBorderStyle() {
5030 return borderStyle;
5031 }
5032
5033 /**
5034 ** Returns the width of the border around each
5035 ** rectangular element used to render this symbol,
5036 ** in pixels.
5037 **
5038 ** <p>
5039 ** @return the previously set border width (in pixels).
5040 **
5041 ** @see #setBorderWidth setBorderWidth
5042 */
5043 public int getBorderWidth() {
5044 return borderWidth;
5045 }
5046 /**
5047 *
5048 * Returns the height of the rectangular "brush" that defines
5049 * how close the mouse cursor must be to a rendered symbol for
5050 * the symbol to be considered to have been "touched" (which
5051 * causes the point's hover feedback to pop up).
5052 *
5053 * @return the height of the "brush", in pixels, associated
5054 * with this symbol/curve.
5055 *
5056 * @see #setBrushHeight setBrushHeight
5057 *
5058 */
5059 public int getBrushHeight() {
5060 return brushHeight;
5061 }
5062
5063 /**
5064 *
5065 * Returns the location of the rectangular brush relative to
5066 * the current x,y coordinates of the mouse cursor.
5067 * <p>
5068 *
5069 * @return the location of the rectangular brush relative to
5070 * the x,y coordinates of the mouse cursor.
5071 *
5072 * @see #setBrushLocation setBrushLocation
5073 */
5074 public AnnotationLocation getBrushLocation() {
5075 return brushLocation;
5076 }
5077
5078
5079 /**
5080 *
5081 * Returns the width of the rectangular "brush" that defines
5082 * how close the mouse cursor must be to a rendered symbol for
5083 * the symbol to be considered to have been "touched" (which
5084 * causes the point's hover feedback to pop up). <p>
5085 *
5086 * @return the width of the "brush", in pixels, associated
5087 * with this symbol/curve.
5088 *
5089 * @see #setBrushWidth setBrushWidth
5090 *
5091 */
5092 public int getBrushWidth() {
5093 return brushWidth;
5094 }
5095
5096 /**
5097 ** @deprecated
5098 **
5099 ** Returns the value previously set by setFillHasHovertext.
5100 **
5101 ** @see #setFillHasHovertext setFillHasHovertext
5102 **
5103 **/
5104 public boolean getFillHasHovertext() {
5105 return fillHasHovertext;
5106 }
5107
5108
5109 /**
5110 ** Returns the spacing between successive rectangular
5111 ** elements used to emulate any required non-rectangular
5112 ** features of the symbol. <p>
5113 **
5114 **
5115 **
5116 ** @return the previously set (or the default, if the
5117 ** fillSpacing has been set to <tt>Double.NaN</tt>) fill spacing
5118 ** (in pixels).
5119 **
5120 ** @see #setFillSpacing setFillSpacing
5121 ** @see #setFillThickness setFillThickness
5122 **
5123 */
5124 public double getFillSpacing() {
5125 if ((fillSpacing!=fillSpacing)) // x!=x is a faster isNaN
5126 return symbolType.defaultFillSpacing();
5127 else
5128 return fillSpacing;
5129 }
5130
5131 /**
5132 ** Returns the "thickness" of rectangular elements used to
5133 ** emulate any required non-rectangular features of the symbol.
5134 ** <p>
5135 **
5136 **
5137 ** @return the previously set (or the default, if the
5138 ** fillThickness has been set to <tt>GChart.NAI</tt>) fill
5139 ** thickness (in pixels).
5140 **
5141 ** @see #setFillThickness setFillThickness
5142 ** @see #setFillSpacing setFillSpacing
5143 */
5144 public int getFillThickness() {
5145 if (fillThickness==GChart.NAI)
5146 return symbolType.defaultFillThickness();
5147 else
5148 return fillThickness;
5149 }
5150
5151 /* Retrieves the annotation that defines the properties of
5152 * the internally generated annotations used to display
5153 * hover feedback. */
5154 Annotation getHoverAnnotation() {
5155 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5156 return hoverAnnotation;
5157 }
5158 /**
5159 * Retrieves a boolean that indicates if point-specific
5160 * annotations popup whenever you hover over a point on the
5161 * curve associated with this symbol.<p>
5162 *
5163 * @return true if hover-induced annotations popup, false otherwise.
5164 *
5165 * @see #setHoverAnnotationEnabled setHoverAnnotationEnabled
5166 *
5167 */
5168 public boolean getHoverAnnotationEnabled() {
5169 return hoverAnnotationEnabled;
5170 }
5171 /**
5172 ** Retrieves the weight of the font that will be used
5173 ** with this symbol's hover annotations.
5174 ** <p>
5175 **
5176 ** @return the standard CSS font-weight
5177 ** specification such as normal, bold, bolder, lighter,
5178 ** 100, 200, ... 900, or inherit used by hover
5179 ** annotations
5180 **
5181 ** @see #setHoverFontWeight setHoverFontWeight
5182 **
5183 **
5184 **/
5185 public String getHoverFontWeight() {
5186 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5187 String result = hoverAnnotation.getFontWeight();
5188 return result;
5189 }
5190 /**
5191 ** Retrieves the font color of this symbol's hover
5192 ** annotations.
5193 **
5194 ** @return color of the font used to display this
5195 ** symbol's hover annotations
5196 **
5197 ** @see #setHoverFontColor setHoverFontColor
5198 **/
5199 public String getHoverFontColor() {
5200 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5201 String result = hoverAnnotation.getFontColor();
5202 return result;
5203 }
5204
5205
5206 /**
5207 ** Retrieves the CSS font-style used with this symbol's
5208 ** hover annotations.
5209 **
5210 ** @return the CSS font-style, namely,
5211 ** normal, italic, oblique, or inherit of text displayed
5212 ** in the hover annotations associated with this symbol
5213 **
5214 ** @see #setHoverFontStyle setHoverFontStyle
5215 **/
5216 public String getHoverFontStyle() {
5217 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5218 String result = hoverAnnotation.getFontStyle();
5219 return result;
5220 }
5221 /**
5222 ** Retrieves the CSS font size used with this symbol's hover
5223 ** annotations, in pixels.
5224 **
5225 ** @return the font size used in the text displayed
5226 ** in the hover annotations associated with this symbol.
5227 **
5228 ** @see #setHoverFontSize setHoverFontSize
5229 **
5230 **/
5231 public int getHoverFontSize() {
5232 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5233 int result = hoverAnnotation.getFontSize();
5234 return result;
5235 }
5236
5237 /**
5238 * Retrieves point-relative location of this symbol's hover
5239 * annotations. <p>
5240 *
5241 * @return the relative location of the hover annotations for
5242 * all points on the curve associated with this symbol.
5243 *
5244 * @see #setHoverLocation setHoverLocation
5245 * @see #DEFAULT_HOVER_LOCATION DEFAULT_HOVER_LOCATION
5246 *
5247 */
5248 public AnnotationLocation getHoverLocation() {
5249 if (hoverAnnotation == null) hoverAnnotation = new Annotation();
5250 AnnotationLocation result = hoverAnnotation.getLocation();
5251 if (null == result) result = getSymbolType().defaultHoverLocation();
5252 return result;
5253 }
5254 /**
5255 * Retrieves the symbol type that will determine how the
5256 * hover annotations for this symbol gets positioned.
5257 * <p>
5258 *
5259 * @return <tt>SymbolType</tt> used to position hover
5260 * annotations, or <tt>null</tt> if the symbol type of the
5261 * hovered over point is being used.
5262 *
5263 * @see #setHoverAnnotationSymbolType setHoverAnnotationSymbolType
5264 *
5265 */
5266 public SymbolType getHoverAnnotationSymbolType() {
5267 return hoverAnnotationSymbolType;
5268 }
5269 /**
5270 * Retrieves the background color used to indicate that the mouse is
5271 * "touching" (hovering over) a point.
5272 *
5273 * @return a CSS color specification string that represents
5274 * the background color used to indicate "hover-selection".
5275 *
5276 * @see #setHoverSelectionBackgroundColor
5277 * setHoverSelectionBackgroundColor