001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved. 006 * 007 * http://www.object-refinery.com/orsoncharts/index.html 008 * 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as published by 011 * the Free Software Foundation, either version 3 of the License, or 012 * (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with this program. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 023 * Other names may be trademarks of their respective owners.] 024 * 025 * If you do not wish to be bound by the terms of the GPL, an alternative 026 * commercial license can be purchased. For details, please see visit the 027 * Orson Charts home page: 028 * 029 * http://www.object-refinery.com/orsoncharts/index.html 030 * 031 */ 032 033package com.orsoncharts.plot; 034 035import java.awt.BasicStroke; 036import java.awt.Color; 037import java.awt.Paint; 038import java.awt.Stroke; 039import java.io.Serializable; 040import java.io.IOException; 041import java.io.ObjectInputStream; 042import java.io.ObjectOutputStream; 043import java.util.ArrayList; 044import java.util.List; 045 046import com.orsoncharts.ChartElementVisitor; 047import com.orsoncharts.axis.Axis3DChangeEvent; 048import com.orsoncharts.axis.Axis3DChangeListener; 049import com.orsoncharts.axis.ValueAxis3D; 050import com.orsoncharts.data.Dataset3DChangeEvent; 051import com.orsoncharts.data.Dataset3DChangeListener; 052import com.orsoncharts.data.ItemKey; 053import com.orsoncharts.data.xyz.XYZDataset; 054import com.orsoncharts.data.xyz.XYZItemKey; 055import com.orsoncharts.renderer.xyz.XYZRenderer; 056import com.orsoncharts.util.ArgChecks; 057import com.orsoncharts.graphics3d.Dimension3D; 058import com.orsoncharts.graphics3d.World; 059import com.orsoncharts.label.StandardXYZLabelGenerator; 060import com.orsoncharts.label.StandardXYZItemLabelGenerator; 061import com.orsoncharts.label.XYZLabelGenerator; 062import com.orsoncharts.label.XYZItemLabelGenerator; 063import com.orsoncharts.legend.LegendItemInfo; 064import com.orsoncharts.legend.StandardLegendItemInfo; 065import com.orsoncharts.renderer.ComposeType; 066import com.orsoncharts.renderer.Renderer3DChangeEvent; 067import com.orsoncharts.renderer.Renderer3DChangeListener; 068import com.orsoncharts.util.ObjectUtils; 069import com.orsoncharts.util.SerialUtils; 070 071/** 072 * A 3D plot with three numerical axes that displays data from an 073 * {@link XYZDataset}. 074 * <br><br> 075 * NOTE: This class is serializable, but the serialization format is subject 076 * to change in future releases and should not be relied upon for persisting 077 * instances of this class. 078 */ 079@SuppressWarnings("serial") 080public class XYZPlot extends AbstractPlot3D implements Dataset3DChangeListener, 081 Axis3DChangeListener, Renderer3DChangeListener, Serializable { 082 083 private static Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, 084 BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f, 085 new float[] { 3f, 3f }, 0f); 086 087 /** The dataset. */ 088 private XYZDataset dataset; 089 090 /** The renderer. */ 091 private XYZRenderer renderer; 092 093 /** The x-axis. */ 094 private ValueAxis3D xAxis; 095 096 /** The y-axis. */ 097 private ValueAxis3D yAxis; 098 099 /** The z-axis. */ 100 private ValueAxis3D zAxis; 101 102 /** Are gridlines visible for the x-axis? */ 103 private boolean gridlinesVisibleX; 104 105 /** The paint for the x-axis gridlines. */ 106 private transient Paint gridlinePaintX; 107 108 /** The stroke for the x-axis gridlines. */ 109 private transient Stroke gridlineStrokeX; 110 111 /** Are gridlines visible for the y-axis? */ 112 private boolean gridlinesVisibleY; 113 114 /** The paint for the y-axis gridlines. */ 115 private transient Paint gridlinePaintY; 116 117 /** The stroke for the y-axis gridlines. */ 118 private transient Stroke gridlineStrokeY; 119 120 /** Are gridlines visible for the z-axis? */ 121 private boolean gridlinesVisibleZ; 122 123 /** The paint for the z-axis gridlines. */ 124 private transient Paint gridlinePaintZ; 125 126 /** The stroke for the z-axis gridlines. */ 127 private transient Stroke gridlineStrokeZ; 128 129 /** The legend label generator. */ 130 private XYZLabelGenerator legendLabelGenerator; 131 132 /** The tool tip generator (if null there will be no tooltips). */ 133 private XYZItemLabelGenerator toolTipGenerator; 134 135 /** 136 * Creates a new plot with the specified axes. 137 * 138 * @param dataset the dataset ({@code null} not permitted). 139 * @param renderer the renderer ({@code null} not permitted). 140 * @param xAxis the x-axis ({@code null} not permitted). 141 * @param yAxis the y-axis ({@code null} not permitted). 142 * @param zAxis the z-axis ({@code null} not permitted). 143 */ 144 public XYZPlot(XYZDataset dataset, XYZRenderer renderer, ValueAxis3D xAxis, 145 ValueAxis3D yAxis, ValueAxis3D zAxis) { 146 ArgChecks.nullNotPermitted(dataset, "dataset"); 147 ArgChecks.nullNotPermitted(renderer, "renderer"); 148 ArgChecks.nullNotPermitted(xAxis, "xAxis"); 149 ArgChecks.nullNotPermitted(yAxis, "yAxis"); 150 ArgChecks.nullNotPermitted(zAxis, "zAxis"); 151 this.dimensions = new Dimension3D(10, 10, 10); 152 this.dataset = dataset; 153 this.dataset.addChangeListener(this); 154 this.renderer = renderer; 155 this.renderer.setPlot(this); 156 this.renderer.addChangeListener(this); 157 this.xAxis = xAxis; 158 this.xAxis.addChangeListener(this); 159 this.xAxis.configureAsXAxis(this); 160 this.zAxis = zAxis; 161 this.zAxis.addChangeListener(this); 162 this.zAxis.configureAsZAxis(this); 163 this.yAxis = yAxis; 164 this.yAxis.addChangeListener(this); 165 this.yAxis.configureAsYAxis(this); 166 this.gridlinesVisibleX = true; 167 this.gridlinePaintX = Color.WHITE; 168 this.gridlineStrokeX = DEFAULT_GRIDLINE_STROKE; 169 this.gridlinesVisibleY = true; 170 this.gridlinePaintY = Color.WHITE; 171 this.gridlineStrokeY = DEFAULT_GRIDLINE_STROKE; 172 this.gridlinesVisibleZ = true; 173 this.gridlinePaintZ = Color.WHITE; 174 this.gridlineStrokeZ = DEFAULT_GRIDLINE_STROKE; 175 this.legendLabelGenerator = new StandardXYZLabelGenerator(); 176 this.toolTipGenerator = new StandardXYZItemLabelGenerator(); 177 } 178 179 /** 180 * Sets the dimensions for the plot and notifies registered listeners that 181 * the plot dimensions have been changed. 182 * 183 * @param dim the new dimensions ({@code null} not permitted). 184 */ 185 public void setDimensions(Dimension3D dim) { 186 ArgChecks.nullNotPermitted(dim, "dim"); 187 this.dimensions = dim; 188 fireChangeEvent(true); 189 } 190 191 /** 192 * Returns the dataset for the plot. 193 * 194 * @return The dataset (never {@code null}). 195 */ 196 public XYZDataset getDataset() { 197 return this.dataset; 198 } 199 200 /** 201 * Sets the dataset and sends a change event notification to all registered 202 * listeners. 203 * 204 * @param dataset the new dataset ({@code null} not permitted). 205 */ 206 public void setDataset(XYZDataset dataset) { 207 ArgChecks.nullNotPermitted(dataset, "dataset"); 208 this.dataset.removeChangeListener(this); 209 this.dataset = dataset; 210 this.dataset.addChangeListener(this); 211 fireChangeEvent(true); 212 } 213 214 /** 215 * Returns the x-axis. 216 * 217 * @return The x-axis (never {@code null}). 218 */ 219 public ValueAxis3D getXAxis() { 220 return this.xAxis; 221 } 222 223 /** 224 * Sets the x-axis and sends a {@link Plot3DChangeEvent} to all registered 225 * listeners. 226 * 227 * @param xAxis the x-axis ({@code null} not permitted). 228 */ 229 public void setXAxis(ValueAxis3D xAxis) { 230 ArgChecks.nullNotPermitted(xAxis, "xAxis"); 231 this.xAxis.removeChangeListener(this); 232 xAxis.configureAsXAxis(this); 233 xAxis.addChangeListener(this); 234 this.xAxis = xAxis; 235 fireChangeEvent(true); 236 } 237 238 /** 239 * Returns the y-axis. 240 * 241 * @return The y-axis (never {@code null}). 242 */ 243 public ValueAxis3D getYAxis() { 244 return this.yAxis; 245 } 246 247 /** 248 * Sets the y-axis and sends a {@link Plot3DChangeEvent} to all registered 249 * listeners. 250 * 251 * @param yAxis the y-axis ({@code null} not permitted). 252 */ 253 public void setYAxis(ValueAxis3D yAxis) { 254 ArgChecks.nullNotPermitted(yAxis, "yAxis"); 255 this.yAxis.removeChangeListener(this); 256 yAxis.configureAsYAxis(this); 257 yAxis.addChangeListener(this); 258 this.yAxis = yAxis; 259 fireChangeEvent(true); 260 } 261 262 /** 263 * Returns the z-axis. 264 * 265 * @return The z-axis (never {@code null}). 266 */ 267 public ValueAxis3D getZAxis() { 268 return this.zAxis; 269 } 270 271 /** 272 * Sets the z-axis and sends a {@link Plot3DChangeEvent} to all registered 273 * listeners. 274 * 275 * @param zAxis the z-axis ({@code null} not permitted). 276 */ 277 public void setZAxis(ValueAxis3D zAxis) { 278 ArgChecks.nullNotPermitted(zAxis, "zAxis"); 279 this.zAxis.removeChangeListener(this); 280 zAxis.configureAsZAxis(this); 281 zAxis.addChangeListener(this); 282 this.zAxis = zAxis; 283 fireChangeEvent(true); 284 } 285 286 /** 287 * Returns the renderer for the plot. 288 * 289 * @return The renderer (possibly {@code null}). 290 */ 291 public XYZRenderer getRenderer() { 292 return this.renderer; 293 } 294 295 /** 296 * Sets the renderer for the plot and sends a {@link Plot3DChangeEvent} 297 * to all registered listeners. 298 * 299 * @param renderer the renderer ({@code null} not permitted). 300 */ 301 public void setRenderer(XYZRenderer renderer) { 302 this.renderer.setPlot(null); 303 this.renderer.removeChangeListener(this); 304 this.renderer = renderer; 305 this.renderer.setPlot(this); 306 this.renderer.addChangeListener(this); 307 fireChangeEvent(true); 308 } 309 310 /** 311 * Returns the flag that controls whether or not gridlines are shown for 312 * the x-axis. 313 * 314 * @return A boolean. 315 */ 316 public boolean isGridlinesVisibleX() { 317 return this.gridlinesVisibleX; 318 } 319 320 /** 321 * Sets the flag that controls whether or not gridlines are shown for the 322 * x-axis and sends a {@link Plot3DChangeEvent} to all registered 323 * listeners. 324 * 325 * @param visible the new flag value. 326 */ 327 public void setGridlinesVisibleX(boolean visible) { 328 this.gridlinesVisibleX = visible; 329 fireChangeEvent(false); 330 } 331 332 /** 333 * Returns the paint used to draw the gridlines for the x-axis. 334 * 335 * @return The paint ({@code null} not permitted). 336 */ 337 public Paint getGridlinePaintX() { 338 return this.gridlinePaintX; 339 } 340 341 /** 342 * Sets the paint used to draw the gridlines for the x-axis, and sends 343 * a {@link Plot3DChangeEvent} to all registered listeners. 344 * 345 * @param paint the paint ({@code null} not permitted). 346 */ 347 public void setGridlinePaintX(Paint paint) { 348 ArgChecks.nullNotPermitted(paint, "paint"); 349 this.gridlinePaintX = paint; 350 fireChangeEvent(false); 351 } 352 353 /** 354 * Returns the stroke used to draw the gridlines for the x-axis. 355 * 356 * @return The stroke ({@code null} not permitted). 357 */ 358 public Stroke getGridlineStrokeX() { 359 return this.gridlineStrokeX; 360 } 361 362 /** 363 * Sets the stroke used to draw the gridlines for the x-axis, and sends 364 * a {@link Plot3DChangeEvent} to all registered listeners. 365 * 366 * @param stroke the stroke ({@code null} not permitted). 367 */ 368 public void setGridlineStrokeX(Stroke stroke) { 369 ArgChecks.nullNotPermitted(stroke, "stroke"); 370 this.gridlineStrokeX = stroke; 371 fireChangeEvent(false); 372 } 373 374 /** 375 * Returns the flag that controls whether or not gridlines are shown for 376 * the y-axis. 377 * 378 * @return A boolean. 379 */ 380 public boolean isGridlinesVisibleY() { 381 return this.gridlinesVisibleY; 382 } 383 384 /** 385 * Sets the flag that controls whether or not gridlines are shown for the 386 * y-axis and sends a {@link Plot3DChangeEvent} to all registered 387 * listeners. 388 * 389 * @param visible the new flag value. 390 */ 391 public void setGridlinesVisibleY(boolean visible) { 392 this.gridlinesVisibleY = visible; 393 fireChangeEvent(false); 394 } 395 396 /** 397 * Returns the paint used to draw the gridlines for the y-axis. 398 * 399 * @return The paint ({@code null} not permitted). 400 */ 401 public Paint getGridlinePaintY() { 402 return this.gridlinePaintY; 403 } 404 405 /** 406 * Sets the paint used to draw the gridlines for the y-axis, and sends 407 * a {@link Plot3DChangeEvent} to all registered listeners. 408 * 409 * @param paint the paint ({@code null} not permitted). 410 */ 411 public void setGridlinePaintY(Paint paint) { 412 ArgChecks.nullNotPermitted(paint, "paint"); 413 this.gridlinePaintY = paint; 414 fireChangeEvent(false); 415 } 416 417 /** 418 * Returns the stroke used to draw the gridlines for the y-axis. 419 * 420 * @return The stroke ({@code null} not permitted). 421 */ 422 public Stroke getGridlineStrokeY() { 423 return this.gridlineStrokeY; 424 } 425 426 /** 427 * Sets the stroke used to draw the gridlines for the y-axis, and sends 428 * a {@link Plot3DChangeEvent} to all registered listeners. 429 * 430 * @param stroke the stroke ({@code null} not permitted). 431 */ 432 public void setGridlineStrokeY(Stroke stroke) { 433 ArgChecks.nullNotPermitted(stroke, "stroke"); 434 this.gridlineStrokeY = stroke; 435 fireChangeEvent(false); 436 } 437 438 /** 439 * Returns the flag that controls whether or not gridlines are shown for 440 * the z-axis. 441 * 442 * @return A boolean. 443 */ 444 public boolean isGridlinesVisibleZ() { 445 return this.gridlinesVisibleZ; 446 } 447 448 /** 449 * Sets the flag that controls whether or not gridlines are shown for the 450 * z-axis and sends a {@link Plot3DChangeEvent} to all registered 451 * listeners. 452 * 453 * @param visible the new flag value. 454 */ 455 public void setGridlinesVisibleZ(boolean visible) { 456 this.gridlinesVisibleZ = visible; 457 fireChangeEvent(false); 458 } 459 460 /** 461 * Returns the paint used to draw the gridlines for the z-axis. 462 * 463 * @return The paint ({@code null} not permitted). 464 */ 465 public Paint getGridlinePaintZ() { 466 return this.gridlinePaintZ; 467 } 468 469 /** 470 * Sets the paint used to draw the gridlines for the z-axis, and sends 471 * a {@link Plot3DChangeEvent} to all registered listeners. 472 * 473 * @param paint the paint ({@code null} not permitted). 474 */ 475 public void setGridlinePaintZ(Paint paint) { 476 ArgChecks.nullNotPermitted(paint, "paint"); 477 this.gridlinePaintZ = paint; 478 fireChangeEvent(false); 479 } 480 481 /** 482 * Returns the stroke used to draw the gridlines for the z-axis. 483 * 484 * @return The stroke ({@code null} not permitted). 485 */ 486 public Stroke getGridlineStrokeZ() { 487 return this.gridlineStrokeZ; 488 } 489 490 /** 491 * Sets the stroke used to draw the gridlines for the z-axis, and sends 492 * a {@link Plot3DChangeEvent} to all registered listeners. 493 * 494 * @param stroke the stroke ({@code null} not permitted). 495 */ 496 public void setGridlineStrokeZ(Stroke stroke) { 497 ArgChecks.nullNotPermitted(stroke, "stroke"); 498 this.gridlineStrokeZ = stroke; 499 fireChangeEvent(false); 500 } 501 502 /** 503 * Returns the legend label generator. The default value is a default 504 * instance of {@link StandardXYZLabelGenerator}. 505 * 506 * @return The legend label generator (never {@code null}). 507 * 508 * @since 1.2 509 */ 510 public XYZLabelGenerator getLegendLabelGenerator() { 511 return this.legendLabelGenerator; 512 } 513 514 /** 515 * Sets the legend label generator and sends a {@link Plot3DChangeEvent} 516 * to all registered listeners. 517 * 518 * @param generator the generator ({@code null} not permitted). 519 * 520 * @since 1.2 521 */ 522 public void setLegendLabelGenerator(XYZLabelGenerator generator) { 523 ArgChecks.nullNotPermitted(generator, "generator"); 524 this.legendLabelGenerator = generator; 525 fireChangeEvent(false); 526 } 527 528 /** 529 * Returns a list containing legend item info, typically one item for 530 * each series in the chart. This is intended for use in the construction 531 * of a chart legend. 532 * 533 * @return A list containing legend item info. 534 */ 535 @Override 536 @SuppressWarnings("unchecked") // we don't know the generic types of the dataset 537 public List<LegendItemInfo> getLegendInfo() { 538 List<LegendItemInfo> result = new ArrayList<LegendItemInfo>(); 539 List<Comparable<?>> keys = this.dataset.getSeriesKeys(); 540 for (Comparable key : keys) { 541 String label = this.legendLabelGenerator.generateSeriesLabel( 542 this.dataset, (Comparable) key); 543 int series = this.dataset.getSeriesIndex(key); 544 Color color = this.renderer.getColorSource().getLegendColor(series); 545 LegendItemInfo info = new StandardLegendItemInfo(key, label, color); 546 result.add(info); 547 } 548 return result; 549 } 550 551 /** 552 * Adds 3D objects representing the current data for the plot to the 553 * specified world. After the world has been populated (or constructed) in 554 * this way, it is ready for rendering. 555 * 556 * @param world the world ({@code null} not permitted). 557 * @param xOffset the x-offset. 558 * @param yOffset the y-offset. 559 * @param zOffset the z-offset. 560 */ 561 @Override 562 public void compose(World world, double xOffset, double yOffset, 563 double zOffset) { 564 if (this.renderer.getComposeType() == ComposeType.ALL) { 565 this.renderer.composeAll(this, world, this.dimensions, xOffset, 566 yOffset, zOffset); 567 } else if (this.renderer.getComposeType() == ComposeType.PER_ITEM) { 568 // for each data point in the dataset figure out if the composed 569 // shape intersects with the visible 570 // subset of the world, and if so add the object 571 int seriesCount = this.dataset.getSeriesCount(); 572 for (int series = 0; series < seriesCount; series++) { 573 int itemCount = this.dataset.getItemCount(series); 574 for (int item = 0; item < itemCount; item++) { 575 this.renderer.composeItem(this.dataset, series, item, world, 576 this.dimensions, xOffset, yOffset, zOffset); 577 } 578 } 579 } else { 580 // if we get here, someone changed the ComposeType enum 581 throw new IllegalStateException("ComposeType not expected: " 582 + this.renderer.getComposeType()); 583 } 584 } 585 586 @Override 587 public String generateToolTipText(ItemKey itemKey) { 588 if (!(itemKey instanceof XYZItemKey)) { 589 throw new IllegalArgumentException( 590 "The itemKey must be a XYZItemKey instance."); 591 } 592 if (this.toolTipGenerator == null) { 593 return null; 594 } 595 XYZItemKey k = (XYZItemKey) itemKey; 596 return this.toolTipGenerator.generateItemLabel(dataset, 597 k.getSeriesKey(), k.getItemIndex()); 598 } 599 600 /** 601 * Receives a visitor. This is a general purpose mechanism, but the main 602 * use is to apply chart style changes across all the elements of a 603 * chart. 604 * 605 * @param visitor the visitor ({@code null} not permitted). 606 * 607 * @since 1.2 608 */ 609 @Override 610 public void receive(ChartElementVisitor visitor) { 611 this.xAxis.receive(visitor); 612 this.yAxis.receive(visitor); 613 this.zAxis.receive(visitor); 614 this.renderer.receive(visitor); 615 visitor.visit(this); 616 } 617 618 /** 619 * Tests this plot instance for equality with an arbitrary object. 620 * 621 * @param obj the object ({@code null} permitted). 622 * 623 * @return A boolean. 624 */ 625 @Override 626 public boolean equals(Object obj) { 627 if (obj == this) { 628 return true; 629 } 630 if (!(obj instanceof XYZPlot)) { 631 return false; 632 } 633 XYZPlot that = (XYZPlot) obj; 634 if (!this.dimensions.equals(that.dimensions)) { 635 return false; 636 } 637 if (this.gridlinesVisibleX != that.gridlinesVisibleX) { 638 return false; 639 } 640 if (this.gridlinesVisibleY != that.gridlinesVisibleY) { 641 return false; 642 } 643 if (this.gridlinesVisibleZ != that.gridlinesVisibleZ) { 644 return false; 645 } 646 if (!ObjectUtils.equalsPaint(this.gridlinePaintX, 647 that.gridlinePaintX)) { 648 return false; 649 } 650 if (!ObjectUtils.equalsPaint(this.gridlinePaintY, 651 that.gridlinePaintY)) { 652 return false; 653 } 654 if (!ObjectUtils.equalsPaint(this.gridlinePaintZ, 655 that.gridlinePaintZ)) { 656 return false; 657 } 658 if (!this.gridlineStrokeX.equals(that.gridlineStrokeX)) { 659 return false; 660 } 661 if (!this.gridlineStrokeY.equals(that.gridlineStrokeY)) { 662 return false; 663 } 664 if (!this.gridlineStrokeZ.equals(that.gridlineStrokeZ)) { 665 return false; 666 } 667 if (!this.legendLabelGenerator.equals(that.legendLabelGenerator)) { 668 return false; 669 } 670 return super.equals(obj); 671 } 672 673 /** 674 * Receives notification that one of the plot's axes has changed, and 675 * responds by passing on a {@link Plot3DChangeEvent} to the plot's 676 * registered listeners (with the default set-up, this notifies the 677 * chart). 678 * 679 * @param event the event. 680 */ 681 @Override 682 public void axisChanged(Axis3DChangeEvent event) { 683 this.yAxis.configureAsYAxis(this); 684 fireChangeEvent(event.requiresWorldUpdate()); 685 } 686 687 /** 688 * Receives notification that the plot's renderer has changed, and 689 * responds by passing on a {@link Plot3DChangeEvent} to the plot's 690 * registered listeners (with the default set-up, this notifies the 691 * chart). 692 * 693 * @param event the event. 694 */ 695 @Override 696 public void rendererChanged(Renderer3DChangeEvent event) { 697 fireChangeEvent(event.requiresWorldUpdate()); 698 } 699 700 /** 701 * Receives notification that the plot's dataset has changed, and 702 * responds by passing on a {@link Plot3DChangeEvent} to the plot's 703 * registered listeners (with the default set-up, this notifies the 704 * chart). 705 * 706 * @param event the event. 707 */ 708 @Override 709 public void datasetChanged(Dataset3DChangeEvent event) { 710 this.xAxis.configureAsXAxis(this); 711 this.yAxis.configureAsYAxis(this); 712 this.zAxis.configureAsZAxis(this); 713 super.datasetChanged(event); 714 } 715 716 /** 717 * Provides serialization support. 718 * 719 * @param stream the output stream. 720 * 721 * @throws IOException if there is an I/O error. 722 */ 723 private void writeObject(ObjectOutputStream stream) throws IOException { 724 stream.defaultWriteObject(); 725 SerialUtils.writePaint(this.gridlinePaintX, stream); 726 SerialUtils.writePaint(this.gridlinePaintY, stream); 727 SerialUtils.writePaint(this.gridlinePaintZ, stream); 728 SerialUtils.writeStroke(this.gridlineStrokeX, stream); 729 SerialUtils.writeStroke(this.gridlineStrokeY, stream); 730 SerialUtils.writeStroke(this.gridlineStrokeZ, stream); 731 732 } 733 734 /** 735 * Provides serialization support. 736 * 737 * @param stream the input stream. 738 * 739 * @throws IOException if there is an I/O error. 740 * @throws ClassNotFoundException if there is a classpath problem. 741 */ 742 private void readObject(ObjectInputStream stream) 743 throws IOException, ClassNotFoundException { 744 stream.defaultReadObject(); 745 this.gridlinePaintX = SerialUtils.readPaint(stream); 746 this.gridlinePaintY = SerialUtils.readPaint(stream); 747 this.gridlinePaintZ = SerialUtils.readPaint(stream); 748 this.gridlineStrokeX = SerialUtils.readStroke(stream); 749 this.gridlineStrokeY = SerialUtils.readStroke(stream); 750 this.gridlineStrokeZ = SerialUtils.readStroke(stream); 751 } 752 753}