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.marker; 034 035import java.awt.Color; 036import java.awt.Font; 037import java.awt.Graphics2D; 038import java.awt.Stroke; 039import java.awt.geom.Line2D; 040import java.awt.geom.Path2D; 041import java.awt.geom.Point2D; 042import java.io.IOException; 043import java.io.ObjectInputStream; 044import java.io.ObjectOutputStream; 045import java.io.Serializable; 046 047import org.jfree.chart3d.axis.CategoryAxis3D; 048import org.jfree.chart3d.graphics2d.Anchor2D; 049import org.jfree.chart3d.internal.SerialUtils; 050import org.jfree.chart3d.internal.Args; 051import org.jfree.chart3d.internal.ObjectUtils; 052 053/** 054 * A marker for a category on a {@link CategoryAxis3D}. This marker could be 055 * used to highlight one selected category. 056 * <br><br> 057 * For an example, please refer to the demo {@code CategoryMarkerDemo1.java}. 058 * <br><br> 059 * NOTE: This class is serializable, but the serialization format is subject 060 * to change in future releases and should not be relied upon for persisting 061 * instances of this class. 062 * 063 * @since 1.2 064 */ 065@SuppressWarnings("serial") 066public class CategoryMarker extends AbstractMarker implements Serializable { 067 068 /** The category to mark. */ 069 Comparable<?> category; 070 071 /** 072 * The marker type (used to indicate whether the marker is represented by 073 * a line or a band). 074 */ 075 CategoryMarkerType type; 076 077 /** The label for the marker (optional). */ 078 private String label; 079 080 /** The font for the label. */ 081 private Font font; 082 083 /** The color for the label. */ 084 private Color labelColor; 085 086 /** The anchor for the label. */ 087 private Anchor2D labelAnchor; 088 089 /** The stroke for the marker line(s). */ 090 private transient Stroke lineStroke; 091 092 /** The color for the marker line. */ 093 private Color lineColor; 094 095 /** The fill color used when drawing a band for the marker. */ 096 private Color fillColor; 097 098 /** 099 * Creates a marker for the specified category. 100 * 101 * @param category the category key ({@code null} not permitted). 102 */ 103 public CategoryMarker(Comparable<?> category) { 104 super(); 105 Args.nullNotPermitted(category, "category"); 106 this.category = category; 107 this.type = CategoryMarkerType.BAND; 108 this.font = Marker.DEFAULT_MARKER_FONT; 109 this.labelColor = Marker.DEFAULT_LABEL_COLOR; 110 this.labelAnchor = Anchor2D.CENTER; 111 this.lineStroke = DEFAULT_LINE_STROKE; 112 this.lineColor = DEFAULT_LINE_COLOR; 113 this.fillColor = DEFAULT_FILL_COLOR; 114 } 115 116 /** 117 * Returns the category. 118 * 119 * @return The category (never {@code null}). 120 */ 121 public Comparable<?> getCategory() { 122 return this.category; 123 } 124 125 /** 126 * Sets the category for the marker and sends a change event to all 127 * registered listeners. 128 * 129 * @param category the new category ({@code null} not permitted). 130 */ 131 public void setCategory(Comparable<?> category) { 132 Args.nullNotPermitted(category, "category"); 133 this.category = category; 134 fireChangeEvent(); 135 } 136 137 /** 138 * Returns the marker type which determines whether the marker is drawn 139 * as a band (the default) or a line. 140 * 141 * @return The type (never {@code null}). 142 */ 143 public CategoryMarkerType getType() { 144 return this.type; 145 } 146 147 /** 148 * Sets the marker type and sends a change event to all registered 149 * listeners. 150 * 151 * @param type the type ({@code null} not permitted). 152 */ 153 public void setType(CategoryMarkerType type) { 154 Args.nullNotPermitted(type, "type"); 155 this.type = type; 156 fireChangeEvent(); 157 } 158 159 /** 160 * Returns the label for the marker (if this is {@code null} then no 161 * label is displayed). 162 * 163 * @return The label (possibly {@code null}). 164 */ 165 public String getLabel() { 166 return this.label; 167 } 168 169 /** 170 * Sets the label and sends a change event to all registered listeners. 171 * 172 * @param label the label ({@code null} permitted). 173 */ 174 public void setLabel(String label) { 175 this.label = label; 176 fireChangeEvent(); 177 } 178 179 /** 180 * Returns the font for the label. The default value is 181 * {@link Marker#DEFAULT_MARKER_FONT}. 182 * 183 * @return The font (never {@code null}). 184 */ 185 public Font getFont() { 186 return this.font; 187 } 188 189 /** 190 * Sets the font for the marker label and sends a change event to all 191 * registered listeners. 192 * 193 * @param font the font ({@code null} not permitted). 194 */ 195 public void setFont(Font font) { 196 Args.nullNotPermitted(font, "font"); 197 this.font = font; 198 fireChangeEvent(); 199 } 200 201 /** 202 * Returns the label color. The default value is 203 * {@link Marker#DEFAULT_LABEL_COLOR}. 204 * 205 * @return The label color (never {@code null}). 206 */ 207 public Color getLabelColor() { 208 return this.labelColor; 209 } 210 211 /** 212 * Sets the label color and sends a change event to all registered 213 * listeners. 214 * 215 * @param color the color ({@code null} not permitted). 216 */ 217 public void setLabelColor(Color color) { 218 Args.nullNotPermitted(color, "color"); 219 this.labelColor = color; 220 fireChangeEvent(); 221 } 222 223 /** 224 * Returns the anchor for the label. The default value is 225 * {@link Anchor2D#CENTER}. 226 * 227 * @return The anchor for the label. 228 */ 229 public Anchor2D getLabelAnchor() { 230 return this.labelAnchor; 231 } 232 233 /** 234 * Sets the anchor for the label and sends a change event to all registered 235 * listeners. 236 * 237 * @param anchor the anchor ({@code null} not permitted). 238 */ 239 public void setLabelAnchor(Anchor2D anchor) { 240 Args.nullNotPermitted(anchor, "anchor"); 241 this.labelAnchor = anchor; 242 fireChangeEvent(); 243 } 244 245 /** 246 * Returns the line color for the marker. 247 * 248 * @return The line color (never {@code null}). 249 */ 250 public Color getLineColor() { 251 return this.lineColor; 252 } 253 254 /** 255 * Sets the line color for the marker and sends a change event to all 256 * registered listeners. 257 * 258 * @param color the color ({@code null} not permitted). 259 */ 260 public void setLineColor(Color color) { 261 Args.nullNotPermitted(color, "color"); 262 this.lineColor = color; 263 fireChangeEvent(); 264 } 265 266 /** 267 * Returns the line stroke. The default value is 268 * {@link Marker#DEFAULT_LINE_STROKE}. 269 * 270 * @return The line stroke (never {@code null}). 271 */ 272 public Stroke getLineStroke() { 273 return this.lineStroke; 274 } 275 276 /** 277 * Sets the line stroke and sends a change event to all registered 278 * listeners. 279 * 280 * @param stroke the stroke ({@code null} not permitted). 281 */ 282 public void setLineStroke(Stroke stroke) { 283 Args.nullNotPermitted(stroke, "stroke"); 284 this.lineStroke = stroke; 285 fireChangeEvent(); 286 } 287 288 /** 289 * Returns the color used to fill the marker band. 290 * 291 * @return The color (never {@code null}). 292 */ 293 public Color getFillColor() { 294 return this.fillColor; 295 } 296 297 /** 298 * Sets the color used to fill the marker band and sends a change event 299 * to all registered listeners. 300 * 301 * @param color the color ({@code null} not permitted). 302 */ 303 public void setFillColor(Color color) { 304 Args.nullNotPermitted(color, "color"); 305 this.fillColor = color; 306 fireChangeEvent(); 307 } 308 309 /** 310 * Handles drawing of the marker. This method is called by the library, 311 * you won't normally call it directly. 312 * 313 * @param g2 the graphics device ({@code null} not permitted). 314 * @param markerData the marker data ({@code null} not permitted). 315 * @param reverse a flag to indicate reverse orientation. 316 */ 317 @Override 318 public void draw(Graphics2D g2, MarkerData markerData, boolean reverse) { 319 if (markerData.getType().equals(MarkerDataType.VALUE)) { 320 MarkerLine ml = markerData.getValueLine(); 321 g2.setPaint(this.lineColor); 322 g2.setStroke(this.lineStroke); 323 Line2D l = new Line2D.Double(ml.getStartPoint(), ml.getEndPoint()); 324 g2.draw(l); 325 Point2D labelPoint = markerData.getLabelPoint(); 326 if (labelPoint != null) { 327 g2.setFont(this.font); 328 g2.setColor(this.labelColor); 329 drawMarkerLabel(g2, this.label, labelPoint.getX(), 330 labelPoint.getY(), this.labelAnchor, l, reverse); 331 } 332 } else if (markerData.getType().equals(MarkerDataType.RANGE)) { 333 MarkerLine sl = markerData.getStartLine(); 334 Line2D l1 = new Line2D.Double(sl.getStartPoint(), sl.getEndPoint()); 335 MarkerLine el = markerData.getEndLine(); 336 Line2D l2 = new Line2D.Double(el.getStartPoint(), el.getEndPoint()); 337 338 Path2D path = new Path2D.Double(); 339 path.moveTo(l1.getX1(), l1.getY1()); 340 path.lineTo(l1.getX2(), l1.getY2()); 341 path.lineTo(l2.getX2(), l2.getY2()); 342 path.lineTo(l2.getX1(), l2.getY1()); 343 path.closePath(); 344 g2.setPaint(this.fillColor); 345 g2.fill(path); 346 347 g2.setColor(this.lineColor); 348 g2.setStroke(this.lineStroke); 349 g2.draw(l1); 350 g2.draw(l2); 351 352 Point2D labelPoint = markerData.getLabelPoint(); 353 if (labelPoint != null) { 354 g2.setFont(this.font); 355 g2.setColor(this.labelColor); 356 drawMarkerLabel(g2, this.label, labelPoint.getX(), 357 labelPoint.getY(), markerData.getLabelAnchor(), l1, l2, 358 reverse); 359 } 360 } 361 } 362 363 @Override 364 public int hashCode() { 365 int hash = 7; 366 hash = 73 * hash + ObjectUtils.hashCode(this.category); 367 return hash; 368 } 369 370 @Override 371 public boolean equals(Object obj) { 372 if (obj == null) { 373 return false; 374 } 375 if (getClass() != obj.getClass()) { 376 return false; 377 } 378 final CategoryMarker other = (CategoryMarker) obj; 379 if (!ObjectUtils.equals(this.category, other.category)) { 380 return false; 381 } 382 if (this.type != other.type) { 383 return false; 384 } 385 if (!ObjectUtils.equals(this.label, other.label)) { 386 return false; 387 } 388 if (!ObjectUtils.equals(this.font, other.font)) { 389 return false; 390 } 391 if (!ObjectUtils.equals(this.labelColor, other.labelColor)) { 392 return false; 393 } 394 if (!ObjectUtils.equals(this.labelAnchor, other.labelAnchor)) { 395 return false; 396 } 397 if (!ObjectUtils.equals(this.lineStroke, other.lineStroke)) { 398 return false; 399 } 400 if (!ObjectUtils.equals(this.lineColor, other.lineColor)) { 401 return false; 402 } 403 if (!ObjectUtils.equals(this.fillColor, other.fillColor)) { 404 return false; 405 } 406 return true; 407 } 408 409 /** 410 * Provides serialization support. 411 * 412 * @param stream the output stream. 413 * 414 * @throws IOException if there is an I/O error. 415 */ 416 private void writeObject(ObjectOutputStream stream) throws IOException { 417 stream.defaultWriteObject(); 418 SerialUtils.writeStroke(this.lineStroke, stream); 419 } 420 421 /** 422 * Provides serialization support. 423 * 424 * @param stream the input stream. 425 * 426 * @throws IOException if there is an I/O error. 427 * @throws ClassNotFoundException if there is a classpath problem. 428 */ 429 private void readObject(ObjectInputStream stream) 430 throws IOException, ClassNotFoundException { 431 stream.defaultReadObject(); 432 this.lineStroke = SerialUtils.readStroke(stream); 433 } 434 435}