001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * FastScatterPlot.java 029 * -------------------- 030 * (C) Copyright 2002-2005, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Arnaud Lelievre; 034 * 035 * $Id: FastScatterPlot.java,v 1.11.2.3 2005/10/25 20:52:08 mungady Exp $ 036 * 037 * Changes (from 29-Oct-2002) 038 * -------------------------- 039 * 29-Oct-2002 : Added standard header (DG); 040 * 07-Nov-2002 : Fixed errors reported by Checkstyle (DG); 041 * 26-Mar-2003 : Implemented Serializable (DG); 042 * 19-Aug-2003 : Implemented Cloneable (DG); 043 * 08-Sep-2003 : Added internationalization via use of properties 044 * resourceBundle (RFE 690236) (AL); 045 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 046 * 12-Nov-2003 : Implemented zooming (DG); 047 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 048 * 26-Jan-2004 : Added domain and range grid lines (DG); 049 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 050 * 29-Sep-2004 : Removed hard-coded color (DG); 051 * 04-Oct-2004 : Reworked equals() method and renamed ArrayUtils 052 * --> ArrayUtilities (DG); 053 * 12-Nov-2004 : Implemented the new Zoomable interface (DG); 054 * 05-May-2005 : Updated draw() method parameters (DG); 055 * 16-Jun-2005 : Added get/setData() methods (DG); 056 * 057 */ 058 059 package org.jfree.chart.plot; 060 061 import java.awt.AlphaComposite; 062 import java.awt.BasicStroke; 063 import java.awt.Color; 064 import java.awt.Composite; 065 import java.awt.Graphics2D; 066 import java.awt.Paint; 067 import java.awt.Shape; 068 import java.awt.Stroke; 069 import java.awt.geom.Line2D; 070 import java.awt.geom.Point2D; 071 import java.awt.geom.Rectangle2D; 072 import java.io.IOException; 073 import java.io.ObjectInputStream; 074 import java.io.ObjectOutputStream; 075 import java.io.Serializable; 076 import java.util.Iterator; 077 import java.util.List; 078 import java.util.ResourceBundle; 079 080 import org.jfree.chart.axis.AxisSpace; 081 import org.jfree.chart.axis.AxisState; 082 import org.jfree.chart.axis.ValueAxis; 083 import org.jfree.chart.axis.ValueTick; 084 import org.jfree.chart.event.PlotChangeEvent; 085 import org.jfree.data.Range; 086 import org.jfree.io.SerialUtilities; 087 import org.jfree.ui.RectangleEdge; 088 import org.jfree.ui.RectangleInsets; 089 import org.jfree.util.ArrayUtilities; 090 import org.jfree.util.ObjectUtilities; 091 import org.jfree.util.PaintUtilities; 092 093 /** 094 * A fast scatter plot. 095 */ 096 public class FastScatterPlot extends Plot implements ValueAxisPlot, 097 Zoomable, 098 Cloneable, Serializable { 099 100 /** For serialization. */ 101 private static final long serialVersionUID = 7871545897358563521L; 102 103 /** The default grid line stroke. */ 104 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, 105 BasicStroke.CAP_BUTT, 106 BasicStroke.JOIN_BEVEL, 107 0.0f, 108 new float[] {2.0f, 2.0f}, 109 0.0f); 110 111 /** The default grid line paint. */ 112 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray; 113 114 /** The data. */ 115 private float[][] data; 116 117 /** The x data range. */ 118 private Range xDataRange; 119 120 /** The y data range. */ 121 private Range yDataRange; 122 123 /** The domain axis (used for the x-values). */ 124 private ValueAxis domainAxis; 125 126 /** The range axis (used for the y-values). */ 127 private ValueAxis rangeAxis; 128 129 /** The paint used to plot data points. */ 130 private transient Paint paint; 131 132 /** A flag that controls whether the domain grid-lines are visible. */ 133 private boolean domainGridlinesVisible; 134 135 /** The stroke used to draw the domain grid-lines. */ 136 private transient Stroke domainGridlineStroke; 137 138 /** The paint used to draw the domain grid-lines. */ 139 private transient Paint domainGridlinePaint; 140 141 /** A flag that controls whether the range grid-lines are visible. */ 142 private boolean rangeGridlinesVisible; 143 144 /** The stroke used to draw the range grid-lines. */ 145 private transient Stroke rangeGridlineStroke; 146 147 /** The paint used to draw the range grid-lines. */ 148 private transient Paint rangeGridlinePaint; 149 150 /** The resourceBundle for the localization. */ 151 protected static ResourceBundle localizationResources = 152 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 153 154 /** 155 * Creates an empty plot. 156 */ 157 public FastScatterPlot() { 158 this(null, null, null); 159 } 160 161 /** 162 * Creates a new fast scatter plot. 163 * <P> 164 * The data is an array of x, y values: data[0][i] = x, data[1][i] = y. 165 * 166 * @param data the data. 167 * @param domainAxis the domain (x) axis. 168 * @param rangeAxis the range (y) axis. 169 */ 170 public FastScatterPlot(float[][] data, 171 ValueAxis domainAxis, ValueAxis rangeAxis) { 172 173 super(); 174 175 this.data = data; 176 this.xDataRange = calculateXDataRange(data); 177 this.yDataRange = calculateYDataRange(data); 178 this.domainAxis = domainAxis; 179 if (domainAxis != null) { 180 domainAxis.setPlot(this); 181 domainAxis.addChangeListener(this); 182 } 183 184 this.rangeAxis = rangeAxis; 185 if (rangeAxis != null) { 186 rangeAxis.setPlot(this); 187 rangeAxis.addChangeListener(this); 188 } 189 190 this.paint = Color.red; 191 192 this.domainGridlinesVisible = true; 193 this.domainGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT; 194 this.domainGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE; 195 196 this.rangeGridlinesVisible = true; 197 this.rangeGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT; 198 this.rangeGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE; 199 200 } 201 202 /** 203 * Returns a short string describing the plot type. 204 * 205 * @return A short string describing the plot type. 206 */ 207 public String getPlotType() { 208 return localizationResources.getString("Fast_Scatter_Plot"); 209 } 210 211 /** 212 * Returns the data array used by the plot. 213 * 214 * @return The data array (possibly <code>null</code>). 215 */ 216 public float[][] getData() { 217 return this.data; 218 } 219 220 /** 221 * Sets the data array used by the plot and sends a {@link PlotChangeEvent} 222 * to all registered listeners. 223 * 224 * @param data the data array (<code>null</code> permitted). 225 */ 226 public void setData(float[][] data) { 227 this.data = data; 228 notifyListeners(new PlotChangeEvent(this)); 229 } 230 231 /** 232 * Returns the orientation of the plot. 233 * 234 * @return The orientation (always {@link PlotOrientation#VERTICAL}). 235 */ 236 public PlotOrientation getOrientation() { 237 return PlotOrientation.VERTICAL; 238 } 239 240 /** 241 * Returns the domain axis for the plot. If the domain axis for this plot 242 * is null, then the method will return the parent plot's domain axis (if 243 * there is a parent plot). 244 * 245 * @return The domain axis. 246 */ 247 public ValueAxis getDomainAxis() { 248 return this.domainAxis; 249 } 250 251 /** 252 * Returns the range axis for the plot. If the range axis for this plot is 253 * null, then the method will return the parent plot's range axis (if 254 * there is a parent plot). 255 * 256 * @return The range axis. 257 */ 258 public ValueAxis getRangeAxis() { 259 return this.rangeAxis; 260 } 261 262 /** 263 * Returns the paint used to plot data points. 264 * 265 * @return The paint. 266 */ 267 public Paint getPaint() { 268 return this.paint; 269 } 270 271 /** 272 * Sets the color for the data points and sends a {@link PlotChangeEvent} 273 * to all registered listeners. 274 * 275 * @param paint the paint (<code>null</code> not permitted). 276 */ 277 public void setPaint(Paint paint) { 278 if (paint == null) { 279 throw new IllegalArgumentException("Null 'paint' argument."); 280 } 281 this.paint = paint; 282 notifyListeners(new PlotChangeEvent(this)); 283 } 284 285 /** 286 * Returns <code>true</code> if the domain gridlines are visible, and 287 * <code>false<code> otherwise. 288 * 289 * @return <code>true</code> or <code>false</code>. 290 */ 291 public boolean isDomainGridlinesVisible() { 292 return this.domainGridlinesVisible; 293 } 294 295 /** 296 * Sets the flag that controls whether or not the domain grid-lines are 297 * visible. If the flag value is changed, a {@link PlotChangeEvent} is 298 * sent to all registered listeners. 299 * 300 * @param visible the new value of the flag. 301 */ 302 public void setDomainGridlinesVisible(boolean visible) { 303 if (this.domainGridlinesVisible != visible) { 304 this.domainGridlinesVisible = visible; 305 notifyListeners(new PlotChangeEvent(this)); 306 } 307 } 308 309 /** 310 * Returns the stroke for the grid-lines (if any) plotted against the 311 * domain axis. 312 * 313 * @return The stroke. 314 */ 315 public Stroke getDomainGridlineStroke() { 316 return this.domainGridlineStroke; 317 } 318 319 /** 320 * Sets the stroke for the grid lines plotted against the domain axis. 321 * <p> 322 * If you set this to <code>null</code>, no grid lines will be drawn. 323 * 324 * @param stroke the stroke (<code>null</code> permitted). 325 */ 326 public void setDomainGridlineStroke(Stroke stroke) { 327 this.domainGridlineStroke = stroke; 328 notifyListeners(new PlotChangeEvent(this)); 329 } 330 331 /** 332 * Returns the paint for the grid lines (if any) plotted against the domain 333 * axis. 334 * 335 * @return The paint. 336 */ 337 public Paint getDomainGridlinePaint() { 338 return this.domainGridlinePaint; 339 } 340 341 /** 342 * Sets the paint for the grid lines plotted against the domain axis. 343 * <p> 344 * If you set this to <code>null</code>, no grid lines will be drawn. 345 * 346 * @param paint the paint (<code>null</code> permitted). 347 */ 348 public void setDomainGridlinePaint(Paint paint) { 349 this.domainGridlinePaint = paint; 350 notifyListeners(new PlotChangeEvent(this)); 351 } 352 353 /** 354 * Returns <code>true</code> if the range axis grid is visible, and 355 * <code>false<code> otherwise. 356 * 357 * @return <code>true</code> or <code>false</code>. 358 */ 359 public boolean isRangeGridlinesVisible() { 360 return this.rangeGridlinesVisible; 361 } 362 363 /** 364 * Sets the flag that controls whether or not the range axis grid lines are 365 * visible. If the flag value is changed, a {@link PlotChangeEvent} is 366 * sent to all registered listeners. 367 * 368 * @param visible the new value of the flag. 369 */ 370 public void setRangeGridlinesVisible(boolean visible) { 371 if (this.rangeGridlinesVisible != visible) { 372 this.rangeGridlinesVisible = visible; 373 notifyListeners(new PlotChangeEvent(this)); 374 } 375 } 376 377 /** 378 * Returns the stroke for the grid lines (if any) plotted against the range 379 * axis. 380 * 381 * @return The stroke. 382 */ 383 public Stroke getRangeGridlineStroke() { 384 return this.rangeGridlineStroke; 385 } 386 387 /** 388 * Sets the stroke for the grid lines plotted against the range axis. 389 * <p> 390 * If you set this to <code>null</code>, no grid lines will be drawn. 391 * 392 * @param stroke the stroke (<code>null</code> permitted). 393 */ 394 public void setRangeGridlineStroke(Stroke stroke) { 395 this.rangeGridlineStroke = stroke; 396 notifyListeners(new PlotChangeEvent(this)); 397 } 398 399 /** 400 * Returns the paint for the grid lines (if any) plotted against the range 401 * axis. 402 * 403 * @return The paint. 404 */ 405 public Paint getRangeGridlinePaint() { 406 return this.rangeGridlinePaint; 407 } 408 409 /** 410 * Sets the paint for the grid lines plotted against the range axis. 411 * <p> 412 * If you set this to <code>null</code>, no grid lines will be drawn. 413 * 414 * @param paint the paint (<code>null</code> permitted). 415 */ 416 public void setRangeGridlinePaint(Paint paint) { 417 this.rangeGridlinePaint = paint; 418 notifyListeners(new PlotChangeEvent(this)); 419 } 420 421 /** 422 * Draws the fast scatter plot on a Java 2D graphics device (such as the 423 * screen or a printer). 424 * 425 * @param g2 the graphics device. 426 * @param area the area within which the plot (including axis labels) 427 * should be drawn. 428 * @param anchor the anchor point (<code>null</code> permitted). 429 * @param parentState the state from the parent plot, if there is one. 430 * @param info collects chart drawing information (<code>null</code> 431 * permitted). 432 */ 433 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 434 PlotState parentState, 435 PlotRenderingInfo info) { 436 437 // set up info collection... 438 if (info != null) { 439 info.setPlotArea(area); 440 } 441 442 // adjust the drawing area for plot insets (if any)... 443 RectangleInsets insets = getInsets(); 444 insets.trim(area); 445 446 AxisSpace space = new AxisSpace(); 447 space = this.domainAxis.reserveSpace( 448 g2, this, area, RectangleEdge.BOTTOM, space 449 ); 450 space = this.rangeAxis.reserveSpace( 451 g2, this, area, RectangleEdge.LEFT, space 452 ); 453 Rectangle2D dataArea = space.shrink(area, null); 454 455 if (info != null) { 456 info.setDataArea(dataArea); 457 } 458 459 // draw the plot background and axes... 460 drawBackground(g2, dataArea); 461 462 AxisState domainAxisState = null; 463 AxisState rangeAxisState = null; 464 if (this.domainAxis != null) { 465 domainAxisState = this.domainAxis.draw( 466 g2, dataArea.getMaxY(), area, dataArea, 467 RectangleEdge.BOTTOM, info 468 ); 469 } 470 if (this.rangeAxis != null) { 471 rangeAxisState = this.rangeAxis.draw( 472 g2, dataArea.getMinX(), area, dataArea, 473 RectangleEdge.LEFT, info 474 ); 475 } 476 drawDomainGridlines(g2, dataArea, domainAxisState.getTicks()); 477 drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); 478 479 Shape originalClip = g2.getClip(); 480 Composite originalComposite = g2.getComposite(); 481 482 g2.clip(dataArea); 483 g2.setComposite( 484 AlphaComposite.getInstance( 485 AlphaComposite.SRC_OVER, getForegroundAlpha() 486 ) 487 ); 488 489 render(g2, dataArea, info, null); 490 491 g2.setClip(originalClip); 492 g2.setComposite(originalComposite); 493 drawOutline(g2, dataArea); 494 495 } 496 497 /** 498 * Draws a representation of the data within the dataArea region. The 499 * <code>info</code> and <code>crosshairState</code> arguments may be 500 * <code>null</code>. 501 * 502 * @param g2 the graphics device. 503 * @param dataArea the region in which the data is to be drawn. 504 * @param info an optional object for collection dimension information. 505 * @param crosshairState collects crosshair information (<code>null</code> 506 * permitted). 507 */ 508 public void render(Graphics2D g2, Rectangle2D dataArea, 509 PlotRenderingInfo info, CrosshairState crosshairState) { 510 511 512 //long start = System.currentTimeMillis(); 513 //System.out.println("Start: " + start); 514 g2.setPaint(this.paint); 515 516 // if the axes use a linear scale, you can uncomment the code below and 517 // switch to the alternative transX/transY calculation inside the loop 518 // that follows - it is a little bit faster then. 519 // 520 // int xx = (int) dataArea.getMinX(); 521 // int ww = (int) dataArea.getWidth(); 522 // int yy = (int) dataArea.getMaxY(); 523 // int hh = (int) dataArea.getHeight(); 524 // double domainMin = this.domainAxis.getLowerBound(); 525 // double domainLength = this.domainAxis.getUpperBound() - domainMin; 526 // double rangeMin = this.rangeAxis.getLowerBound(); 527 // double rangeLength = this.rangeAxis.getUpperBound() - rangeMin; 528 529 if (this.data != null) { 530 for (int i = 0; i < this.data[0].length; i++) { 531 float x = this.data[0][i]; 532 float y = this.data[1][i]; 533 534 //int transX = (int) (xx + ww * (x - domainMin) / domainLength); 535 //int transY = (int) (yy - hh * (y - rangeMin) / rangeLength); 536 int transX = (int) this.domainAxis.valueToJava2D( 537 x, dataArea, RectangleEdge.BOTTOM 538 ); 539 int transY = (int) this.rangeAxis.valueToJava2D( 540 y, dataArea, RectangleEdge.LEFT 541 ); 542 g2.fillRect(transX, transY, 1, 1); 543 } 544 } 545 //long finish = System.currentTimeMillis(); 546 //System.out.println("Finish: " + finish); 547 //System.out.println("Time: " + (finish - start)); 548 549 } 550 551 /** 552 * Draws the gridlines for the plot, if they are visible. 553 * 554 * @param g2 the graphics device. 555 * @param dataArea the data area. 556 * @param ticks the ticks. 557 */ 558 protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea, 559 List ticks) { 560 561 // draw the domain grid lines, if any... 562 if (isDomainGridlinesVisible()) { 563 Stroke gridStroke = getDomainGridlineStroke(); 564 Paint gridPaint = getDomainGridlinePaint(); 565 if ((gridStroke != null) && (gridPaint != null)) { 566 Iterator iterator = ticks.iterator(); 567 while (iterator.hasNext()) { 568 ValueTick tick = (ValueTick) iterator.next(); 569 double v = this.domainAxis.valueToJava2D( 570 tick.getValue(), dataArea, RectangleEdge.BOTTOM 571 ); 572 Line2D line = new Line2D.Double( 573 v, dataArea.getMinY(), v, dataArea.getMaxY() 574 ); 575 g2.setPaint(gridPaint); 576 g2.setStroke(gridStroke); 577 g2.draw(line); 578 } 579 } 580 } 581 } 582 583 /** 584 * Draws the gridlines for the plot, if they are visible. 585 * 586 * @param g2 the graphics device. 587 * @param dataArea the data area. 588 * @param ticks the ticks. 589 */ 590 protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea, 591 List ticks) { 592 593 // draw the range grid lines, if any... 594 if (isRangeGridlinesVisible()) { 595 Stroke gridStroke = getRangeGridlineStroke(); 596 Paint gridPaint = getRangeGridlinePaint(); 597 if ((gridStroke != null) && (gridPaint != null)) { 598 Iterator iterator = ticks.iterator(); 599 while (iterator.hasNext()) { 600 ValueTick tick = (ValueTick) iterator.next(); 601 double v = this.rangeAxis.valueToJava2D( 602 tick.getValue(), dataArea, RectangleEdge.LEFT 603 ); 604 Line2D line = new Line2D.Double( 605 dataArea.getMinX(), v, dataArea.getMaxX(), v 606 ); 607 g2.setPaint(gridPaint); 608 g2.setStroke(gridStroke); 609 g2.draw(line); 610 } 611 } 612 } 613 614 } 615 616 /** 617 * Returns the range of data values to be plotted along the axis. 618 * 619 * @param axis the axis. 620 * 621 * @return The range. 622 */ 623 public Range getDataRange(ValueAxis axis) { 624 625 Range result = null; 626 if (axis == this.domainAxis) { 627 result = this.xDataRange; 628 } 629 else if (axis == this.rangeAxis) { 630 result = this.yDataRange; 631 } 632 return result; 633 } 634 635 /** 636 * Calculates the X data range. 637 * 638 * @param data the data. 639 * 640 * @return The range. 641 */ 642 private Range calculateXDataRange(float[][] data) { 643 644 Range result = null; 645 646 if (data != null) { 647 float lowest = Float.POSITIVE_INFINITY; 648 float highest = Float.NEGATIVE_INFINITY; 649 for (int i = 0; i < data[0].length; i++) { 650 float v = data[0][i]; 651 if (v < lowest) { 652 lowest = v; 653 } 654 if (v > highest) { 655 highest = v; 656 } 657 } 658 if (lowest <= highest) { 659 result = new Range(lowest, highest); 660 } 661 } 662 663 return result; 664 665 } 666 667 /** 668 * Calculates the Y data range. 669 * 670 * @param data the data. 671 * 672 * @return The range. 673 */ 674 private Range calculateYDataRange(float[][] data) { 675 676 Range result = null; 677 678 if (data != null) { 679 float lowest = Float.POSITIVE_INFINITY; 680 float highest = Float.NEGATIVE_INFINITY; 681 for (int i = 0; i < data[0].length; i++) { 682 float v = data[1][i]; 683 if (v < lowest) { 684 lowest = v; 685 } 686 if (v > highest) { 687 highest = v; 688 } 689 } 690 if (lowest <= highest) { 691 result = new Range(lowest, highest); 692 } 693 } 694 return result; 695 696 } 697 698 /** 699 * Multiplies the range on the domain axis/axes by the specified factor. 700 * 701 * @param factor the zoom factor. 702 * @param info the plot rendering info. 703 * @param source the source point. 704 */ 705 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 706 Point2D source) { 707 this.domainAxis.resizeRange(factor); 708 } 709 710 /** 711 * Zooms in on the domain axes. 712 * 713 * @param lowerPercent the new lower bound as a percentage of the current 714 * range. 715 * @param upperPercent the new upper bound as a percentage of the current 716 * range. 717 * @param info the plot rendering info. 718 * @param source the source point. 719 */ 720 public void zoomDomainAxes(double lowerPercent, double upperPercent, 721 PlotRenderingInfo info, Point2D source) { 722 this.domainAxis.zoomRange(lowerPercent, upperPercent); 723 } 724 725 /** 726 * Multiplies the range on the range axis/axes by the specified factor. 727 * 728 * @param factor the zoom factor. 729 * @param info the plot rendering info. 730 * @param source the source point. 731 */ 732 public void zoomRangeAxes(double factor, 733 PlotRenderingInfo info, Point2D source) { 734 this.rangeAxis.resizeRange(factor); 735 } 736 737 /** 738 * Zooms in on the range axes. 739 * 740 * @param lowerPercent the new lower bound as a percentage of the current 741 * range. 742 * @param upperPercent the new upper bound as a percentage of the current 743 * range. 744 * @param info the plot rendering info. 745 * @param source the source point. 746 */ 747 public void zoomRangeAxes(double lowerPercent, double upperPercent, 748 PlotRenderingInfo info, Point2D source) { 749 this.rangeAxis.zoomRange(lowerPercent, upperPercent); 750 } 751 752 /** 753 * Returns <code>true</code>. 754 * 755 * @return A boolean. 756 */ 757 public boolean isDomainZoomable() { 758 return true; 759 } 760 761 /** 762 * Returns <code>true</code>. 763 * 764 * @return A boolean. 765 */ 766 public boolean isRangeZoomable() { 767 return true; 768 } 769 770 /** 771 * Tests an object for equality with this instance. 772 * 773 * @param obj the object (<code>null</code> permitted). 774 * 775 * @return A boolean. 776 */ 777 public boolean equals(Object obj) { 778 if (obj == this) { 779 return true; 780 } 781 if (!super.equals(obj)) { 782 return false; 783 } 784 if (!(obj instanceof FastScatterPlot)) { 785 return false; 786 } 787 FastScatterPlot that = (FastScatterPlot) obj; 788 if (!ArrayUtilities.equal(this.data, that.data)) { 789 return false; 790 } 791 if (!ObjectUtilities.equal(this.domainAxis, that.domainAxis)) { 792 return false; 793 } 794 if (!ObjectUtilities.equal(this.rangeAxis, that.rangeAxis)) { 795 return false; 796 } 797 if (!PaintUtilities.equal(this.paint, that.paint)) { 798 return false; 799 } 800 if (this.domainGridlinesVisible != that.domainGridlinesVisible) { 801 return false; 802 } 803 if (!PaintUtilities.equal(this.domainGridlinePaint, 804 that.domainGridlinePaint)) { 805 return false; 806 } 807 if (!ObjectUtilities.equal(this.domainGridlineStroke, 808 that.domainGridlineStroke)) { 809 return false; 810 } 811 if (!this.rangeGridlinesVisible == that.rangeGridlinesVisible) { 812 return false; 813 } 814 if (!PaintUtilities.equal(this.rangeGridlinePaint, 815 that.rangeGridlinePaint)) { 816 return false; 817 } 818 if (!ObjectUtilities.equal(this.rangeGridlineStroke, 819 that.rangeGridlineStroke)) { 820 return false; 821 } 822 return true; 823 } 824 825 /** 826 * Returns a clone of the plot. 827 * 828 * @return A clone. 829 * 830 * @throws CloneNotSupportedException if some component of the plot does 831 * not support cloning. 832 */ 833 public Object clone() throws CloneNotSupportedException { 834 835 FastScatterPlot clone = (FastScatterPlot) super.clone(); 836 837 if (this.data != null) { 838 clone.data = ArrayUtilities.clone(this.data); 839 } 840 841 if (this.domainAxis != null) { 842 clone.domainAxis = (ValueAxis) this.domainAxis.clone(); 843 clone.domainAxis.setPlot(clone); 844 clone.domainAxis.addChangeListener(clone); 845 } 846 847 if (this.rangeAxis != null) { 848 clone.rangeAxis = (ValueAxis) this.rangeAxis.clone(); 849 clone.rangeAxis.setPlot(clone); 850 clone.rangeAxis.addChangeListener(clone); 851 } 852 853 return clone; 854 855 } 856 857 /** 858 * Provides serialization support. 859 * 860 * @param stream the output stream. 861 * 862 * @throws IOException if there is an I/O error. 863 */ 864 private void writeObject(ObjectOutputStream stream) throws IOException { 865 stream.defaultWriteObject(); 866 SerialUtilities.writePaint(this.paint, stream); 867 SerialUtilities.writeStroke(this.domainGridlineStroke, stream); 868 SerialUtilities.writePaint(this.domainGridlinePaint, stream); 869 SerialUtilities.writeStroke(this.rangeGridlineStroke, stream); 870 SerialUtilities.writePaint(this.rangeGridlinePaint, stream); 871 } 872 873 /** 874 * Provides serialization support. 875 * 876 * @param stream the input stream. 877 * 878 * @throws IOException if there is an I/O error. 879 * @throws ClassNotFoundException if there is a classpath problem. 880 */ 881 private void readObject(ObjectInputStream stream) 882 throws IOException, ClassNotFoundException { 883 stream.defaultReadObject(); 884 885 this.paint = SerialUtilities.readPaint(stream); 886 this.domainGridlineStroke = SerialUtilities.readStroke(stream); 887 this.domainGridlinePaint = SerialUtilities.readPaint(stream); 888 889 this.rangeGridlineStroke = SerialUtilities.readStroke(stream); 890 this.rangeGridlinePaint = SerialUtilities.readPaint(stream); 891 892 if (this.domainAxis != null) { 893 this.domainAxis.addChangeListener(this); 894 } 895 896 if (this.rangeAxis != null) { 897 this.rangeAxis.addChangeListener(this); 898 } 899 } 900 901 }