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 };