Home | History | Annotate | Download | only in math
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
      5  * License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
     10  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
     11  * governing permissions and limitations under the License.
     12  ******************************************************************************/
     13 
     14 package com.badlogic.gdx.math;
     15 
     16 import java.io.Serializable;
     17 
     18 import com.badlogic.gdx.utils.GdxRuntimeException;
     19 import com.badlogic.gdx.utils.NumberUtils;
     20 import com.badlogic.gdx.utils.Scaling;
     21 
     22 /** Encapsulates a 2D rectangle defined by its corner point in the bottom left and its extents in x (width) and y (height).
     23  * @author badlogicgames (at) gmail.com */
     24 public class Rectangle implements Serializable, Shape2D {
     25 	/** Static temporary rectangle. Use with care! Use only when sure other code will not also use this. */
     26 	static public final Rectangle tmp = new Rectangle();
     27 
     28 	/** Static temporary rectangle. Use with care! Use only when sure other code will not also use this. */
     29 	static public final Rectangle tmp2 = new Rectangle();
     30 
     31 	private static final long serialVersionUID = 5733252015138115702L;
     32 	public float x, y;
     33 	public float width, height;
     34 
     35 	/** Constructs a new rectangle with all values set to zero */
     36 	public Rectangle () {
     37 
     38 	}
     39 
     40 	/** Constructs a new rectangle with the given corner point in the bottom left and dimensions.
     41 	 * @param x The corner point x-coordinate
     42 	 * @param y The corner point y-coordinate
     43 	 * @param width The width
     44 	 * @param height The height */
     45 	public Rectangle (float x, float y, float width, float height) {
     46 		this.x = x;
     47 		this.y = y;
     48 		this.width = width;
     49 		this.height = height;
     50 	}
     51 
     52 	/** Constructs a rectangle based on the given rectangle
     53 	 * @param rect The rectangle */
     54 	public Rectangle (Rectangle rect) {
     55 		x = rect.x;
     56 		y = rect.y;
     57 		width = rect.width;
     58 		height = rect.height;
     59 	}
     60 
     61 	/** @param x bottom-left x coordinate
     62 	 * @param y bottom-left y coordinate
     63 	 * @param width width
     64 	 * @param height height
     65 	 * @return this rectangle for chaining */
     66 	public Rectangle set (float x, float y, float width, float height) {
     67 		this.x = x;
     68 		this.y = y;
     69 		this.width = width;
     70 		this.height = height;
     71 
     72 		return this;
     73 	}
     74 
     75 	/** @return the x-coordinate of the bottom left corner */
     76 	public float getX () {
     77 		return x;
     78 	}
     79 
     80 	/** Sets the x-coordinate of the bottom left corner
     81 	 * @param x The x-coordinate
     82 	 * @return this rectangle for chaining */
     83 	public Rectangle setX (float x) {
     84 		this.x = x;
     85 
     86 		return this;
     87 	}
     88 
     89 	/** @return the y-coordinate of the bottom left corner */
     90 	public float getY () {
     91 		return y;
     92 	}
     93 
     94 	/** Sets the y-coordinate of the bottom left corner
     95 	 * @param y The y-coordinate
     96 	 * @return this rectangle for chaining */
     97 	public Rectangle setY (float y) {
     98 		this.y = y;
     99 
    100 		return this;
    101 	}
    102 
    103 	/** @return the width */
    104 	public float getWidth () {
    105 		return width;
    106 	}
    107 
    108 	/** Sets the width of this rectangle
    109 	 * @param width The width
    110 	 * @return this rectangle for chaining */
    111 	public Rectangle setWidth (float width) {
    112 		this.width = width;
    113 
    114 		return this;
    115 	}
    116 
    117 	/** @return the height */
    118 	public float getHeight () {
    119 		return height;
    120 	}
    121 
    122 	/** Sets the height of this rectangle
    123 	 * @param height The height
    124 	 * @return this rectangle for chaining */
    125 	public Rectangle setHeight (float height) {
    126 		this.height = height;
    127 
    128 		return this;
    129 	}
    130 
    131 	/** return the Vector2 with coordinates of this rectangle
    132 	 * @param position The Vector2 */
    133 	public Vector2 getPosition (Vector2 position) {
    134 		return position.set(x, y);
    135 	}
    136 
    137 	/** Sets the x and y-coordinates of the bottom left corner from vector
    138 	 * @param position The position vector
    139 	 * @return this rectangle for chaining */
    140 	public Rectangle setPosition (Vector2 position) {
    141 		this.x = position.x;
    142 		this.y = position.y;
    143 
    144 		return this;
    145 	}
    146 
    147 	/** Sets the x and y-coordinates of the bottom left corner
    148 	 * @param x The x-coordinate
    149 	 * @param y The y-coordinate
    150 	 * @return this rectangle for chaining */
    151 	public Rectangle setPosition (float x, float y) {
    152 		this.x = x;
    153 		this.y = y;
    154 
    155 		return this;
    156 	}
    157 
    158 	/** Sets the width and height of this rectangle
    159 	 * @param width The width
    160 	 * @param height The height
    161 	 * @return this rectangle for chaining */
    162 	public Rectangle setSize (float width, float height) {
    163 		this.width = width;
    164 		this.height = height;
    165 
    166 		return this;
    167 	}
    168 
    169 	/** Sets the squared size of this rectangle
    170 	 * @param sizeXY The size
    171 	 * @return this rectangle for chaining */
    172 	public Rectangle setSize (float sizeXY) {
    173 		this.width = sizeXY;
    174 		this.height = sizeXY;
    175 
    176 		return this;
    177 	}
    178 
    179 	/** @return the Vector2 with size of this rectangle
    180 	 * @param size The Vector2 */
    181 	public Vector2 getSize (Vector2 size) {
    182 		return size.set(width, height);
    183 	}
    184 
    185 	/** @param x point x coordinate
    186 	 * @param y point y coordinate
    187 	 * @return whether the point is contained in the rectangle */
    188 	public boolean contains (float x, float y) {
    189 		return this.x <= x && this.x + this.width >= x && this.y <= y && this.y + this.height >= y;
    190 	}
    191 
    192 	/** @param point The coordinates vector
    193 	 * @return whether the point is contained in the rectangle */
    194 	public boolean contains (Vector2 point) {
    195 		return contains(point.x, point.y);
    196 	}
    197 
    198 	/** @param rectangle the other {@link Rectangle}.
    199 	 * @return whether the other rectangle is contained in this rectangle. */
    200 	public boolean contains (Rectangle rectangle) {
    201 		float xmin = rectangle.x;
    202 		float xmax = xmin + rectangle.width;
    203 
    204 		float ymin = rectangle.y;
    205 		float ymax = ymin + rectangle.height;
    206 
    207 		return ((xmin > x && xmin < x + width) && (xmax > x && xmax < x + width))
    208 			&& ((ymin > y && ymin < y + height) && (ymax > y && ymax < y + height));
    209 	}
    210 
    211 	/** @param r the other {@link Rectangle}
    212 	 * @return whether this rectangle overlaps the other rectangle. */
    213 	public boolean overlaps (Rectangle r) {
    214 		return x < r.x + r.width && x + width > r.x && y < r.y + r.height && y + height > r.y;
    215 	}
    216 
    217 	/** Sets the values of the given rectangle to this rectangle.
    218 	 * @param rect the other rectangle
    219 	 * @return this rectangle for chaining */
    220 	public Rectangle set (Rectangle rect) {
    221 		this.x = rect.x;
    222 		this.y = rect.y;
    223 		this.width = rect.width;
    224 		this.height = rect.height;
    225 
    226 		return this;
    227 	}
    228 
    229 	/** Merges this rectangle with the other rectangle. The rectangle should not have negative width or negative height.
    230 	 * @param rect the other rectangle
    231 	 * @return this rectangle for chaining */
    232 	public Rectangle merge (Rectangle rect) {
    233 		float minX = Math.min(x, rect.x);
    234 		float maxX = Math.max(x + width, rect.x + rect.width);
    235 		x = minX;
    236 		width = maxX - minX;
    237 
    238 		float minY = Math.min(y, rect.y);
    239 		float maxY = Math.max(y + height, rect.y + rect.height);
    240 		y = minY;
    241 		height = maxY - minY;
    242 
    243 		return this;
    244 	}
    245 
    246 	/** Merges this rectangle with a point. The rectangle should not have negative width or negative height.
    247 	 * @param x the x coordinate of the point
    248 	 * @param y the y coordinate of the point
    249 	 * @return this rectangle for chaining */
    250 	public Rectangle merge (float x, float y) {
    251 		float minX = Math.min(this.x, x);
    252 		float maxX = Math.max(this.x + width, x);
    253 		this.x = minX;
    254 		this.width = maxX - minX;
    255 
    256 		float minY = Math.min(this.y, y);
    257 		float maxY = Math.max(this.y + height, y);
    258 		this.y = minY;
    259 		this.height = maxY - minY;
    260 
    261 		return this;
    262 	}
    263 
    264 	/** Merges this rectangle with a point. The rectangle should not have negative width or negative height.
    265 	 * @param vec the vector describing the point
    266 	 * @return this rectangle for chaining */
    267 	public Rectangle merge (Vector2 vec) {
    268 		return merge(vec.x, vec.y);
    269 	}
    270 
    271 	/** Merges this rectangle with a list of points. The rectangle should not have negative width or negative height.
    272 	 * @param vecs the vectors describing the points
    273 	 * @return this rectangle for chaining */
    274 	public Rectangle merge (Vector2[] vecs) {
    275 		float minX = x;
    276 		float maxX = x + width;
    277 		float minY = y;
    278 		float maxY = y + height;
    279 		for (int i = 0; i < vecs.length; ++i) {
    280 			Vector2 v = vecs[i];
    281 			minX = Math.min(minX, v.x);
    282 			maxX = Math.max(maxX, v.x);
    283 			minY = Math.min(minY, v.y);
    284 			maxY = Math.max(maxY, v.y);
    285 		}
    286 		x = minX;
    287 		width = maxX - minX;
    288 		y = minY;
    289 		height = maxY - minY;
    290 		return this;
    291 	}
    292 
    293 	/** Calculates the aspect ratio ( width / height ) of this rectangle
    294 	 * @return the aspect ratio of this rectangle. Returns Float.NaN if height is 0 to avoid ArithmeticException */
    295 	public float getAspectRatio () {
    296 		return (height == 0) ? Float.NaN : width / height;
    297 	}
    298 
    299 	/** Calculates the center of the rectangle. Results are located in the given Vector2
    300 	 * @param vector the Vector2 to use
    301 	 * @return the given vector with results stored inside */
    302 	public Vector2 getCenter (Vector2 vector) {
    303 		vector.x = x + width / 2;
    304 		vector.y = y + height / 2;
    305 		return vector;
    306 	}
    307 
    308 	/** Moves this rectangle so that its center point is located at a given position
    309 	 * @param x the position's x
    310 	 * @param y the position's y
    311 	 * @return this for chaining */
    312 	public Rectangle setCenter (float x, float y) {
    313 		setPosition(x - width / 2, y - height / 2);
    314 		return this;
    315 	}
    316 
    317 	/** Moves this rectangle so that its center point is located at a given position
    318 	 * @param position the position
    319 	 * @return this for chaining */
    320 	public Rectangle setCenter (Vector2 position) {
    321 		setPosition(position.x - width / 2, position.y - height / 2);
    322 		return this;
    323 	}
    324 
    325 	/** Fits this rectangle around another rectangle while maintaining aspect ratio. This scales and centers the rectangle to the
    326 	 * other rectangle (e.g. Having a camera translate and scale to show a given area)
    327 	 * @param rect the other rectangle to fit this rectangle around
    328 	 * @return this rectangle for chaining
    329 	 * @see Scaling */
    330 	public Rectangle fitOutside (Rectangle rect) {
    331 		float ratio = getAspectRatio();
    332 
    333 		if (ratio > rect.getAspectRatio()) {
    334 			// Wider than tall
    335 			setSize(rect.height * ratio, rect.height);
    336 		} else {
    337 			// Taller than wide
    338 			setSize(rect.width, rect.width / ratio);
    339 		}
    340 
    341 		setPosition((rect.x + rect.width / 2) - width / 2, (rect.y + rect.height / 2) - height / 2);
    342 		return this;
    343 	}
    344 
    345 	/** Fits this rectangle into another rectangle while maintaining aspect ratio. This scales and centers the rectangle to the
    346 	 * other rectangle (e.g. Scaling a texture within a arbitrary cell without squeezing)
    347 	 * @param rect the other rectangle to fit this rectangle inside
    348 	 * @return this rectangle for chaining
    349 	 * @see Scaling */
    350 	public Rectangle fitInside (Rectangle rect) {
    351 		float ratio = getAspectRatio();
    352 
    353 		if (ratio < rect.getAspectRatio()) {
    354 			// Taller than wide
    355 			setSize(rect.height * ratio, rect.height);
    356 		} else {
    357 			// Wider than tall
    358 			setSize(rect.width, rect.width / ratio);
    359 		}
    360 
    361 		setPosition((rect.x + rect.width / 2) - width / 2, (rect.y + rect.height / 2) - height / 2);
    362 		return this;
    363 	}
    364 
    365 	/** Converts this {@code Rectangle} to a string in the format {@code [x,y,width,height]}.
    366 	 * @return a string representation of this object. */
    367 	public String toString () {
    368 		return "[" + x + "," + y + "," + width + "," + height + "]";
    369 	}
    370 
    371 	/** Sets this {@code Rectangle} to the value represented by the specified string according to the format of {@link #toString()}
    372 	 * .
    373 	 * @param v the string.
    374 	 * @return this rectangle for chaining */
    375 	public Rectangle fromString (String v) {
    376 		int s0 = v.indexOf(',', 1);
    377 		int s1 = v.indexOf(',', s0 + 1);
    378 		int s2 = v.indexOf(',', s1 + 1);
    379 		if (s0 != -1 && s1 != -1 && s2 != -1 && v.charAt(0) == '[' && v.charAt(v.length() - 1) == ']') {
    380 			try {
    381 				float x = Float.parseFloat(v.substring(1, s0));
    382 				float y = Float.parseFloat(v.substring(s0 + 1, s1));
    383 				float width = Float.parseFloat(v.substring(s1 + 1, s2));
    384 				float height = Float.parseFloat(v.substring(s2 + 1, v.length() - 1));
    385 				return this.set(x, y, width, height);
    386 			} catch (NumberFormatException ex) {
    387 				// Throw a GdxRuntimeException
    388 			}
    389 		}
    390 		throw new GdxRuntimeException("Malformed Rectangle: " + v);
    391 	}
    392 
    393 	public float area () {
    394 		return this.width * this.height;
    395 	}
    396 
    397 	public float perimeter () {
    398 		return 2 * (this.width + this.height);
    399 	}
    400 
    401 	public int hashCode () {
    402 		final int prime = 31;
    403 		int result = 1;
    404 		result = prime * result + NumberUtils.floatToRawIntBits(height);
    405 		result = prime * result + NumberUtils.floatToRawIntBits(width);
    406 		result = prime * result + NumberUtils.floatToRawIntBits(x);
    407 		result = prime * result + NumberUtils.floatToRawIntBits(y);
    408 		return result;
    409 	}
    410 
    411 	public boolean equals (Object obj) {
    412 		if (this == obj) return true;
    413 		if (obj == null) return false;
    414 		if (getClass() != obj.getClass()) return false;
    415 		Rectangle other = (Rectangle)obj;
    416 		if (NumberUtils.floatToRawIntBits(height) != NumberUtils.floatToRawIntBits(other.height)) return false;
    417 		if (NumberUtils.floatToRawIntBits(width) != NumberUtils.floatToRawIntBits(other.width)) return false;
    418 		if (NumberUtils.floatToRawIntBits(x) != NumberUtils.floatToRawIntBits(other.x)) return false;
    419 		if (NumberUtils.floatToRawIntBits(y) != NumberUtils.floatToRawIntBits(other.y)) return false;
    420 		return true;
    421 	}
    422 
    423 }
    424