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.geom.Line2D; 039import java.awt.geom.Path2D; 040import java.awt.geom.Point2D; 041import java.io.Serializable; 042 043import org.jfree.chart3d.data.Range; 044import org.jfree.chart3d.graphics2d.Anchor2D; 045import org.jfree.chart3d.internal.Args; 046import org.jfree.chart3d.internal.ObjectUtils; 047 048/** 049 * A marker that marks a range of values on an axis. 050 * <br><br> 051 * For an example, please refer to the demo {@code RangeMarkerDemo1.java}. 052 * <br><br> 053 * NOTE: This class is serializable, but the serialization format is subject 054 * to change in future releases and should not be relied upon for persisting 055 * instances of this class. 056 * 057 * @since 1.2 058 */ 059@SuppressWarnings("serial") 060public class RangeMarker extends AbstractMarker implements ValueMarker, 061 MarkerChangeListener, Serializable { 062 063 /** The start of the range. */ 064 private NumberMarker start; 065 066 /** The end of the range. */ 067 private NumberMarker end; 068 069 /** The label for the marker (optional). */ 070 private String label; 071 072 /** The font for the label. */ 073 private Font font; 074 075 /** The color for the label. */ 076 private Color labelColor; 077 078 /** The anchor for the label. */ 079 private Anchor2D labelAnchor; 080 081 /** The color used to fill the band representing the marker range. */ 082 Color fillColor; 083 084 /** 085 * Creates a new range marker for the given bounds. 086 * 087 * @param lowerBound the lower bound. 088 * @param upperBound the upper bound. 089 */ 090 public RangeMarker(double lowerBound, double upperBound) { 091 this(lowerBound, upperBound, null); 092 } 093 094 /** 095 * Creates a new range marker for the given bounds. 096 * 097 * @param lowerBound the lower bound. 098 * @param upperBound the upper bound. 099 * @param label the label ({@code null} permitted). 100 */ 101 public RangeMarker(double lowerBound, double upperBound, String label) { 102 super(); 103 this.start = new NumberMarker(lowerBound); 104 this.start.addChangeListener(this); 105 this.end = new NumberMarker(upperBound); 106 this.end.addChangeListener(this); 107 this.label = label; 108 this.font = DEFAULT_MARKER_FONT; 109 this.labelColor = DEFAULT_LABEL_COLOR; 110 this.labelAnchor = Anchor2D.CENTER; 111 this.fillColor = DEFAULT_FILL_COLOR; 112 } 113 114 /** 115 * Returns the starting point for the range marker. 116 * 117 * @return The starting point. 118 */ 119 public NumberMarker getStart() { 120 return this.start; 121 } 122 123 /** 124 * Returns the ending point for the range marker. 125 * 126 * @return The ending point. 127 */ 128 public NumberMarker getEnd() { 129 return this.end; 130 } 131 132 /** 133 * Returns the range of values for the marker. 134 * 135 * @return The range of values for the marker. 136 */ 137 @Override 138 public Range getRange() { 139 return new Range(this.start.getValue(), this.end.getValue()); 140 } 141 142 /** 143 * Returns the label for the marker (if this is {@code null} then no 144 * label is displayed). 145 * 146 * @return The label (possibly {@code null}). 147 */ 148 public String getLabel() { 149 return this.label; 150 } 151 152 /** 153 * Sets the label and sends a change event to all registered listeners. If 154 * the label is set to {@code null} then no label is displayed for 155 * the marker. 156 * 157 * @param label the label ({@code null} permitted). 158 */ 159 public void setLabel(String label) { 160 this.label = label; 161 fireChangeEvent(); 162 } 163 164 /** 165 * Returns the font for the label. The default value is 166 * {@link Marker#DEFAULT_MARKER_FONT}. 167 * 168 * @return The font (never {@code null}). 169 */ 170 public Font getFont() { 171 return this.font; 172 } 173 174 /** 175 * Sets the font for the marker label and sends a change event to all 176 * registered listeners. 177 * 178 * @param font the font ({@code null} not permitted). 179 */ 180 public void setFont(Font font) { 181 Args.nullNotPermitted(font, "font"); 182 this.font = font; 183 fireChangeEvent(); 184 } 185 186 /** 187 * Returns the label color. The default value is 188 * {@link Marker#DEFAULT_LABEL_COLOR}. 189 * 190 * @return The label color (never {@code null}). 191 */ 192 public Color getLabelColor() { 193 return this.labelColor; 194 } 195 196 /** 197 * Sets the label color and sends a change event to all registered 198 * listeners. 199 * 200 * @param color the color ({@code null} not permitted). 201 */ 202 public void setLabelColor(Color color) { 203 Args.nullNotPermitted(color, "color"); 204 this.labelColor = color; 205 fireChangeEvent(); 206 } 207 208 /** 209 * Returns the anchor for the label. The default value is 210 * {@link Anchor2D#CENTER}. 211 * 212 * @return The anchor for the label. 213 */ 214 public Anchor2D getLabelAnchor() { 215 return this.labelAnchor; 216 } 217 218 /** 219 * Sets the anchor for the label and sends a change event to all registered 220 * listeners. 221 * 222 * @param anchor the anchor ({@code null} not permitted). 223 */ 224 public void setLabelAnchor(Anchor2D anchor) { 225 Args.nullNotPermitted(anchor, "anchor"); 226 this.labelAnchor = anchor; 227 fireChangeEvent(); 228 } 229 230 /** 231 * Returns the color used to fill the band representing the range for 232 * the marker. The default value is {@link Marker#DEFAULT_FILL_COLOR}. 233 * 234 * @return The fill color (never {@code null}). 235 */ 236 public Color getFillColor() { 237 return this.fillColor; 238 } 239 240 /** 241 * Sets the color used to fill the band representing the range for the 242 * marker and sends a change event to all registered listeners. 243 * 244 * @param color the color ({@code null} not permitted). 245 */ 246 public void setFillColor(Color color) { 247 Args.nullNotPermitted(color, "color"); 248 this.fillColor = color; 249 fireChangeEvent(); 250 } 251 252 @Override 253 public void draw(Graphics2D g2, MarkerData markerData, boolean reverse) { 254 MarkerLine startLine = markerData.getStartLine(); 255 Line2D l1 = new Line2D.Double(startLine.getStartPoint(), 256 startLine.getEndPoint()); 257 MarkerLine endLine = markerData.getEndLine(); 258 Line2D l2 = new Line2D.Double(endLine.getStartPoint(), 259 endLine.getEndPoint()); 260 261 Path2D path = new Path2D.Double(); 262 path.moveTo(l1.getX1(), l1.getY1()); 263 path.lineTo(l1.getX2(), l1.getY2()); 264 path.lineTo(l2.getX2(), l2.getY2()); 265 path.lineTo(l2.getX1(), l2.getY1()); 266 path.closePath(); 267 g2.setPaint(this.fillColor); 268 g2.fill(path); 269 270 if (!startLine.isPegged()) { 271 g2.setPaint(this.start.getLineColor()); 272 g2.setStroke(this.start.getLineStroke()); 273 g2.draw(l1); 274 } 275 if (!endLine.isPegged()) { 276 g2.setPaint(this.end.getLineColor()); 277 g2.setStroke(this.end.getLineStroke()); 278 g2.draw(l2); 279 } 280 281 Point2D labelPoint = markerData.getLabelPoint(); 282 if (labelPoint != null) { 283 g2.setFont(this.font); 284 g2.setColor(this.labelColor); 285 drawMarkerLabel(g2, this.label, labelPoint.getX(), 286 labelPoint.getY(), markerData.getLabelAnchor(), l1, l2, 287 reverse); 288 } 289 } 290 291 /** 292 * Receives notification of a change to the start or end marker for the 293 * range. 294 * 295 * @param event the event ({@code null} not permitted). 296 */ 297 @Override 298 public void markerChanged(MarkerChangeEvent event) { 299 fireChangeEvent(); 300 } 301 302 @Override 303 public int hashCode() { 304 int hash = 7; 305 hash = 97 * hash + ObjectUtils.hashCode(this.start); 306 hash = 97 * hash + ObjectUtils.hashCode(this.end); 307 hash = 97 * hash + ObjectUtils.hashCode(this.label); 308 return hash; 309 } 310 311 /** 312 * Tests this marker for equality with an arbitrary object. 313 * 314 * @param obj the object to test against ({@code null} permitted). 315 * 316 * @return A boolean. 317 */ 318 @Override 319 public boolean equals(Object obj) { 320 if (obj == null) { 321 return false; 322 } 323 if (getClass() != obj.getClass()) { 324 return false; 325 } 326 final RangeMarker other = (RangeMarker) obj; 327 if (!ObjectUtils.equals(this.start, other.start)) { 328 return false; 329 } 330 if (!ObjectUtils.equals(this.end, other.end)) { 331 return false; 332 } 333 if (!ObjectUtils.equals(this.label, other.label)) { 334 return false; 335 } 336 if (!ObjectUtils.equals(this.font, other.font)) { 337 return false; 338 } 339 if (!ObjectUtils.equals(this.labelColor, other.labelColor)) { 340 return false; 341 } 342 if (!ObjectUtils.equals(this.labelAnchor, other.labelAnchor)) { 343 return false; 344 } 345 if (!ObjectUtils.equals(this.fillColor, other.fillColor)) { 346 return false; 347 } 348 return true; 349 } 350 351}