001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.math.geometry.shape; 031 032import org.openimaj.math.geometry.point.Point2d; 033import org.openimaj.math.geometry.point.Point2dImpl; 034 035import Jama.Matrix; 036 037/** 038 * A rectangle rotated by an angle. 039 * 040 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 041 * 042 */ 043public class RotatedRectangle implements Shape, Cloneable { 044 /** 045 * The width of the rotated rectangle (Note that this is different to the 046 * width returned by {@link #getWidth()} which is the width of the regular 047 * bounding box) 048 */ 049 public float width; 050 051 /** 052 * The height of the rotated rectangle (Note that this is different to the 053 * height returned by {@link #getHeight()} which is the height of the 054 * regular bounding box) 055 */ 056 public float height; 057 058 /** 059 * The rotation angle in radians 060 */ 061 public float rotation; 062 063 /** 064 * The x-ordinate of the centroid 065 */ 066 public float cx; 067 068 /** 069 * The y-ordinate of the centroid 070 */ 071 public float cy; 072 073 /** 074 * Construct with a regular {@link Rectangle} rotated about its centroid 075 * 076 * @param regRect 077 * the regular rectangle 078 * @param rotation 079 * the rotation angle in radians 080 */ 081 public RotatedRectangle(Rectangle regRect, float rotation) { 082 this.width = regRect.width; 083 this.height = regRect.height; 084 this.cx = regRect.x + regRect.width / 2f; 085 this.cy = regRect.y + regRect.height / 2f; 086 this.rotation = rotation; 087 } 088 089 /** 090 * Construct with the given parameters 091 * 092 * @param x 093 * the x-ordinate of the centroid 094 * @param y 095 * the y-ordinate of the centroid 096 * @param width 097 * the width 098 * @param height 099 * the height 100 * @param rotation 101 * the rotation 102 */ 103 public RotatedRectangle(double x, double y, double width, double height, double rotation) { 104 this.cx = (float) x; 105 this.cy = (float) y; 106 this.width = (float) width; 107 this.height = (float) height; 108 this.rotation = (float) rotation; 109 } 110 111 /** 112 * Construct with the given parameters 113 * 114 * @param x 115 * the x-ordinate of the centroid 116 * @param y 117 * the y-ordinate of the centroid 118 * @param width 119 * the width 120 * @param height 121 * the height 122 * @param rotation 123 * the rotation 124 */ 125 public RotatedRectangle(float x, float y, float width, float height, float rotation) { 126 this.cx = x; 127 this.cy = y; 128 this.width = width; 129 this.height = height; 130 this.rotation = rotation; 131 } 132 133 @Override 134 public Rectangle calculateRegularBoundingBox() { 135 return this.asPolygon().calculateRegularBoundingBox(); 136 } 137 138 @Override 139 public void translate(float x, float y) { 140 cx += x; 141 cy += y; 142 } 143 144 @Override 145 public void scale(float sc) { 146 cx *= sc; 147 cy *= sc; 148 width *= sc; 149 height *= sc; 150 } 151 152 @Override 153 public void scale(Point2d centre, float sc) { 154 this.translate(-centre.getX(), -centre.getY()); 155 scale(sc); 156 this.translate(centre.getX(), centre.getY()); 157 158 } 159 160 @Override 161 public void scaleCentroid(float sc) { 162 scale(calculateCentroid(), sc); 163 } 164 165 @Override 166 public Point2d calculateCentroid() { 167 return new Point2dImpl(cx, cy); 168 } 169 170 @Override 171 public double minX() { 172 return this.asPolygon().minX(); 173 } 174 175 @Override 176 public double minY() { 177 return this.asPolygon().minY(); 178 } 179 180 @Override 181 public double maxX() { 182 return this.asPolygon().maxX(); 183 } 184 185 @Override 186 public double maxY() { 187 return this.asPolygon().maxY(); 188 } 189 190 @Override 191 public double getWidth() { 192 return this.asPolygon().getWidth(); 193 } 194 195 @Override 196 public double getHeight() { 197 return this.asPolygon().getHeight(); 198 } 199 200 @Override 201 public boolean isInside(Point2d point) { 202 return this.asPolygon().isInside(point); 203 } 204 205 @Override 206 public double calculateArea() { 207 return width * height; 208 } 209 210 @Override 211 public double calculatePerimeter() { 212 return 2 * (width + height); 213 } 214 215 @Override 216 public Polygon asPolygon() { 217 final float b = (float) Math.cos(rotation) * 0.5f; 218 final float a = (float) Math.sin(rotation) * 0.5f; 219 220 final Point2dImpl[] pts = new Point2dImpl[4]; 221 pts[0] = new Point2dImpl(); 222 pts[0].x = cx - a * height - b * width; 223 pts[0].y = cy + b * height - a * width; 224 225 pts[1] = new Point2dImpl(); 226 pts[1].x = cx + a * height - b * width; 227 pts[1].y = cy - b * height - a * width; 228 229 pts[2] = new Point2dImpl(); 230 pts[2].x = 2 * cx - pts[0].x; 231 pts[2].y = 2 * cy - pts[0].y; 232 233 pts[3] = new Point2dImpl(); 234 pts[3].x = 2 * cx - pts[1].x; 235 pts[3].y = 2 * cy - pts[1].y; 236 237 return new Polygon(pts); 238 } 239 240 @Override 241 public double intersectionArea(Shape that) { 242 return this.asPolygon().intersectionArea(that); 243 } 244 245 @Override 246 public double intersectionArea(Shape that, int nStepsPerDimension) { 247 return this.asPolygon().intersectionArea(that, nStepsPerDimension); 248 } 249 250 @Override 251 public Shape transform(Matrix transform) { 252 return this.asPolygon().transform(transform); 253 } 254 255 @Override 256 public RotatedRectangle minimumBoundingRectangle() { 257 return this.clone(); 258 } 259 260 @Override 261 public RotatedRectangle clone() { 262 try { 263 return (RotatedRectangle) super.clone(); 264 } catch (final CloneNotSupportedException e) { 265 throw new RuntimeException(e); 266 } 267 } 268 269 @Override 270 public String toString() { 271 return String.format("RotatedRect[angle=%2.2f, cx=%2.2f, cy=%2.2f, width=%2.2f, height=%2.2f]", rotation, cx, cy, 272 width, height); 273 } 274 275 @Override 276 public boolean isConvex() { 277 return true; 278 } 279}