001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ----------------
028 * ContourPlot.java
029 * ----------------
030 * (C) Copyright 2002-2008, by David M. O'Donnell and Contributors.
031 *
032 * Original Author: David M. O'Donnell;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 * Arnaud Lelievre;
035 * Nicolas Brodu;
036 *
037 * Changes
038 * -------
039 * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG);
040 * 14-Jan-2003 : Added crosshair attributes (DG);
041 * 23-Jan-2003 : Removed two constructors (DG);
042 * 21-Mar-2003 : Bug fix 701744 (DG);
043 * 26-Mar-2003 : Implemented Serializable (DG);
044 * 09-Jul-2003 : Changed ColorBar from extending axis classes to enclosing
045 * them (DG);
046 * 05-Aug-2003 : Applied changes in bug report 780298 (DG);
047 * 08-Sep-2003 : Added internationalization via use of properties
048 * resourceBundle (RFE 690236) (AL);
049 * 11-Sep-2003 : Cloning support (NB);
050 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
051 * 17-Jan-2004 : Removed references to DefaultContourDataset class, replaced
052 * with ContourDataset interface (with changes to the interface).
053 * See bug 741048 (DG);
054 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
055 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
056 * 06-Oct-2004 : Updated for changes in DatasetUtilities class (DG);
057 * 11-Nov-2004 : Renamed zoom methods to match ValueAxisPlot interface (DG);
058 * 25-Nov-2004 : Small update to clone() implementation (DG);
059 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
060 * 05-May-2005 : Updated draw() method parameters (DG);
061 * 16-Jun-2005 : Added default constructor (DG);
062 * 01-Sep-2005 : Moved dataAreaRatio from Plot to here (DG);
063 * ------------- JFREECHART 1.0.x ---------------------------------------------
064 * 31-Jan-2007 : Deprecated (DG);
065 * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by
066 * Jess Thrysoee (DG);
067 *
068 */
069
070 package org.jfree.chart.plot;
071
072 import java.awt.AlphaComposite;
073 import java.awt.Composite;
074 import java.awt.Graphics2D;
075 import java.awt.Paint;
076 import java.awt.RenderingHints;
077 import java.awt.Shape;
078 import java.awt.Stroke;
079 import java.awt.geom.Ellipse2D;
080 import java.awt.geom.GeneralPath;
081 import java.awt.geom.Line2D;
082 import java.awt.geom.Point2D;
083 import java.awt.geom.Rectangle2D;
084 import java.awt.geom.RectangularShape;
085 import java.beans.PropertyChangeEvent;
086 import java.beans.PropertyChangeListener;
087 import java.io.Serializable;
088 import java.util.Iterator;
089 import java.util.List;
090 import java.util.ResourceBundle;
091
092 import org.jfree.chart.ClipPath;
093 import org.jfree.chart.annotations.XYAnnotation;
094 import org.jfree.chart.axis.AxisSpace;
095 import org.jfree.chart.axis.ColorBar;
096 import org.jfree.chart.axis.NumberAxis;
097 import org.jfree.chart.axis.ValueAxis;
098 import org.jfree.chart.entity.ContourEntity;
099 import org.jfree.chart.entity.EntityCollection;
100 import org.jfree.chart.event.AxisChangeEvent;
101 import org.jfree.chart.event.PlotChangeEvent;
102 import org.jfree.chart.labels.ContourToolTipGenerator;
103 import org.jfree.chart.labels.StandardContourToolTipGenerator;
104 import org.jfree.chart.renderer.xy.XYBlockRenderer;
105 import org.jfree.chart.urls.XYURLGenerator;
106 import org.jfree.chart.util.ResourceBundleWrapper;
107 import org.jfree.data.Range;
108 import org.jfree.data.contour.ContourDataset;
109 import org.jfree.data.general.DatasetChangeEvent;
110 import org.jfree.data.general.DatasetUtilities;
111 import org.jfree.ui.RectangleEdge;
112 import org.jfree.ui.RectangleInsets;
113 import org.jfree.util.ObjectUtilities;
114
115 /**
116 * A class for creating shaded contours.
117 *
118 * @deprecated This plot is no longer supported, please use {@link XYPlot} with
119 * an {@link XYBlockRenderer}.
120 */
121 public class ContourPlot extends Plot implements ContourValuePlot,
122 ValueAxisPlot, PropertyChangeListener, Serializable, Cloneable {
123
124 /** For serialization. */
125 private static final long serialVersionUID = 7861072556590502247L;
126
127 /** The default insets. */
128 protected static final RectangleInsets DEFAULT_INSETS
129 = new RectangleInsets(2.0, 2.0, 100.0, 10.0);
130
131 /** The domain axis (used for the x-values). */
132 private ValueAxis domainAxis;
133
134 /** The range axis (used for the y-values). */
135 private ValueAxis rangeAxis;
136
137 /** The dataset. */
138 private ContourDataset dataset;
139
140 /** The colorbar axis (used for the z-values). */
141 private ColorBar colorBar = null;
142
143 /** The color bar location. */
144 private RectangleEdge colorBarLocation;
145
146 /** A flag that controls whether or not a domain crosshair is drawn..*/
147 private boolean domainCrosshairVisible;
148
149 /** The domain crosshair value. */
150 private double domainCrosshairValue;
151
152 /** The pen/brush used to draw the crosshair (if any). */
153 private transient Stroke domainCrosshairStroke;
154
155 /** The color used to draw the crosshair (if any). */
156 private transient Paint domainCrosshairPaint;
157
158 /**
159 * A flag that controls whether or not the crosshair locks onto actual data
160 * points.
161 */
162 private boolean domainCrosshairLockedOnData = true;
163
164 /** A flag that controls whether or not a range crosshair is drawn..*/
165 private boolean rangeCrosshairVisible;
166
167 /** The range crosshair value. */
168 private double rangeCrosshairValue;
169
170 /** The pen/brush used to draw the crosshair (if any). */
171 private transient Stroke rangeCrosshairStroke;
172
173 /** The color used to draw the crosshair (if any). */
174 private transient Paint rangeCrosshairPaint;
175
176 /**
177 * A flag that controls whether or not the crosshair locks onto actual data
178 * points.
179 */
180 private boolean rangeCrosshairLockedOnData = true;
181
182 /**
183 * Defines dataArea rectangle as the ratio formed from dividing height by
184 * width (of the dataArea). Modifies plot area calculations.
185 * ratio>0 will attempt to layout the plot so that the
186 * dataArea.height/dataArea.width = ratio.
187 * ratio<0 will attempt to layout the plot so that the
188 * dataArea.height/dataArea.width in plot units (not java2D units as when
189 * ratio>0) = -1.*ratio.
190 */ //dmo
191 private double dataAreaRatio = 0.0; //zero when the parameter is not set
192
193 /** A list of markers (optional) for the domain axis. */
194 private List domainMarkers;
195
196 /** A list of markers (optional) for the range axis. */
197 private List rangeMarkers;
198
199 /** A list of annotations (optional) for the plot. */
200 private List annotations;
201
202 /** The tool tip generator. */
203 private ContourToolTipGenerator toolTipGenerator;
204
205 /** The URL text generator. */
206 private XYURLGenerator urlGenerator;
207
208 /**
209 * Controls whether data are render as filled rectangles or rendered as
210 * points
211 */
212 private boolean renderAsPoints = false;
213
214 /**
215 * Size of points rendered when renderAsPoints = true. Size is relative to
216 * dataArea
217 */
218 private double ptSizePct = 0.05;
219
220 /** Contains the a ClipPath to "trim" the contours. */
221 private transient ClipPath clipPath = null;
222
223 /** Set to Paint to represent missing values. */
224 private transient Paint missingPaint = null;
225
226 /** The resourceBundle for the localization. */
227 protected static ResourceBundle localizationResources
228 = ResourceBundleWrapper.getBundle(
229 "org.jfree.chart.plot.LocalizationBundle");
230
231 /**
232 * Creates a new plot with no dataset or axes.
233 */
234 public ContourPlot() {
235 this(null, null, null, null);
236 }
237
238 /**
239 * Constructs a contour plot with the specified axes (other attributes take
240 * default values).
241 *
242 * @param dataset The dataset.
243 * @param domainAxis The domain axis.
244 * @param rangeAxis The range axis.
245 * @param colorBar The z-axis axis.
246 */
247 public ContourPlot(ContourDataset dataset,
248 ValueAxis domainAxis, ValueAxis rangeAxis,
249 ColorBar colorBar) {
250
251 super();
252
253 this.dataset = dataset;
254 if (dataset != null) {
255 dataset.addChangeListener(this);
256 }
257
258 this.domainAxis = domainAxis;
259 if (domainAxis != null) {
260 domainAxis.setPlot(this);
261 domainAxis.addChangeListener(this);
262 }
263
264 this.rangeAxis = rangeAxis;
265 if (rangeAxis != null) {
266 rangeAxis.setPlot(this);
267 rangeAxis.addChangeListener(this);
268 }
269
270 this.colorBar = colorBar;
271 if (colorBar != null) {
272 colorBar.getAxis().setPlot(this);
273 colorBar.getAxis().addChangeListener(this);
274 colorBar.configure(this);
275 }
276 this.colorBarLocation = RectangleEdge.LEFT;
277
278 this.toolTipGenerator = new StandardContourToolTipGenerator();
279
280 }
281
282 /**
283 * Returns the color bar location.
284 *
285 * @return The color bar location.
286 */
287 public RectangleEdge getColorBarLocation() {
288 return this.colorBarLocation;
289 }
290
291 /**
292 * Sets the color bar location and sends a {@link PlotChangeEvent} to all
293 * registered listeners.
294 *
295 * @param edge the location.
296 */
297 public void setColorBarLocation(RectangleEdge edge) {
298 this.colorBarLocation = edge;
299 fireChangeEvent();
300 }
301
302 /**
303 * Returns the primary dataset for the plot.
304 *
305 * @return The primary dataset (possibly <code>null</code>).
306 */
307 public ContourDataset getDataset() {
308 return this.dataset;
309 }
310
311 /**
312 * Sets the dataset for the plot, replacing the existing dataset if there
313 * is one.
314 *
315 * @param dataset the dataset (<code>null</code> permitted).
316 */
317 public void setDataset(ContourDataset dataset) {
318
319 // if there is an existing dataset, remove the plot from the list of
320 // change listeners...
321 ContourDataset existing = this.dataset;
322 if (existing != null) {
323 existing.removeChangeListener(this);
324 }
325
326 // set the new dataset, and register the chart as a change listener...
327 this.dataset = dataset;
328 if (dataset != null) {
329 setDatasetGroup(dataset.getGroup());
330 dataset.addChangeListener(this);
331 }
332
333 // send a dataset change event to self...
334 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
335 datasetChanged(event);
336
337 }
338
339 /**
340 * Returns the domain axis for the plot.
341 *
342 * @return The domain axis.
343 */
344 public ValueAxis getDomainAxis() {
345
346 ValueAxis result = this.domainAxis;
347
348 return result;
349
350 }
351
352 /**
353 * Sets the domain axis for the plot (this must be compatible with the plot
354 * type or an exception is thrown).
355 *
356 * @param axis The new axis.
357 */
358 public void setDomainAxis(ValueAxis axis) {
359
360 if (isCompatibleDomainAxis(axis)) {
361
362 if (axis != null) {
363 axis.setPlot(this);
364 axis.addChangeListener(this);
365 }
366
367 // plot is likely registered as a listener with the existing axis...
368 if (this.domainAxis != null) {
369 this.domainAxis.removeChangeListener(this);
370 }
371
372 this.domainAxis = axis;
373 fireChangeEvent();
374
375 }
376
377 }
378
379 /**
380 * Returns the range axis for the plot.
381 *
382 * @return The range axis.
383 */
384 public ValueAxis getRangeAxis() {
385
386 ValueAxis result = this.rangeAxis;
387
388 return result;
389
390 }
391
392 /**
393 * Sets the range axis for the plot.
394 * <P>
395 * An exception is thrown if the new axis and the plot are not mutually
396 * compatible.
397 *
398 * @param axis The new axis (null permitted).
399 */
400 public void setRangeAxis(ValueAxis axis) {
401
402 if (axis != null) {
403 axis.setPlot(this);
404 axis.addChangeListener(this);
405 }
406
407 // plot is likely registered as a listener with the existing axis...
408 if (this.rangeAxis != null) {
409 this.rangeAxis.removeChangeListener(this);
410 }
411
412 this.rangeAxis = axis;
413 fireChangeEvent();
414
415 }
416
417 /**
418 * Sets the colorbar for the plot.
419 *
420 * @param axis The new axis (null permitted).
421 */
422 public void setColorBarAxis(ColorBar axis) {
423
424 this.colorBar = axis;
425 fireChangeEvent();
426
427 }
428
429 /**
430 * Returns the data area ratio.
431 *
432 * @return The ratio.
433 */
434 public double getDataAreaRatio() {
435 return this.dataAreaRatio;
436 }
437
438 /**
439 * Sets the data area ratio.
440 *
441 * @param ratio the ratio.
442 */
443 public void setDataAreaRatio(double ratio) {
444 this.dataAreaRatio = ratio;
445 }
446
447 /**
448 * Adds a marker for the domain axis.
449 * <P>
450 * Typically a marker will be drawn by the renderer as a line perpendicular
451 * to the range axis, however this is entirely up to the renderer.
452 *
453 * @param marker the marker.
454 */
455 public void addDomainMarker(Marker marker) {
456
457 if (this.domainMarkers == null) {
458 this.domainMarkers = new java.util.ArrayList();
459 }
460 this.domainMarkers.add(marker);
461 fireChangeEvent();
462
463 }
464
465 /**
466 * Clears all the domain markers.
467 */
468 public void clearDomainMarkers() {
469 if (this.domainMarkers != null) {
470 this.domainMarkers.clear();
471 fireChangeEvent();
472 }
473 }
474
475 /**
476 * Adds a marker for the range axis.
477 * <P>
478 * Typically a marker will be drawn by the renderer as a line perpendicular
479 * to the range axis, however this is entirely up to the renderer.
480 *
481 * @param marker The marker.
482 */
483 public void addRangeMarker(Marker marker) {
484
485 if (this.rangeMarkers == null) {
486 this.rangeMarkers = new java.util.ArrayList();
487 }
488 this.rangeMarkers.add(marker);
489 fireChangeEvent();
490
491 }
492
493 /**
494 * Clears all the range markers.
495 */
496 public void clearRangeMarkers() {
497 if (this.rangeMarkers != null) {
498 this.rangeMarkers.clear();
499 fireChangeEvent();
500 }
501 }
502
503 /**
504 * Adds an annotation to the plot.
505 *
506 * @param annotation the annotation.
507 */
508 public void addAnnotation(XYAnnotation annotation) {
509
510 if (this.annotations == null) {
511 this.annotations = new java.util.ArrayList();
512 }
513 this.annotations.add(annotation);
514 fireChangeEvent();
515
516 }
517
518 /**
519 * Clears all the annotations.
520 */
521 public void clearAnnotations() {
522 if (this.annotations != null) {
523 this.annotations.clear();
524 fireChangeEvent();
525 }
526 }
527
528 /**
529 * Checks the compatibility of a domain axis, returning true if the axis is
530 * compatible with the plot, and false otherwise.
531 *
532 * @param axis The proposed axis.
533 *
534 * @return <code>true</code> if the axis is compatible with the plot.
535 */
536 public boolean isCompatibleDomainAxis(ValueAxis axis) {
537
538 return true;
539
540 }
541
542 /**
543 * Draws the plot on a Java 2D graphics device (such as the screen or a
544 * printer).
545 * <P>
546 * The optional <code>info</code> argument collects information about the
547 * rendering of the plot (dimensions, tooltip information etc). Just pass
548 * in <code>null</code> if you do not need this information.
549 *
550 * @param g2 the graphics device.
551 * @param area the area within which the plot (including axis labels)
552 * should be drawn.
553 * @param anchor the anchor point (<code>null</code> permitted).
554 * @param parentState the state from the parent plot, if there is one.
555 * @param info collects chart drawing information (<code>null</code>
556 * permitted).
557 */
558 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
559 PlotState parentState,
560 PlotRenderingInfo info) {
561
562 // if the plot area is too small, just return...
563 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
564 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
565 if (b1 || b2) {
566 return;
567 }
568
569 // record the plot area...
570 if (info != null) {
571 info.setPlotArea(area);
572 }
573
574 // adjust the drawing area for plot insets (if any)...
575 RectangleInsets insets = getInsets();
576 insets.trim(area);
577
578 AxisSpace space = new AxisSpace();
579
580 space = this.domainAxis.reserveSpace(g2, this, area,
581 RectangleEdge.BOTTOM, space);
582 space = this.rangeAxis.reserveSpace(g2, this, area,
583 RectangleEdge.LEFT, space);
584
585 Rectangle2D estimatedDataArea = space.shrink(area, null);
586
587 AxisSpace space2 = new AxisSpace();
588 space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea,
589 this.colorBarLocation, space2);
590 Rectangle2D adjustedPlotArea = space2.shrink(area, null);
591
592 Rectangle2D dataArea = space.shrink(adjustedPlotArea, null);
593
594 Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation);
595
596 // additional dataArea modifications
597 if (getDataAreaRatio() != 0.0) { //check whether modification is
598 double ratio = getDataAreaRatio();
599 Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone();
600 double h = tmpDataArea.getHeight();
601 double w = tmpDataArea.getWidth();
602
603 if (ratio > 0) { // ratio represents pixels
604 if (w * ratio <= h) {
605 h = ratio * w;
606 }
607 else {
608 w = h / ratio;
609 }
610 }
611 else { // ratio represents axis units
612 ratio *= -1.0;
613 double xLength = getDomainAxis().getRange().getLength();
614 double yLength = getRangeAxis().getRange().getLength();
615 double unitRatio = yLength / xLength;
616
617 ratio = unitRatio * ratio;
618
619 if (w * ratio <= h) {
620 h = ratio * w;
621 }
622 else {
623 w = h / ratio;
624 }
625 }
626
627 dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2
628 - w / 2, tmpDataArea.getY(), w, h);
629 }
630
631 if (info != null) {
632 info.setDataArea(dataArea);
633 }
634
635 CrosshairState crosshairState = new CrosshairState();
636 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
637
638 // draw the plot background...
639 drawBackground(g2, dataArea);
640
641 double cursor = dataArea.getMaxY();
642 if (this.domainAxis != null) {
643 this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea,
644 RectangleEdge.BOTTOM, info);
645 }
646
647 if (this.rangeAxis != null) {
648 cursor = dataArea.getMinX();
649 this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea,
650 RectangleEdge.LEFT, info);
651 }
652
653 if (this.colorBar != null) {
654 cursor = 0.0;
655 cursor = this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea,
656 colorBarArea, this.colorBarLocation);
657 }
658 Shape originalClip = g2.getClip();
659 Composite originalComposite = g2.getComposite();
660
661 g2.clip(dataArea);
662 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
663 getForegroundAlpha()));
664 render(g2, dataArea, info, crosshairState);
665
666 if (this.domainMarkers != null) {
667 Iterator iterator = this.domainMarkers.iterator();
668 while (iterator.hasNext()) {
669 Marker marker = (Marker) iterator.next();
670 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea);
671 }
672 }
673
674 if (this.rangeMarkers != null) {
675 Iterator iterator = this.rangeMarkers.iterator();
676 while (iterator.hasNext()) {
677 Marker marker = (Marker) iterator.next();
678 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea);
679 }
680 }
681
682 // TO DO: these annotations only work with XYPlot, see if it is possible to
683 // make ContourPlot a subclass of XYPlot (DG);
684
685 // // draw the annotations...
686 // if (this.annotations != null) {
687 // Iterator iterator = this.annotations.iterator();
688 // while (iterator.hasNext()) {
689 // Annotation annotation = (Annotation) iterator.next();
690 // if (annotation instanceof XYAnnotation) {
691 // XYAnnotation xya = (XYAnnotation) annotation;
692 // // get the annotation to draw itself...
693 // xya.draw(g2, this, dataArea, getDomainAxis(),
694 // getRangeAxis());
695 // }
696 // }
697 // }
698
699 g2.setClip(originalClip);
700 g2.setComposite(originalComposite);
701 drawOutline(g2, dataArea);
702
703 }
704
705 /**
706 * Draws a representation of the data within the dataArea region, using the
707 * current renderer.
708 * <P>
709 * The <code>info</code> and <code>crosshairState</code> arguments may be
710 * <code>null</code>.
711 *
712 * @param g2 the graphics device.
713 * @param dataArea the region in which the data is to be drawn.
714 * @param info an optional object for collection dimension information.
715 * @param crosshairState an optional object for collecting crosshair info.
716 */
717 public void render(Graphics2D g2, Rectangle2D dataArea,
718 PlotRenderingInfo info, CrosshairState crosshairState) {
719
720 // now get the data and plot it (the visual representation will depend
721 // on the renderer that has been set)...
722 ContourDataset data = getDataset();
723 if (data != null) {
724
725 ColorBar zAxis = getColorBar();
726
727 if (this.clipPath != null) {
728 GeneralPath clipper = getClipPath().draw(g2, dataArea,
729 this.domainAxis, this.rangeAxis);
730 if (this.clipPath.isClip()) {
731 g2.clip(clipper);
732 }
733 }
734
735 if (this.renderAsPoints) {
736 pointRenderer(g2, dataArea, info, this, this.domainAxis,
737 this.rangeAxis, zAxis, data, crosshairState);
738 }
739 else {
740 contourRenderer(g2, dataArea, info, this, this.domainAxis,
741 this.rangeAxis, zAxis, data, crosshairState);
742 }
743
744 // draw vertical crosshair if required...
745 setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
746 if (isDomainCrosshairVisible()) {
747 drawVerticalLine(g2, dataArea,
748 getDomainCrosshairValue(),
749 getDomainCrosshairStroke(),
750 getDomainCrosshairPaint());
751 }
752
753 // draw horizontal crosshair if required...
754 setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
755 if (isRangeCrosshairVisible()) {
756 drawHorizontalLine(g2, dataArea,
757 getRangeCrosshairValue(),
758 getRangeCrosshairStroke(),
759 getRangeCrosshairPaint());
760 }
761
762 }
763 else if (this.clipPath != null) {
764 getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis);
765 }
766
767 }
768
769 /**
770 * Fills the plot.
771 *
772 * @param g2 the graphics device.
773 * @param dataArea the area within which the data is being drawn.
774 * @param info collects information about the drawing.
775 * @param plot the plot (can be used to obtain standard color
776 * information etc).
777 * @param horizontalAxis the domain (horizontal) axis.
778 * @param verticalAxis the range (vertical) axis.
779 * @param colorBar the color bar axis.
780 * @param data the dataset.
781 * @param crosshairState information about crosshairs on a plot.
782 */
783 public void contourRenderer(Graphics2D g2,
784 Rectangle2D dataArea,
785 PlotRenderingInfo info,
786 ContourPlot plot,
787 ValueAxis horizontalAxis,
788 ValueAxis verticalAxis,
789 ColorBar colorBar,
790 ContourDataset data,
791 CrosshairState crosshairState) {
792
793 // setup for collecting optional entity info...
794 Rectangle2D.Double entityArea = null;
795 EntityCollection entities = null;
796 if (info != null) {
797 entities = info.getOwner().getEntityCollection();
798 }
799
800 Rectangle2D.Double rect = null;
801 rect = new Rectangle2D.Double();
802
803 //turn off anti-aliasing when filling rectangles
804 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
805 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
806 RenderingHints.VALUE_ANTIALIAS_OFF);
807
808 // get the data points
809 Number[] xNumber = data.getXValues();
810 Number[] yNumber = data.getYValues();
811 Number[] zNumber = data.getZValues();
812
813 double[] x = new double[xNumber.length];
814 double[] y = new double[yNumber.length];
815
816 for (int i = 0; i < x.length; i++) {
817 x[i] = xNumber[i].doubleValue();
818 y[i] = yNumber[i].doubleValue();
819 }
820
821 int[] xIndex = data.indexX();
822 int[] indexX = data.getXIndices();
823 boolean vertInverted = ((NumberAxis) verticalAxis).isInverted();
824 boolean horizInverted = false;
825 if (horizontalAxis instanceof NumberAxis) {
826 horizInverted = ((NumberAxis) horizontalAxis).isInverted();
827 }
828 double transX = 0.0;
829 double transXm1 = 0.0;
830 double transXp1 = 0.0;
831 double transDXm1 = 0.0;
832 double transDXp1 = 0.0;
833 double transDX = 0.0;
834 double transY = 0.0;
835 double transYm1 = 0.0;
836 double transYp1 = 0.0;
837 double transDYm1 = 0.0;
838 double transDYp1 = 0.0;
839 double transDY = 0.0;
840 int iMax = xIndex[xIndex.length - 1];
841 for (int k = 0; k < x.length; k++) {
842 int i = xIndex[k];
843 if (indexX[i] == k) { // this is a new column
844 if (i == 0) {
845 transX = horizontalAxis.valueToJava2D(x[k], dataArea,
846 RectangleEdge.BOTTOM);
847 transXm1 = transX;
848 transXp1 = horizontalAxis.valueToJava2D(
849 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM);
850 transDXm1 = Math.abs(0.5 * (transX - transXm1));
851 transDXp1 = Math.abs(0.5 * (transX - transXp1));
852 }
853 else if (i == iMax) {
854 transX = horizontalAxis.valueToJava2D(x[k], dataArea,
855 RectangleEdge.BOTTOM);
856 transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]],
857 dataArea, RectangleEdge.BOTTOM);
858 transXp1 = transX;
859 transDXm1 = Math.abs(0.5 * (transX - transXm1));
860 transDXp1 = Math.abs(0.5 * (transX - transXp1));
861 }
862 else {
863 transX = horizontalAxis.valueToJava2D(x[k], dataArea,
864 RectangleEdge.BOTTOM);
865 transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]],
866 dataArea, RectangleEdge.BOTTOM);
867 transDXm1 = transDXp1;
868 transDXp1 = Math.abs(0.5 * (transX - transXp1));
869 }
870
871 if (horizInverted) {
872 transX -= transDXp1;
873 }
874 else {
875 transX -= transDXm1;
876 }
877
878 transDX = transDXm1 + transDXp1;
879
880 transY = verticalAxis.valueToJava2D(y[k], dataArea,
881 RectangleEdge.LEFT);
882 transYm1 = transY;
883 if (k + 1 == y.length) {
884 continue;
885 }
886 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea,
887 RectangleEdge.LEFT);
888 transDYm1 = Math.abs(0.5 * (transY - transYm1));
889 transDYp1 = Math.abs(0.5 * (transY - transYp1));
890 }
891 else if ((i < indexX.length - 1
892 && indexX[i + 1] - 1 == k) || k == x.length - 1) {
893 // end of column
894 transY = verticalAxis.valueToJava2D(y[k], dataArea,
895 RectangleEdge.LEFT);
896 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea,
897 RectangleEdge.LEFT);
898 transYp1 = transY;
899 transDYm1 = Math.abs(0.5 * (transY - transYm1));
900 transDYp1 = Math.abs(0.5 * (transY - transYp1));
901 }
902 else {
903 transY = verticalAxis.valueToJava2D(y[k], dataArea,
904 RectangleEdge.LEFT);
905 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea,
906 RectangleEdge.LEFT);
907 transDYm1 = transDYp1;
908 transDYp1 = Math.abs(0.5 * (transY - transYp1));
909 }
910 if (vertInverted) {
911 transY -= transDYm1;
912 }
913 else {
914 transY -= transDYp1;
915 }
916
917 transDY = transDYm1 + transDYp1;
918
919 rect.setRect(transX, transY, transDX, transDY);
920 if (zNumber[k] != null) {
921 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
922 g2.fill(rect);
923 }
924 else if (this.missingPaint != null) {
925 g2.setPaint(this.missingPaint);
926 g2.fill(rect);
927 }
928
929 entityArea = rect;
930
931 // add an entity for the item...
932 if (entities != null) {
933 String tip = "";
934 if (getToolTipGenerator() != null) {
935 tip = this.toolTipGenerator.generateToolTip(data, k);
936 }
937 // Shape s = g2.getClip();
938 // if (s.contains(rect) || s.intersects(rect)) {
939 String url = null;
940 // if (getURLGenerator() != null) { //dmo: look at this later
941 // url = getURLGenerator().generateURL(data, series, item);
942 // }
943 // Unlike XYItemRenderer, we need to clone entityArea since it
944 // reused.
945 ContourEntity entity = new ContourEntity(
946 (Rectangle2D.Double) entityArea.clone(), tip, url);
947 entity.setIndex(k);
948 entities.add(entity);
949 // }
950 }
951
952 // do we need to update the crosshair values?
953 if (plot.isDomainCrosshairLockedOnData()) {
954 if (plot.isRangeCrosshairLockedOnData()) {
955 // both axes
956 crosshairState.updateCrosshairPoint(x[k], y[k], transX,
957 transY, PlotOrientation.VERTICAL);
958 }
959 else {
960 // just the horizontal axis...
961 crosshairState.updateCrosshairX(transX);
962 }
963 }
964 else {
965 if (plot.isRangeCrosshairLockedOnData()) {
966 // just the vertical axis...
967 crosshairState.updateCrosshairY(transY);
968 }
969 }
970 }
971
972 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
973
974 return;
975
976 }
977
978 /**
979 * Draws the visual representation of a single data item.
980 *
981 * @param g2 the graphics device.
982 * @param dataArea the area within which the data is being drawn.
983 * @param info collects information about the drawing.
984 * @param plot the plot (can be used to obtain standard color
985 * information etc).
986 * @param domainAxis the domain (horizontal) axis.
987 * @param rangeAxis the range (vertical) axis.
988 * @param colorBar the color bar axis.
989 * @param data the dataset.
990 * @param crosshairState information about crosshairs on a plot.
991 */
992 public void pointRenderer(Graphics2D g2,
993 Rectangle2D dataArea,
994 PlotRenderingInfo info,
995 ContourPlot plot,
996 ValueAxis domainAxis,
997 ValueAxis rangeAxis,
998 ColorBar colorBar,
999 ContourDataset data,
1000 CrosshairState crosshairState) {
1001
1002 // setup for collecting optional entity info...
1003 RectangularShape entityArea = null;
1004 EntityCollection entities = null;
1005 if (info != null) {
1006 entities = info.getOwner().getEntityCollection();
1007 }
1008
1009 // Rectangle2D.Double rect = null;
1010 // rect = new Rectangle2D.Double();
1011 RectangularShape rect = new Ellipse2D.Double();
1012
1013
1014 //turn off anti-aliasing when filling rectangles
1015 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
1016 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1017 RenderingHints.VALUE_ANTIALIAS_OFF);
1018
1019 // if (tooltips!=null) tooltips.clearToolTips(); // reset collection
1020 // get the data points
1021 Number[] xNumber = data.getXValues();
1022 Number[] yNumber = data.getYValues();
1023 Number[] zNumber = data.getZValues();
1024
1025 double[] x = new double[xNumber.length];
1026 double[] y = new double[yNumber.length];
1027
1028 for (int i = 0; i < x.length; i++) {
1029 x[i] = xNumber[i].doubleValue();
1030 y[i] = yNumber[i].doubleValue();
1031 }
1032
1033 double transX = 0.0;
1034 double transDX = 0.0;
1035 double transY = 0.0;
1036 double transDY = 0.0;
1037 double size = dataArea.getWidth() * this.ptSizePct;
1038 for (int k = 0; k < x.length; k++) {
1039
1040 transX = domainAxis.valueToJava2D(x[k], dataArea,
1041 RectangleEdge.BOTTOM) - 0.5 * size;
1042 transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT)
1043 - 0.5 * size;
1044 transDX = size;
1045 transDY = size;
1046
1047 rect.setFrame(transX, transY, transDX, transDY);
1048
1049 if (zNumber[k] != null) {
1050 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
1051 g2.fill(rect);
1052 }
1053 else if (this.missingPaint != null) {
1054 g2.setPaint(this.missingPaint);
1055 g2.fill(rect);
1056 }
1057
1058
1059 entityArea = rect;
1060
1061 // add an entity for the item...
1062 if (entities != null) {
1063 String tip = null;
1064 if (getToolTipGenerator() != null) {
1065 tip = this.toolTipGenerator.generateToolTip(data, k);
1066 }
1067 String url = null;
1068 // if (getURLGenerator() != null) { //dmo: look at this later
1069 // url = getURLGenerator().generateURL(data, series, item);
1070 // }
1071 // Unlike XYItemRenderer, we need to clone entityArea since it
1072 // reused.
1073 ContourEntity entity = new ContourEntity(
1074 (RectangularShape) entityArea.clone(), tip, url);
1075 entity.setIndex(k);
1076 entities.add(entity);
1077 }
1078
1079 // do we need to update the crosshair values?
1080 if (plot.isDomainCrosshairLockedOnData()) {
1081 if (plot.isRangeCrosshairLockedOnData()) {
1082 // both axes
1083 crosshairState.updateCrosshairPoint(x[k], y[k], transX,
1084 transY, PlotOrientation.VERTICAL);
1085 }
1086 else {
1087 // just the horizontal axis...
1088 crosshairState.updateCrosshairX(transX);
1089 }
1090 }
1091 else {
1092 if (plot.isRangeCrosshairLockedOnData()) {
1093 // just the vertical axis...
1094 crosshairState.updateCrosshairY(transY);
1095 }
1096 }
1097 }
1098
1099
1100 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
1101
1102 return;
1103
1104 }
1105
1106 /**
1107 * Utility method for drawing a crosshair on the chart (if required).
1108 *
1109 * @param g2 The graphics device.
1110 * @param dataArea The data area.
1111 * @param value The coordinate, where to draw the line.
1112 * @param stroke The stroke to use.
1113 * @param paint The paint to use.
1114 */
1115 protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea,
1116 double value, Stroke stroke, Paint paint) {
1117
1118 double xx = getDomainAxis().valueToJava2D(value, dataArea,
1119 RectangleEdge.BOTTOM);
1120 Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx,
1121 dataArea.getMaxY());
1122 g2.setStroke(stroke);
1123 g2.setPaint(paint);
1124 g2.draw(line);
1125
1126 }
1127
1128 /**
1129 * Utility method for drawing a crosshair on the chart (if required).
1130 *
1131 * @param g2 The graphics device.
1132 * @param dataArea The data area.
1133 * @param value The coordinate, where to draw the line.
1134 * @param stroke The stroke to use.
1135 * @param paint The paint to use.
1136 */
1137 protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea,
1138 double value, Stroke stroke,
1139 Paint paint) {
1140
1141 double yy = getRangeAxis().valueToJava2D(value, dataArea,
1142 RectangleEdge.LEFT);
1143 Line2D line = new Line2D.Double(dataArea.getMinX(), yy,
1144 dataArea.getMaxX(), yy);
1145 g2.setStroke(stroke);
1146 g2.setPaint(paint);
1147 g2.draw(line);
1148
1149 }
1150
1151 /**
1152 * Handles a 'click' on the plot by updating the anchor values...
1153 *
1154 * @param x x-coordinate, where the click occured.
1155 * @param y y-coordinate, where the click occured.
1156 * @param info An object for collection dimension information.
1157 */
1158 public void handleClick(int x, int y, PlotRenderingInfo info) {
1159
1160 /* // set the anchor value for the horizontal axis...
1161 ValueAxis hva = getDomainAxis();
1162 if (hva != null) {
1163 double hvalue = hva.translateJava2DtoValue(
1164 (float) x, info.getDataArea()
1165 );
1166
1167 hva.setAnchorValue(hvalue);
1168 setDomainCrosshairValue(hvalue);
1169 }
1170
1171 // set the anchor value for the vertical axis...
1172 ValueAxis vva = getRangeAxis();
1173 if (vva != null) {
1174 double vvalue = vva.translateJava2DtoValue(
1175 (float) y, info.getDataArea()
1176 );
1177 vva.setAnchorValue(vvalue);
1178 setRangeCrosshairValue(vvalue);
1179 }
1180 */
1181 }
1182
1183 /**
1184 * Zooms the axis ranges by the specified percentage about the anchor point.
1185 *
1186 * @param percent The amount of the zoom.
1187 */
1188 public void zoom(double percent) {
1189
1190 if (percent > 0) {
1191 // double range = this.domainAxis.getRange().getLength();
1192 // double scaledRange = range * percent;
1193 // domainAxis.setAnchoredRange(scaledRange);
1194
1195 // range = this.rangeAxis.getRange().getLength();
1196 // scaledRange = range * percent;
1197 // rangeAxis.setAnchoredRange(scaledRange);
1198 }
1199 else {
1200 getRangeAxis().setAutoRange(true);
1201 getDomainAxis().setAutoRange(true);
1202 }
1203
1204 }
1205
1206 /**
1207 * Returns the plot type as a string.
1208 *
1209 * @return A short string describing the type of plot.
1210 */
1211 public String getPlotType() {
1212 return localizationResources.getString("Contour_Plot");
1213 }
1214
1215 /**
1216 * Returns the range for an axis.
1217 *
1218 * @param axis the axis.
1219 *
1220 * @return The range for an axis.
1221 */
1222 public Range getDataRange(ValueAxis axis) {
1223
1224 if (this.dataset == null) {
1225 return null;
1226 }
1227
1228 Range result = null;
1229
1230 if (axis == getDomainAxis()) {
1231 result = DatasetUtilities.findDomainBounds(this.dataset);
1232 }
1233 else if (axis == getRangeAxis()) {
1234 result = DatasetUtilities.findRangeBounds(this.dataset);
1235 }
1236
1237 return result;
1238
1239 }
1240
1241 /**
1242 * Returns the range for the Contours.
1243 *
1244 * @return The range for the Contours (z-axis).
1245 */
1246 public Range getContourDataRange() {
1247
1248 Range result = null;
1249
1250 ContourDataset data = getDataset();
1251
1252 if (data != null) {
1253 Range h = getDomainAxis().getRange();
1254 Range v = getRangeAxis().getRange();
1255 result = this.visibleRange(data, h, v);
1256 }
1257
1258 return result;
1259 }
1260
1261 /**
1262 * Notifies all registered listeners of a property change.
1263 * <P>
1264 * One source of property change events is the plot's renderer.
1265 *
1266 * @param event Information about the property change.
1267 */
1268 public void propertyChange(PropertyChangeEvent event) {
1269 fireChangeEvent();
1270 }
1271
1272 /**
1273 * Receives notification of a change to the plot's dataset.
1274 * <P>
1275 * The chart reacts by passing on a chart change event to all registered
1276 * listeners.
1277 *
1278 * @param event Information about the event (not used here).
1279 */
1280 public void datasetChanged(DatasetChangeEvent event) {
1281 if (this.domainAxis != null) {
1282 this.domainAxis.configure();
1283 }
1284 if (this.rangeAxis != null) {
1285 this.rangeAxis.configure();
1286 }
1287 if (this.colorBar != null) {
1288 this.colorBar.configure(this);
1289 }
1290 super.datasetChanged(event);
1291 }
1292
1293 /**
1294 * Returns the colorbar.
1295 *
1296 * @return The colorbar.
1297 */
1298 public ColorBar getColorBar() {
1299 return this.colorBar;
1300 }
1301
1302 /**
1303 * Returns a flag indicating whether or not the domain crosshair is visible.
1304 *
1305 * @return The flag.
1306 */
1307 public boolean isDomainCrosshairVisible() {
1308 return this.domainCrosshairVisible;
1309 }
1310
1311 /**
1312 * Sets the flag indicating whether or not the domain crosshair is visible.
1313 *
1314 * @param flag the new value of the flag.
1315 */
1316 public void setDomainCrosshairVisible(boolean flag) {
1317
1318 if (this.domainCrosshairVisible != flag) {
1319 this.domainCrosshairVisible = flag;
1320 fireChangeEvent();
1321 }
1322
1323 }
1324
1325 /**
1326 * Returns a flag indicating whether or not the crosshair should "lock-on"
1327 * to actual data values.
1328 *
1329 * @return The flag.
1330 */
1331 public boolean isDomainCrosshairLockedOnData() {
1332 return this.domainCrosshairLockedOnData;
1333 }
1334
1335 /**
1336 * Sets the flag indicating whether or not the domain crosshair should
1337 * "lock-on" to actual data values.
1338 *
1339 * @param flag the flag.
1340 */
1341 public void setDomainCrosshairLockedOnData(boolean flag) {
1342 if (this.domainCrosshairLockedOnData != flag) {
1343 this.domainCrosshairLockedOnData = flag;
1344 fireChangeEvent();
1345 }
1346 }
1347
1348 /**
1349 * Returns the domain crosshair value.
1350 *
1351 * @return The value.
1352 */
1353 public double getDomainCrosshairValue() {
1354 return this.domainCrosshairValue;
1355 }
1356
1357 /**
1358 * Sets the domain crosshair value.
1359 * <P>
1360 * Registered listeners are notified that the plot has been modified, but
1361 * only if the crosshair is visible.
1362 *
1363 * @param value the new value.
1364 */
1365 public void setDomainCrosshairValue(double value) {
1366 setDomainCrosshairValue(value, true);
1367 }
1368
1369 /**
1370 * Sets the domain crosshair value.
1371 * <P>
1372 * Registered listeners are notified that the axis has been modified, but
1373 * only if the crosshair is visible.
1374 *
1375 * @param value the new value.
1376 * @param notify a flag that controls whether or not listeners are
1377 * notified.
1378 */
1379 public void setDomainCrosshairValue(double value, boolean notify) {
1380 this.domainCrosshairValue = value;
1381 if (isDomainCrosshairVisible() && notify) {
1382 fireChangeEvent();
1383 }
1384 }
1385
1386 /**
1387 * Returns the Stroke used to draw the crosshair (if visible).
1388 *
1389 * @return The crosshair stroke.
1390 */
1391 public Stroke getDomainCrosshairStroke() {
1392 return this.domainCrosshairStroke;
1393 }
1394
1395 /**
1396 * Sets the Stroke used to draw the crosshairs (if visible) and notifies
1397 * registered listeners that the axis has been modified.
1398 *
1399 * @param stroke the new crosshair stroke.
1400 */
1401 public void setDomainCrosshairStroke(Stroke stroke) {
1402 this.domainCrosshairStroke = stroke;
1403 fireChangeEvent();
1404 }
1405
1406 /**
1407 * Returns the domain crosshair color.
1408 *
1409 * @return The crosshair color.
1410 */
1411 public Paint getDomainCrosshairPaint() {
1412 return this.domainCrosshairPaint;
1413 }
1414
1415 /**
1416 * Sets the Paint used to color the crosshairs (if visible) and notifies
1417 * registered listeners that the axis has been modified.
1418 *
1419 * @param paint the new crosshair paint.
1420 */
1421 public void setDomainCrosshairPaint(Paint paint) {
1422 this.domainCrosshairPaint = paint;
1423 fireChangeEvent();
1424 }
1425
1426 /**
1427 * Returns a flag indicating whether or not the range crosshair is visible.
1428 *
1429 * @return The flag.
1430 */
1431 public boolean isRangeCrosshairVisible() {
1432 return this.rangeCrosshairVisible;
1433 }
1434
1435 /**
1436 * Sets the flag indicating whether or not the range crosshair is visible.
1437 *
1438 * @param flag the new value of the flag.
1439 */
1440 public void setRangeCrosshairVisible(boolean flag) {
1441 if (this.rangeCrosshairVisible != flag) {
1442 this.rangeCrosshairVisible = flag;
1443 fireChangeEvent();
1444 }
1445 }
1446
1447 /**
1448 * Returns a flag indicating whether or not the crosshair should "lock-on"
1449 * to actual data values.
1450 *
1451 * @return The flag.
1452 */
1453 public boolean isRangeCrosshairLockedOnData() {
1454 return this.rangeCrosshairLockedOnData;
1455 }
1456
1457 /**
1458 * Sets the flag indicating whether or not the range crosshair should
1459 * "lock-on" to actual data values.
1460 *
1461 * @param flag the flag.
1462 */
1463 public void setRangeCrosshairLockedOnData(boolean flag) {
1464 if (this.rangeCrosshairLockedOnData != flag) {
1465 this.rangeCrosshairLockedOnData = flag;
1466 fireChangeEvent();
1467 }
1468 }
1469
1470 /**
1471 * Returns the range crosshair value.
1472 *
1473 * @return The value.
1474 */
1475 public double getRangeCrosshairValue() {
1476 return this.rangeCrosshairValue;
1477 }
1478
1479 /**
1480 * Sets the domain crosshair value.
1481 * <P>
1482 * Registered listeners are notified that the plot has been modified, but
1483 * only if the crosshair is visible.
1484 *
1485 * @param value the new value.
1486 */
1487 public void setRangeCrosshairValue(double value) {
1488 setRangeCrosshairValue(value, true);
1489 }
1490
1491 /**
1492 * Sets the range crosshair value.
1493 * <P>
1494 * Registered listeners are notified that the axis has been modified, but
1495 * only if the crosshair is visible.
1496 *
1497 * @param value the new value.
1498 * @param notify a flag that controls whether or not listeners are
1499 * notified.
1500 */
1501 public void setRangeCrosshairValue(double value, boolean notify) {
1502 this.rangeCrosshairValue = value;
1503 if (isRangeCrosshairVisible() && notify) {
1504 fireChangeEvent();
1505 }
1506 }
1507
1508 /**
1509 * Returns the Stroke used to draw the crosshair (if visible).
1510 *
1511 * @return The crosshair stroke.
1512 */
1513 public Stroke getRangeCrosshairStroke() {
1514 return this.rangeCrosshairStroke;
1515 }
1516
1517 /**
1518 * Sets the Stroke used to draw the crosshairs (if visible) and notifies
1519 * registered listeners that the axis has been modified.
1520 *
1521 * @param stroke the new crosshair stroke.
1522 */
1523 public void setRangeCrosshairStroke(Stroke stroke) {
1524 this.rangeCrosshairStroke = stroke;
1525 fireChangeEvent();
1526 }
1527
1528 /**
1529 * Returns the range crosshair color.
1530 *
1531 * @return The crosshair color.
1532 */
1533 public Paint getRangeCrosshairPaint() {
1534 return this.rangeCrosshairPaint;
1535 }
1536
1537 /**
1538 * Sets the Paint used to color the crosshairs (if visible) and notifies
1539 * registered listeners that the axis has been modified.
1540 *
1541 * @param paint the new crosshair paint.
1542 */
1543 public void setRangeCrosshairPaint(Paint paint) {
1544 this.rangeCrosshairPaint = paint;
1545 fireChangeEvent();
1546 }
1547
1548 /**
1549 * Returns the tool tip generator.
1550 *
1551 * @return The tool tip generator (possibly null).
1552 */
1553 public ContourToolTipGenerator getToolTipGenerator() {
1554 return this.toolTipGenerator;
1555 }
1556
1557 /**
1558 * Sets the tool tip generator.
1559 *
1560 * @param generator the tool tip generator (null permitted).
1561 */
1562 public void setToolTipGenerator(ContourToolTipGenerator generator) {
1563 //Object oldValue = this.toolTipGenerator;
1564 this.toolTipGenerator = generator;
1565 }
1566
1567 /**
1568 * Returns the URL generator for HTML image maps.
1569 *
1570 * @return The URL generator (possibly null).
1571 */
1572 public XYURLGenerator getURLGenerator() {
1573 return this.urlGenerator;
1574 }
1575
1576 /**
1577 * Sets the URL generator for HTML image maps.
1578 *
1579 * @param urlGenerator the URL generator (null permitted).
1580 */
1581 public void setURLGenerator(XYURLGenerator urlGenerator) {
1582 //Object oldValue = this.urlGenerator;
1583 this.urlGenerator = urlGenerator;
1584 }
1585
1586 /**
1587 * Draws a vertical line on the chart to represent a 'range marker'.
1588 *
1589 * @param g2 the graphics device.
1590 * @param plot the plot.
1591 * @param domainAxis the domain axis.
1592 * @param marker the marker line.
1593 * @param dataArea the axis data area.
1594 */
1595 public void drawDomainMarker(Graphics2D g2,
1596 ContourPlot plot,
1597 ValueAxis domainAxis,
1598 Marker marker,
1599 Rectangle2D dataArea) {
1600
1601 if (marker instanceof ValueMarker) {
1602 ValueMarker vm = (ValueMarker) marker;
1603 double value = vm.getValue();
1604 Range range = domainAxis.getRange();
1605 if (!range.contains(value)) {
1606 return;
1607 }
1608
1609 double x = domainAxis.valueToJava2D(value, dataArea,
1610 RectangleEdge.BOTTOM);
1611 Line2D line = new Line2D.Double(x, dataArea.getMinY(), x,
1612 dataArea.getMaxY());
1613 Paint paint = marker.getOutlinePaint();
1614 Stroke stroke = marker.getOutlineStroke();
1615 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
1616 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
1617 g2.draw(line);
1618 }
1619
1620 }
1621
1622 /**
1623 * Draws a horizontal line across the chart to represent a 'range marker'.
1624 *
1625 * @param g2 the graphics device.
1626 * @param plot the plot.
1627 * @param rangeAxis the range axis.
1628 * @param marker the marker line.
1629 * @param dataArea the axis data area.
1630 */
1631 public void drawRangeMarker(Graphics2D g2,
1632 ContourPlot plot,
1633 ValueAxis rangeAxis,
1634 Marker marker,
1635 Rectangle2D dataArea) {
1636
1637 if (marker instanceof ValueMarker) {
1638 ValueMarker vm = (ValueMarker) marker;
1639 double value = vm.getValue();
1640 Range range = rangeAxis.getRange();
1641 if (!range.contains(value)) {
1642 return;
1643 }
1644
1645 double y = rangeAxis.valueToJava2D(value, dataArea,
1646 RectangleEdge.LEFT);
1647 Line2D line = new Line2D.Double(dataArea.getMinX(), y,
1648 dataArea.getMaxX(), y);
1649 Paint paint = marker.getOutlinePaint();
1650 Stroke stroke = marker.getOutlineStroke();
1651 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
1652 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
1653 g2.draw(line);
1654 }
1655
1656 }
1657
1658 /**
1659 * Returns the clipPath.
1660 * @return ClipPath
1661 */
1662 public ClipPath getClipPath() {
1663 return this.clipPath;
1664 }
1665
1666 /**
1667 * Sets the clipPath.
1668 * @param clipPath The clipPath to set
1669 */
1670 public void setClipPath(ClipPath clipPath) {
1671 this.clipPath = clipPath;
1672 }
1673
1674 /**
1675 * Returns the ptSizePct.
1676 * @return double
1677 */
1678 public double getPtSizePct() {
1679 return this.ptSizePct;
1680 }
1681
1682 /**
1683 * Returns the renderAsPoints.
1684 * @return boolean
1685 */
1686 public boolean isRenderAsPoints() {
1687 return this.renderAsPoints;
1688 }
1689
1690 /**
1691 * Sets the ptSizePct.
1692 * @param ptSizePct The ptSizePct to set
1693 */
1694 public void setPtSizePct(double ptSizePct) {
1695 this.ptSizePct = ptSizePct;
1696 }
1697
1698 /**
1699 * Sets the renderAsPoints.
1700 * @param renderAsPoints The renderAsPoints to set
1701 */
1702 public void setRenderAsPoints(boolean renderAsPoints) {
1703 this.renderAsPoints = renderAsPoints;
1704 }
1705
1706 /**
1707 * Receives notification of a change to one of the plot's axes.
1708 *
1709 * @param event information about the event.
1710 */
1711 public void axisChanged(AxisChangeEvent event) {
1712 Object source = event.getSource();
1713 if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) {
1714 ColorBar cba = this.colorBar;
1715 if (this.colorBar.getAxis().isAutoRange()) {
1716 cba.getAxis().configure();
1717 }
1718
1719 }
1720 super.axisChanged(event);
1721 }
1722
1723 /**
1724 * Returns the visible z-range.
1725 *
1726 * @param data the dataset.
1727 * @param x the x range.
1728 * @param y the y range.
1729 *
1730 * @return The range.
1731 */
1732 public Range visibleRange(ContourDataset data, Range x, Range y) {
1733 Range range = null;
1734 range = data.getZValueRange(x, y);
1735 return range;
1736 }
1737
1738 /**
1739 * Returns the missingPaint.
1740 * @return Paint
1741 */
1742 public Paint getMissingPaint() {
1743 return this.missingPaint;
1744 }
1745
1746 /**
1747 * Sets the missingPaint.
1748 *
1749 * @param paint the missingPaint to set.
1750 */
1751 public void setMissingPaint(Paint paint) {
1752 this.missingPaint = paint;
1753 }
1754
1755 /**
1756 * Multiplies the range on the domain axis/axes by the specified factor
1757 * (to be implemented).
1758 *
1759 * @param x the x-coordinate (in Java2D space).
1760 * @param y the y-coordinate (in Java2D space).
1761 * @param factor the zoom factor.
1762 */
1763 public void zoomDomainAxes(double x, double y, double factor) {
1764 // TODO: to be implemented
1765 }
1766
1767 /**
1768 * Zooms the domain axes (not yet implemented).
1769 *
1770 * @param x the x-coordinate (in Java2D space).
1771 * @param y the y-coordinate (in Java2D space).
1772 * @param lowerPercent the new lower bound.
1773 * @param upperPercent the new upper bound.
1774 */
1775 public void zoomDomainAxes(double x, double y, double lowerPercent,
1776 double upperPercent) {
1777 // TODO: to be implemented
1778 }
1779
1780 /**
1781 * Multiplies the range on the range axis/axes by the specified factor.
1782 *
1783 * @param x the x-coordinate (in Java2D space).
1784 * @param y the y-coordinate (in Java2D space).
1785 * @param factor the zoom factor.
1786 */
1787 public void zoomRangeAxes(double x, double y, double factor) {
1788 // TODO: to be implemented
1789 }
1790
1791 /**
1792 * Zooms the range axes (not yet implemented).
1793 *
1794 * @param x the x-coordinate (in Java2D space).
1795 * @param y the y-coordinate (in Java2D space).
1796 * @param lowerPercent the new lower bound.
1797 * @param upperPercent the new upper bound.
1798 */
1799 public void zoomRangeAxes(double x, double y, double lowerPercent,
1800 double upperPercent) {
1801 // TODO: to be implemented
1802 }
1803
1804 /**
1805 * Returns <code>false</code>.
1806 *
1807 * @return A boolean.
1808 */
1809 public boolean isDomainZoomable() {
1810 return false;
1811 }
1812
1813 /**
1814 * Returns <code>false</code>.
1815 *
1816 * @return A boolean.
1817 */
1818 public boolean isRangeZoomable() {
1819 return false;
1820 }
1821
1822 /**
1823 * Extends plot cloning to this plot type
1824 * @see org.jfree.chart.plot.Plot#clone()
1825 */
1826 public Object clone() throws CloneNotSupportedException {
1827 ContourPlot clone = (ContourPlot) super.clone();
1828
1829 if (this.domainAxis != null) {
1830 clone.domainAxis = (ValueAxis) this.domainAxis.clone();
1831 clone.domainAxis.setPlot(clone);
1832 clone.domainAxis.addChangeListener(clone);
1833 }
1834 if (this.rangeAxis != null) {
1835 clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
1836 clone.rangeAxis.setPlot(clone);
1837 clone.rangeAxis.addChangeListener(clone);
1838 }
1839
1840 if (clone.dataset != null) {
1841 clone.dataset.addChangeListener(clone);
1842 }
1843
1844 if (this.colorBar != null) {
1845 clone.colorBar = (ColorBar) this.colorBar.clone();
1846 }
1847
1848 clone.domainMarkers = (List) ObjectUtilities.deepClone(
1849 this.domainMarkers);
1850 clone.rangeMarkers = (List) ObjectUtilities.deepClone(
1851 this.rangeMarkers);
1852 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
1853
1854 if (this.clipPath != null) {
1855 clone.clipPath = (ClipPath) this.clipPath.clone();
1856 }
1857
1858 return clone;
1859 }
1860
1861 }