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