1 /*
2 * Licensed under the GPL License. You may not use this file except in compliance with the License.
3 * You may obtain a copy of the License at
4 *
5 * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6 *
7 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8 * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9 * PURPOSE.
10 */
11
12 /* ===========================================================
13 * JFreeChart : a free chart library for the Java(tm) platform
14 * ===========================================================
15 *
16 * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors.
17 *
18 * Project Info: http://www.jfree.org/jfreechart/index.html
19 *
20 * This library is free software; you can redistribute it and/or modify it
21 * under the terms of the GNU Lesser General Public License as published by
22 * the Free Software Foundation; either version 2.1 of the License, or
23 * (at your option) any later version.
24 *
25 * This library is distributed in the hope that it will be useful, but
26 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
28 * License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
33 * USA.
34 *
35 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
36 * Other names may be trademarks of their respective owners.]
37 *
38 * ---------------------
39 * XYLine3DRenderer.java
40 * ---------------------
41 * (C) Copyright 2005-2008, by Object Refinery Limited.
42 *
43 * Original Author: Thomas Morgner;
44 * Contributor(s): David Gilbert (for Object Refinery Limited);
45 *
46 * Changes
47 * -------
48 * 14-Jan-2005 : Added standard header (DG);
49 * 01-May-2007 : Fixed equals() and serialization bugs (DG);
50 * 15-Sep-2019 : Copied from original Jfreechart as code obsoleted and necessary for visuals
51 * in Psi Probe without extra rework (JWL);
52 *
53 */
54 package psiprobe.jfreechart;
55
56 import java.awt.Color;
57 import java.awt.Graphics2D;
58 import java.awt.Paint;
59 import java.awt.Shape;
60 import java.io.IOException;
61 import java.io.ObjectInputStream;
62 import java.io.ObjectOutputStream;
63 import java.io.Serializable;
64
65 import org.jfree.chart.event.RendererChangeEvent;
66 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
67 import org.jfree.chart.util.PaintUtils;
68 import org.jfree.chart.util.SerialUtils;
69
70 /**
71 * A XYLineAndShapeRenderer that adds a shadow line to the graph to emulate a 3D-effect.
72 */
73 public class XYLine3DRenderer extends XYLineAndShapeRenderer implements Effect3D, Serializable {
74
75 /** For serialization. */
76 private static final long serialVersionUID = 588933208243446087L;
77
78 /** The default x-offset for the 3D effect. */
79 public static final double DEFAULT_X_OFFSET = 12.0;
80
81 /** The default y-offset for the 3D effect. */
82 public static final double DEFAULT_Y_OFFSET = 8.0;
83
84 /** The default wall paint. */
85 public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
86
87 /** The size of x-offset for the 3D effect. */
88 private double xOffset;
89
90 /** The size of y-offset for the 3D effect. */
91 private double yOffset;
92
93 /** The paint used to shade the left and lower 3D wall. */
94 private transient Paint wallPaint;
95
96 /**
97 * Creates a new renderer.
98 */
99 public XYLine3DRenderer() {
100 this.wallPaint = DEFAULT_WALL_PAINT;
101 this.xOffset = DEFAULT_X_OFFSET;
102 this.yOffset = DEFAULT_Y_OFFSET;
103 }
104
105 /**
106 * Returns the x-offset for the 3D effect.
107 *
108 * @return The 3D effect.
109 */
110 @Override
111 public double getXOffset() {
112 return this.xOffset;
113 }
114
115 /**
116 * Returns the y-offset for the 3D effect.
117 *
118 * @return The 3D effect.
119 */
120 @Override
121 public double getYOffset() {
122 return this.yOffset;
123 }
124
125 /**
126 * Sets the x-offset and sends a {@link RendererChangeEvent} to all registered listeners.
127 *
128 * @param xOffset the x-offset.
129 */
130 public void setXOffset(double xOffset) {
131 this.xOffset = xOffset;
132 fireChangeEvent();
133 }
134
135 /**
136 * Sets the y-offset and sends a {@link RendererChangeEvent} to all registered listeners.
137 *
138 * @param yOffset the y-offset.
139 */
140 public void setYOffset(double yOffset) {
141 this.yOffset = yOffset;
142 fireChangeEvent();
143 }
144
145 /**
146 * Returns the paint used to highlight the left and bottom wall in the plot background.
147 *
148 * @return The paint.
149 */
150 public Paint getWallPaint() {
151 return this.wallPaint;
152 }
153
154 /**
155 * Sets the paint used to hightlight the left and bottom walls in the plot background and sends a
156 * {@link RendererChangeEvent} to all registered listeners.
157 *
158 * @param paint the paint.
159 */
160 public void setWallPaint(Paint paint) {
161 this.wallPaint = paint;
162 fireChangeEvent();
163 }
164
165 /**
166 * Returns the number of passes through the data that the renderer requires in order to draw the
167 * chart. Most charts will require a single pass, but some require two passes.
168 *
169 * @return The pass count.
170 */
171 @Override
172 public int getPassCount() {
173 return 3;
174 }
175
176 /**
177 * Returns {@code true} if the specified pass involves drawing lines.
178 *
179 * @param pass the pass.
180 *
181 * @return A boolean.
182 */
183 @Override
184 protected boolean isLinePass(int pass) {
185 return pass == 0 || pass == 1;
186 }
187
188 /**
189 * Returns {@code true} if the specified pass involves drawing items.
190 *
191 * @param pass the pass.
192 *
193 * @return A boolean.
194 */
195 @Override
196 protected boolean isItemPass(int pass) {
197 return pass == 2;
198 }
199
200 /**
201 * Returns {@code true} if the specified pass involves drawing shadows.
202 *
203 * @param pass the pass.
204 *
205 * @return A boolean.
206 */
207 protected boolean isShadowPass(int pass) {
208 return pass == 0;
209 }
210
211 /**
212 * Overrides the method in the subclass to draw a shadow in the first pass.
213 *
214 * @param g2 the graphics device.
215 * @param pass the pass.
216 * @param series the series index (zero-based).
217 * @param item the item index (zero-based).
218 * @param shape the shape.
219 */
220 @Override
221 protected void drawFirstPassShape(Graphics2D g2, int pass, int series, int item, Shape shape) {
222 if (isShadowPass(pass)) {
223 if (getWallPaint() != null) {
224 g2.setStroke(getItemStroke(series, item));
225 g2.setPaint(getWallPaint());
226 g2.translate(getXOffset(), getYOffset());
227 g2.draw(shape);
228 g2.translate(-getXOffset(), -getYOffset());
229 }
230 } else {
231 // now draw the real shape
232 super.drawFirstPassShape(g2, pass, series, item, shape);
233 }
234 }
235
236 /**
237 * Tests this renderer for equality with an arbitrary object.
238 *
239 * @param obj the object ({@code null} permitted).
240 *
241 * @return A boolean.
242 */
243 @Override
244 public boolean equals(Object obj) {
245 if (obj == this) {
246 return true;
247 }
248 if (!(obj instanceof XYLine3DRenderer that)) {
249 return false;
250 }
251 if (this.xOffset != that.xOffset || this.yOffset != that.yOffset
252 || !PaintUtils.equal(this.wallPaint, that.wallPaint)) {
253 return false;
254 }
255 return super.equals(obj);
256 }
257
258 /**
259 * Hash code.
260 *
261 * @return the int
262 */
263 @Override
264 public int hashCode() {
265 int result = super.hashCode();
266 long temp;
267 temp = Double.doubleToLongBits(xOffset);
268 result = 31 * result + (int) (temp ^ (temp >>> 32));
269 temp = Double.doubleToLongBits(yOffset);
270 result = 31 * result + (int) (temp ^ (temp >>> 32));
271 result = 31 * result + (wallPaint != null ? wallPaint.hashCode() : 0);
272 return result;
273 }
274
275 /**
276 * Provides serialization support.
277 *
278 * @param stream the input stream.
279 *
280 * @throws IOException if there is an I/O error.
281 * @throws ClassNotFoundException if there is a classpath problem.
282 */
283 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
284 stream.defaultReadObject();
285 this.wallPaint = SerialUtils.readPaint(stream);
286 }
287
288 /**
289 * Provides serialization support.
290 *
291 * @param stream the output stream.
292 *
293 * @throws IOException if there is an I/O error.
294 */
295 private void writeObject(ObjectOutputStream stream) throws IOException {
296 stream.defaultWriteObject();
297 SerialUtils.writePaint(this.wallPaint, stream);
298 }
299
300 }