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