1 /** 2 * The Render Engine 3 * Rectangle2D 4 * 5 * @fileoverview A Rectangle2D class 6 * 7 * @author: Brett Fattori (brettf@renderengine.com) 8 * @author: $Author: bfattori $ 9 * @version: $Revision: 1555 $ 10 * 11 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 * of this software and associated documentation files (the "Software"), to deal 15 * in the Software without restriction, including without limitation the rights 16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 * copies of the Software, and to permit persons to whom the Software is 18 * furnished to do so, subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be included in 21 * all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 * THE SOFTWARE. 30 * 31 */ 32 33 // The class this file defines and its required classes 34 R.Engine.define({ 35 "class":"R.math.Rectangle2D", 36 "requires":[ 37 "R.math.PooledMathObject", 38 "R.math.Point2D", 39 "R.math.Math2D" 40 ] 41 }); 42 43 /** 44 * @class A 2D rectangle class with helpful manipulation methods. 45 * @extends R.math.PooledMathObject 46 * @constructor 47 * @param x {R.math.Rectangle2D|Number} A rectangle to clone, or the top-left X coordinate 48 * @param y {Number} The top-left Y coordinate 49 * @param width {Number} the width of the rectangle 50 * @param height {Number} The height of the rectangle 51 * @description Create a rectangle object specifying the X and Y position and 52 * the width and height. 53 */ 54 R.math.Rectangle2D = function () { 55 "use strict"; 56 return R.math.PooledMathObject.extend(/** @scope R.math.Rectangle2D.prototype */{ 57 58 x:0, 59 y:0, 60 w:0, 61 h:0, 62 r:0, 63 b:0, 64 topLeft:null, 65 bottomRight:null, 66 dims:null, 67 center:null, 68 __RECTANGLE2D:true, 69 70 /** @private */ 71 constructor:function (x, y, width, height) { 72 this.base("Rectangle2D"); 73 this.__RECTANGLE2D = true; 74 75 this.topLeft = R.math.Point2D.create(0, 0); 76 this.bottomRight = R.math.Point2D.create(0, 0); 77 this.dims = R.math.Point2D.create(0, 0); 78 this.center = R.math.Point2D.create(0, 0); 79 80 this.set(x, y, width, height); 81 return this; 82 }, 83 84 /** 85 * Destroy the rectangle instance 86 */ 87 destroy:function () { 88 this.x = 0; 89 this.y = 0; 90 this.w = 0; 91 this.h = 0; 92 this.b = 0; 93 this.r = 0; 94 this.topLeft.destroy(); 95 this.bottomRight.destroy(); 96 this.dims.destroy(); 97 this.center.destroy(); 98 this.base(); 99 }, 100 101 /** 102 * Release the rectangle back into the pool for reuse 103 */ 104 release:function () { 105 this.base(); 106 this.topLeft = null; 107 this.dims = null; 108 this.bottomRight = null; 109 this.center = null; 110 }, 111 112 /** 113 * @private 114 */ 115 _upd:function () { 116 this.r = this.x + this.w; 117 this.b = this.y + this.h; 118 this.center.set(this.x + (this.w * 0.5), this.y + (this.h * 0.5)); 119 }, 120 121 /** 122 * Set the values of this rectangle. 123 * 124 * @param x {Array|Number|R.math.Rectangle2D} An optional value to initialize the X coordinate of the rectangle, or a rectangle to clone 125 * @param y {Number} An optional value to initialize the Y coordinate of the rectangle 126 * @param width {Number} An optional value to initialize the width of the rectangle 127 * @param height {Number} An optional value to initialize the height of the rectangle 128 */ 129 set:function (x, y, width, height) { 130 if (x.length && x.splice && x.shift) { 131 this.x = x[0]; 132 this.y = x[1]; 133 this.w = x[2]; 134 this.h = x[3]; 135 } else if (x.__RECTANGLE2D) { 136 this.x = x.x; 137 this.y = x.y; 138 this.w = x.w; 139 this.h = x.h; 140 } else if (x.__POINT2D) { 141 this.x = 0; 142 this.y = 0; 143 this.w = x.x; 144 this.h = w.y; 145 } else { 146 this.x = x; 147 this.y = y; 148 this.w = width; 149 this.h = height; 150 } 151 this._upd(); 152 return this; 153 }, 154 155 /** 156 * Get an object with the elements containing left, top, width, height, right 157 * and bottom as the elements x, y, w, h, r, and b. 158 * 159 * @return {Object} An object with the specified elements 160 * @deprecated 161 */ 162 get:function () { 163 return {x:this.x, y:this.y, w:this.w, h:this.h, r:this.r, b:this.b}; 164 }, 165 166 /** 167 * Returns <tt>true</tt> if this rectangle is equal to the specified rectangle. 168 * 169 * @param rect {R.math.Rectangle2D} The rectangle to compare to 170 * @return {Boolean} <tt>true</tt> if the two rectangles are equal 171 */ 172 equals:function (rect) { 173 return this.x == rect.x && this.y == rect.y && 174 this.w == rect.w && this.h == rect.h; 175 }, 176 177 /** 178 * A mutator method that offsets this rectangle by the given amount in the X and Y axis. 179 * The first parameter can be either a point, or the value for the X axis. If the X axis is 180 * specified, the second parameter should be the amount to offset in the Y axis. 181 * 182 * @param offsetPtOrX {R.math.Point2D|int} Either a {@link R.math.Point} which contains the offset in X and Y, or an integer 183 * representing the offset in the X axis. 184 * @param offsetY {int} If <code>offsetPtOrX</code> is an integer value for the offset in the X axis, this should be 185 * the offset along the Y axis. 186 * @return {R.math.Rectangle2D} This rectangle 187 */ 188 offset:function (offsetPtOrX, offsetY) { 189 var offs = R.math.Point2D.create(0, 0); 190 if (offsetPtOrX.__POINT2D) { 191 offs.set(offsetPtOrX); 192 } else { 193 offs.set(offsetPtOrX, offsetY); 194 } 195 196 this.x += offs.x; 197 this.y += offs.y; 198 offs.destroy(); 199 this._upd(); 200 return this; 201 }, 202 203 /** 204 * Shrink the size of the rectangle by the amounts given. 205 * @param pixelsX {Number} The pixels to shrink the rectangle along the X axis, or both. 206 * @param [pixelsY] {Number} If defined, the pixels to shrink the rectangle along the Y axis. 207 */ 208 shrink:function (pixelsX, pixelsY) { 209 pixelsY = pixelsY || pixelsX; 210 this.w -= pixelsX; 211 this.h -= pixelsY; 212 this._upd(); 213 return this; 214 }, 215 216 /** 217 * Grow the size of the rectangle by the amounts given. 218 * @param pixelsX {Number} The pixels to grow the rectangle along the X axis, or both. 219 * @param [pixelsY] {Number} If defined, the pixels to grow the rectangle along the Y axis. 220 */ 221 grow:function (pixelsX, pixelsY) { 222 return this.shrink((-1 * pixelsX), (-1 * pixelsY)); 223 }, 224 225 /** 226 * A mutator method that multiplies the top left of this rectangle with the 227 * point specified. 228 * @param point {R.math.Point2D} 229 */ 230 convolve:function (point) { 231 this.x *= point.x; 232 this.y *= point.y; 233 this._upd(); 234 return this; 235 }, 236 237 /** 238 * A mutator method that divides the top left of this rectangle with the 239 * point specified. 240 * @param point {R.math.Point2D} 241 */ 242 convolveInverse:function (point) { 243 this.x /= point.x; 244 this.y /= point.y; 245 this._upd(); 246 return this; 247 }, 248 249 /** 250 * A mutator method that adds the point to the top left of this rectangle. 251 * @param point {R.math.Point2D} 252 */ 253 add:function (point) { 254 this.x += point.x; 255 this.y += point.y; 256 this._upd(); 257 return this; 258 }, 259 260 /** 261 * A mutator method that subtracts the point from the top left of this rectangle. 262 * @param point {R.math.Point2D} 263 */ 264 sub:function (point) { 265 this.x -= point.x; 266 this.y -= point.y; 267 this._upd(); 268 return this; 269 }, 270 271 /** 272 * Set the top left of this rectangle to the point, or coordinates specified. 273 * 274 * @param ptOrX {R.math.Point2D|Number} The top left {@link R.math.Point2D}, or the X coordinate 275 * @param y {Number} If the top left wasn't specified as the first argument, this is the Y coordinate 276 */ 277 setTopLeft:function (ptOrX, y) { 278 if (ptOrX.__POINT2D) { 279 this.x = ptOrX.x; 280 this.y = ptOrX.y; 281 } else { 282 this.x = ptOrX; 283 this.y = y; 284 } 285 this._upd(); 286 }, 287 288 /** 289 * Set the width and height of this rectangle using the point, or coordinates specified. 290 * @param ptOrX {Point2D|Number} A {@link R.math.Point2D}, or the X coordinate 291 * @param [y] {Number} If the top left isn't a point, this is the Y coordinate 292 */ 293 setDims:function (ptOrX, y) { 294 if (ptOrX.__POINT2D) { 295 this.w = ptOrX.x; 296 this.h = ptOrX.y; 297 } else { 298 this.w = ptOrX; 299 this.h = y; 300 } 301 this._upd(); 302 }, 303 304 /** 305 * Set the width of the rectangle. 306 * 307 * @param width {Number} The new width of the rectangle 308 */ 309 setWidth:function (width) { 310 this.w = width; 311 this._upd(); 312 }, 313 314 /** 315 * Set the height of the rectangle 316 * 317 * @param height {Number} The new height of the rectangle 318 */ 319 setHeight:function (height) { 320 this.h = height; 321 this._upd(); 322 }, 323 324 /** 325 * Determine if this rectangle intersects another rectangle. 326 * 327 * @param rect A {@link R.math.Rectangle2D} to compare against 328 * @return {Boolean} <tt>true</tt> if the two rectangles intersect. 329 */ 330 isIntersecting:function (rect) { 331 return !(this.r < rect.x || 332 this.x > rect.r || 333 this.y > rect.b || 334 this.b < rect.y); 335 }, 336 337 /** 338 * Determine if this rectangle is contained within the specified rectangle. 339 * 340 * @param rect A {@link R.math.Rectangle2D} to compare against 341 * @return {Boolean} <tt>true</tt> if the this rectangle is fully contained in the specified rectangle. 342 */ 343 isContained:function (rect) { 344 return ((this.x >= rect.x) && 345 (this.y >= rect.y) && 346 (this.r <= rect.r) && 347 (this.b <= rect.b)); 348 }, 349 350 /** 351 * Determine if this rectangle contains the specified rectangle. 352 * 353 * @param rect A {@link R.math.Rectangle2D} to compare against 354 * @return {Boolean} <tt>true</tt> if the rectangle is fully contained within this rectangle. 355 */ 356 containsRect:function (rect) { 357 return rect.isContained(this); 358 }, 359 360 /** 361 * Returns <tt>true</tt> if this rectangle contains the specified point. 362 * 363 * @param point {R.math.Point} The point to test 364 * @return {Boolean} <tt>true</tt> if the point is within this rectangle 365 */ 366 containsPoint:function (point) { 367 return (point.x >= this.x && 368 point.y >= this.y && 369 point.x <= this.r && 370 point.y <= this.b); 371 }, 372 373 /** 374 * Returns a {@link R.math.Point2D} that contains the center point of this rectangle. 375 * 376 * @return {R.math.Point2D} The center point of the rectangle 377 */ 378 getCenter:function () { 379 return this.center; 380 }, 381 382 /** 383 * Returns the half length of the width dimension of this rectangle 384 * @return {Number} The half-width 385 */ 386 getHalfWidth:function () { 387 return this.len_x() * 0.5; 388 }, 389 390 /** 391 * Returns the half length of the height dimension of this rectangle 392 * @return {Number} The half-height 393 */ 394 getHalfHeight:function () { 395 return this.len_y() * 0.5; 396 }, 397 398 /** 399 * Returns the positive length of this rectangle, along the X axis. 400 * 401 * @return {Number} 402 */ 403 len_x:function () { 404 return Math.abs(this.w); 405 }, 406 407 /** 408 * Returns the positive length of this rectangle, along the Y axis. 409 * 410 * @return {Number} 411 */ 412 len_y:function () { 413 return Math.abs(this.h); 414 }, 415 416 /** 417 * Gets a {@link R.math.Point2D} representing the top-left corner of this rectangle. 418 * @return {R.math.Point2D} 419 */ 420 getTopLeft:function () { 421 this.topLeft.set(this.x, this.y); 422 return this.topLeft; 423 }, 424 425 /** 426 * Gets a {@link R.math.Point2D) representing the width and height of this rectangle. 427 * @return {R.math.Point2D} 428 */ 429 getDims:function () { 430 this.dims.set(this.w, this.h); 431 return this.dims; 432 }, 433 434 /** 435 * Gets a {@link R.math.Point2D} representing the bottom-right corner of this rectangle. 436 * @return {R.math.Point2D} 437 */ 438 getBottomRight:function () { 439 this.bottomRight.set(this.r, this.b); 440 return this.bottomRight; 441 }, 442 443 /** 444 * Mutator method which will join this rectangle with another 445 * rectangle. Joining two rectangles will create a rectangle that 446 * would enclose both rectangles. It is best to see if two rectangles 447 * are overlapping before joining them, since joining two disjoint 448 * rectangles would enclose areas not contained in either. 449 * 450 * @param rect {R.math.Rectangle2D} The rectangle to join with 451 * @return {R.math.Rectangle2D} This rectangle 452 */ 453 join:function (rect) { 454 var x1 = this.x; 455 var x2 = this.x + this.w; 456 var x3 = rect.x; 457 var x4 = rect.x + rect.w; 458 var y1 = this.y; 459 var y2 = this.y + this.h; 460 var y3 = rect.y; 461 var y4 = rect.y + rect.h; 462 463 var x = Math2.MAX_INT, y = x, w = -Math2.MAX_INT, h = w; 464 if (x1 < x) x = x1; 465 if (x2 < x) x = x2; 466 if (x3 < x) x = x3; 467 if (x4 < x) x = x4; 468 if (x1 > w) w = x1; 469 if (x2 > w) w = x2; 470 if (x3 > w) w = x3; 471 if (x4 > w) w = x4; 472 if (y1 < y) y = y1; 473 if (y2 < y) y = y2; 474 if (y3 < y) y = y3; 475 if (y4 < y) y = y4; 476 if (y1 > h) h = y1; 477 if (y2 > h) h = y2; 478 if (y3 > h) h = y3; 479 if (y4 > h) h = y4; 480 481 this.x = x; 482 this.y = y; 483 this.w = w - x; 484 this.h = h - y; 485 this._upd(); 486 return this; 487 }, 488 489 /** 490 * Returns a printable version of this object. 491 * @return {String} Formatted like "x,y [w,h]" 492 */ 493 toString:function () { 494 return (this.getTopLeft() + " [" + this.getDims() + "]"); 495 } 496 497 }, /** @scope R.math.Rectangle2D.prototype */{ 498 499 /** 500 * Return the classname of the this object 501 * @return {String} "R.math.Rectangle2D" 502 */ 503 getClassName:function () { 504 return "R.math.Rectangle2D"; 505 } 506 507 }); 508 509 };