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