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.graphics2d; 034 035import org.jfree.chart3d.internal.Args; 036import java.awt.geom.Dimension2D; 037import java.awt.geom.Point2D; 038import java.awt.geom.Rectangle2D; 039import java.io.Serializable; 040 041/** 042 * A specification for the alignment and fitting of one rectangle (the source 043 * rectangle) with reference to another (the target rectangle). Instances of 044 * this class are immutable. 045 * <br><br> 046 * One application for this is to specify how the background image for a chart 047 * should be aligned and scaled. 048 * <br><br> 049 * NOTE: This class is serializable, but the serialization format is subject 050 * to change in future releases and should not be relied upon for persisting 051 * instances of this class. 052 */ 053@SuppressWarnings("serial") 054public class Fit2D implements Serializable { 055 056 /** 057 * Aligns a source rectangle to the center of a target rectangle, without 058 * resizing it. 059 * 060 * @since 1.1 061 */ 062 public static final Fit2D CENTER_NO_SCALING = new Fit2D(Anchor2D.CENTER, 063 Scale2D.NONE); 064 065 /** 066 * Fits a source rectangle to the top left of a target rectangle, without 067 * resizing it. 068 * 069 * @since 1.1 070 */ 071 public static final Fit2D TOP_LEFT_NO_SCALING 072 = new Fit2D(Anchor2D.TOP_LEFT, Scale2D.NONE); 073 074 /** 075 * Fits a source rectangle to the top center of a target rectangle, without 076 * resizing it. 077 * 078 * @since 1.1 079 */ 080 public static final Fit2D TOP_CENTER_NO_SCALING 081 = new Fit2D(Anchor2D.TOP_CENTER, Scale2D.NONE); 082 083 /** 084 * Fits a source rectangle to the top right of a target rectangle, without 085 * resizing it. 086 * 087 * @since 1.1 088 */ 089 public static final Fit2D TOP_RIGHT_NO_SCALING 090 = new Fit2D(Anchor2D.TOP_RIGHT, Scale2D.NONE); 091 092 /** 093 * Fits a source rectangle to the center left of a target rectangle, 094 * without resizing it. 095 * 096 * @since 1.1 097 */ 098 public static final Fit2D CENTER_LEFT_NO_SCALING 099 = new Fit2D(Anchor2D.CENTER_LEFT, Scale2D.NONE); 100 101 /** 102 * Fits a source rectangle to the center right of a target rectangle, 103 * without resizing it. 104 * 105 * @since 1.1 106 */ 107 public static final Fit2D CENTER_RIGHT_NO_SCALING 108 = new Fit2D(Anchor2D.CENTER_RIGHT, Scale2D.NONE); 109 110 /** 111 * Fits a source rectangle to the bottom left of a target rectangle, 112 * without resizing it. 113 * 114 * @since 1.1 115 */ 116 public static final Fit2D BOTTOM_LEFT_NO_SCALING 117 = new Fit2D(Anchor2D.BOTTOM_LEFT, Scale2D.NONE); 118 119 /** 120 * Fits a source rectangle to the bottom center of a target rectangle, 121 * without resizing it. 122 * 123 * @since 1.1 124 */ 125 public static final Fit2D BOTTOM_CENTER_NO_SCALING 126 = new Fit2D(Anchor2D.BOTTOM_CENTER, Scale2D.NONE); 127 128 /** 129 * Fits a source rectangle to the bottom right of a target rectangle, 130 * without resizing it. 131 * 132 * @since 1.1 133 */ 134 public static final Fit2D BOTTOM_RIGHT_NO_SCALING 135 = new Fit2D(Anchor2D.BOTTOM_RIGHT, Scale2D.NONE); 136 137 /** 138 * Returns a fitter for the specified reference point. 139 * 140 * @param refPt the reference point ({@code null} not permitted). 141 * 142 * @return A fitter. 143 * 144 * @since 1.1 145 */ 146 public static Fit2D getNoScalingFitter(RefPt2D refPt) { 147 switch (refPt) { 148 case TOP_LEFT : return Fit2D.TOP_LEFT_NO_SCALING; 149 case TOP_CENTER : return Fit2D.TOP_CENTER_NO_SCALING; 150 case TOP_RIGHT : return Fit2D.TOP_RIGHT_NO_SCALING; 151 case CENTER_LEFT : return Fit2D.CENTER_LEFT_NO_SCALING; 152 case CENTER : return Fit2D.CENTER_NO_SCALING; 153 case CENTER_RIGHT : return Fit2D.CENTER_RIGHT_NO_SCALING; 154 case BOTTOM_LEFT : return Fit2D.BOTTOM_LEFT_NO_SCALING; 155 case BOTTOM_CENTER : return Fit2D.BOTTOM_CENTER_NO_SCALING; 156 case BOTTOM_RIGHT : return Fit2D.BOTTOM_RIGHT_NO_SCALING; 157 } 158 throw new IllegalStateException("RefPt2D not recognised : " + refPt); 159 } 160 161 /** 162 * Scale the source rectangle to fit the target rectangle. 163 * 164 * @since 1.1 165 */ 166 public static final Fit2D SCALE_TO_FIT_TARGET 167 = new Fit2D(Anchor2D.CENTER, Scale2D.SCALE_BOTH); 168 169 /** The anchor point for alignment. */ 170 private final Anchor2D anchor; 171 172 /** The scaling to apply. */ 173 private final Scale2D scale; 174 175 /** 176 * Creates a new instance. 177 * 178 * @param anchor the anchor point ({@code null} not permitted). 179 * @param scale the scaling ({@code null} not permitted). 180 */ 181 public Fit2D(Anchor2D anchor, Scale2D scale) { 182 Args.nullNotPermitted(anchor, "anchor"); 183 Args.nullNotPermitted(scale, "scale"); 184 this.anchor = anchor; 185 this.scale = scale; 186 } 187 188 /** 189 * Returns the anchor. 190 * 191 * @return The anchor (never {@code null}). 192 * 193 * @since 1.1 194 */ 195 public Anchor2D getAnchor() { 196 return this.anchor; 197 } 198 199 /** 200 * Returns the scaling. 201 * 202 * @return The scaling (never {@code null}). 203 * 204 * @since 1.1 205 */ 206 public Scale2D getScale() { 207 return this.scale; 208 } 209 210 /** 211 * Fits a rectangle of the specified dimension to the target rectangle, 212 * aligning and scaling according to the attributes of this instance. 213 * 214 * @param srcDim the dimensions of the source rectangle ({@code null} 215 * not permitted). 216 * @param target the target rectangle ({@code null} not permitted). 217 * 218 * @return The bounds of the fitted rectangle (never {@code null}). 219 */ 220 public Rectangle2D fit(Dimension2D srcDim, Rectangle2D target) { 221 Rectangle2D result = new Rectangle2D.Double(); 222 if (this.scale == Scale2D.SCALE_BOTH) { 223 result.setFrame(target); 224 return result; 225 } 226 double width = srcDim.getWidth(); 227 if (this.scale == Scale2D.SCALE_HORIZONTAL) { 228 width = target.getWidth(); 229 if (!this.anchor.getRefPt().isHorizontalCenter()) { 230 width -= 2 * this.anchor.getOffset().getDX(); 231 } 232 } 233 double height = srcDim.getHeight(); 234 if (this.scale == Scale2D.SCALE_VERTICAL) { 235 height = target.getHeight(); 236 if (!this.anchor.getRefPt().isVerticalCenter()) { 237 height -= 2 * this.anchor.getOffset().getDY(); 238 } 239 } 240 Point2D pt = this.anchor.getAnchorPoint(target); 241 double x = Double.NaN; 242 if (this.anchor.getRefPt().isLeft()) { 243 x = pt.getX(); 244 } else if (this.anchor.getRefPt().isHorizontalCenter()) { 245 x = target.getCenterX() - width / 2; 246 } else if (this.anchor.getRefPt().isRight()) { 247 x = pt.getX() - width; 248 } 249 double y = Double.NaN; 250 if (this.anchor.getRefPt().isTop()) { 251 y = pt.getY(); 252 } else if (this.anchor.getRefPt().isVerticalCenter()) { 253 y = target.getCenterY() - height / 2; 254 } else if (this.anchor.getRefPt().isBottom()) { 255 y = pt.getY() - height; 256 } 257 result.setRect(x, y, width, height); 258 return result; 259 } 260 261 /** 262 * Tests this instance for equality with an arbitrary object. 263 * 264 * @param obj the object ({@code null} permitted). 265 * 266 * @return A boolean. 267 */ 268 @Override 269 public boolean equals(Object obj) { 270 if (obj == this) { 271 return true; 272 } 273 if (!(obj instanceof Fit2D)) { 274 return false; 275 } 276 Fit2D that = (Fit2D) obj; 277 if (!this.anchor.equals(that.anchor)) { 278 return false; 279 } 280 if (!this.scale.equals(that.scale)) { 281 return false; 282 } 283 return true; 284 } 285}