1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 SC.mixin( /** @scope SC */ {
  8   /** A Point at {0,0} */
  9   ZERO_POINT: { x: 0, y: 0 },
 10 
 11   /** Check if the given point is inside the rect. */
 12   pointInRect: function(point, f) {
 13     return  (point.x >= SC.minX(f)) &&
 14             (point.y >= SC.minY(f)) &&
 15             (point.x <= SC.maxX(f)) &&
 16             (point.y <= SC.maxY(f)) ;
 17   },
 18 
 19   /** Return true if the two frames match.  You can also pass only points or sizes.
 20 
 21     @param r1 {Rect} the first rect
 22     @param r2 {Rect} the second rect
 23     @param delta {Float} an optional delta that allows for rects that do not match exactly. Defaults to 0.1
 24     @returns {Boolean} true if rects match
 25    */
 26   rectsEqual: function(r1, r2, delta) {
 27     if (!r1 || !r2) return (r1 == r2) ;
 28     if (!delta && delta !== 0) delta = 0.1;
 29     if ((r1.y != r2.y) && (Math.abs(r1.y - r2.y) > delta)) return NO ;
 30     if ((r1.x != r2.x) && (Math.abs(r1.x - r2.x) > delta)) return NO ;
 31     if ((r1.width != r2.width) && (Math.abs(r1.width - r2.width) > delta)) return NO ;
 32     if ((r1.height != r2.height) && (Math.abs(r1.height - r2.height) > delta)) return NO ;
 33     return YES ;
 34   },
 35 
 36   /** Returns the insersection between two rectangles.
 37 
 38     @param r1 {Rect} The first rect
 39     @param r2 {Rect} the second rect
 40     @returns {Rect} the intersection rect.  width || height will be 0 if they do not interset.
 41   */
 42   intersectRects: function(r1, r2) {
 43     // find all four edges
 44     var ret = {
 45       x: Math.max(SC.minX(r1), SC.minX(r2)),
 46       y: Math.max(SC.minY(r1), SC.minY(r2)),
 47       width: Math.min(SC.maxX(r1), SC.maxX(r2)),
 48       height: Math.min(SC.maxY(r1), SC.maxY(r2))
 49     } ;
 50 
 51     // convert edges to w/h
 52     ret.width = Math.max(0, ret.width - ret.x) ;
 53     ret.height = Math.max(0, ret.height - ret.y) ;
 54     return ret ;
 55   },
 56 
 57   /** Returns the union between two rectangles
 58 
 59     @param r1 {Rect} The first rect
 60     @param r2 {Rect} The second rect
 61     @returns {Rect} The union rect.
 62   */
 63   unionRects: function(r1, r2) {
 64     // find all four edges
 65     var ret = {
 66       x: Math.min(SC.minX(r1), SC.minX(r2)),
 67       y: Math.min(SC.minY(r1), SC.minY(r2)),
 68       width: Math.max(SC.maxX(r1), SC.maxX(r2)),
 69       height: Math.max(SC.maxY(r1), SC.maxY(r2))
 70     } ;
 71 
 72     // convert edges to w/h
 73     ret.width = Math.max(0, ret.width - ret.x) ;
 74     ret.height = Math.max(0, ret.height - ret.y) ;
 75     return ret ;
 76   },
 77 
 78   /**
 79     Returns a copy of the passed rect, scaled by the specified scale, centered on the specified origin.
 80 
 81     @param {Rect} rect The rectangle to scale.
 82     @param {Number|Array|Hash} scale The scale (or [scaleX, scaleY], or { x: scaleX, y: scaleY}) to apply. Defaults to 1.
 83     @param {Number} originX The horizontal scale origin. Defaults to 0.5 (center).
 84     @param {Number} originY The vertical scale origin. Defaults to 0.5 (center).
 85     @returns {Rect} The scaled rect.
 86   */
 87   scaleRect: function(rect, scale, originX, originY) {
 88     // Defaults
 89     if (scale == null) scale = 1;
 90     if (originX == null) originX = 0.5;
 91     if (originY == null) originY = 0.5;
 92 
 93     // Gatekeep: Identity scale.
 94     if (scale === 1) return SC.cloneRect(rect);
 95 
 96     // Unpack scale.
 97     var scaleX, scaleY;
 98     switch (SC.typeOf(scale)) {
 99       case SC.T_ARRAY:
100         scaleX = scale[0];
101         scaleY = scale[1];
102         break;
103       case SC.T_HASH:
104         scaleX = scale.x;
105         scaleY = scale.y;
106         break;
107       default:
108         scaleX = scale;
109         scaleY = scale;
110         break;
111     }
112 
113     var scaledHeight = rect.height * scaleY,
114       scaledWidth = rect.width * scaleX,
115       dHeight = scaledHeight - rect.height,
116       dWidth = scaledWidth - rect.width;
117     
118     // X and Y positions change depending on the origin of the scale. For example, if the
119     // width scales down ten pixels and the origin is 50%, x will move five pixesl (10 * 0.5)
120     // to the right.
121     var scaledX = rect.x - (dWidth * originX),
122       scaledY = rect.y - (dHeight * originY);
123 
124     return {
125       height: scaledHeight,
126       width: scaledWidth,
127       x: scaledX,
128       y: scaledY
129     };
130   },
131 
132   /**
133     Duplicates the passed rect.
134 
135     This is faster than Object.clone().
136 
137     @param r {Rect} The rect to clone.
138     @returns {Rect} The cloned rect
139   */
140   cloneRect: function(r) {
141     return { x: r.x, y: r.y, width: r.width, height: r.height } ;
142   },
143 
144   /** Returns a string representation of the rect as {x, y, width, height}.
145 
146     @param r {Rect} The rect to stringify.
147     @returns {String} A string representation of the rect.
148   */
149   stringFromRect: function(r) {
150     if (!r) {
151       return "(null)";
152     }
153     else {
154       return '{ x:'+r.x+', y:'+r.y+', width:'+r.width+', height:'+r.height+' }';
155     }
156   }
157 
158 
159 });
160