1 /**
  2  * The MIT License (MIT)
  3  *
  4  * Copyright (c) 2016 DeNA Co., Ltd.
  5  *
  6  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7  * of this software and associated documentation files (the "Software"), to deal
  8  * in the Software without restriction, including without limitation the rights
  9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  * copies of the Software, and to permit persons to whom the Software is
 11  * furnished to do so, subject to the following conditions:
 12  *
 13  * The above copyright notice and this permission notice shall be included in
 14  * all copies or substantial portions of the Software.
 15  *
 16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22  * SOFTWARE.
 23  */
 24 
 25 /// <reference path="base.js"/>
 26 /// <reference path="object.js"/>
 27 /// <reference path="region.js"/>
 28 
 29 /**
 30  * A class representing a bounding box.
 31  * @extends {createjs.Object}
 32  * @implements {createjs.Region}
 33  * @constructor
 34  */
 35 createjs.BoundingBox = function() {
 36   createjs.Object.call(this);
 37 };
 38 createjs.inherits('BoundingBox', createjs.BoundingBox, createjs.Object);
 39 
 40 /**
 41  * Returns a clone of the specified box.
 42  * @param {createjs.BoundingBox} box
 43  * @return {createjs.BoundingBox}
 44  */
 45 createjs.BoundingBox.clone = function(box) {
 46   /// <param type="createjs.BoundingBox" name="box"/>
 47   /// <returns type="createjs.BoundingBox"/>
 48   var clone = new createjs.BoundingBox();
 49   clone.minX = box.minX;
 50   clone.minY = box.minY;
 51   clone.maxX = box.maxX;
 52   clone.maxY = box.maxY;
 53   return clone;
 54 };
 55 
 56 /**
 57  * The left position of this bounding box.
 58  * @type {number}
 59  */
 60 createjs.BoundingBox.prototype.minX = 10000;
 61 
 62 /**
 63  * The top position of this bounding box.
 64  * @type {number}
 65  */
 66 createjs.BoundingBox.prototype.minY = 10000;
 67 
 68 /**
 69  * The right position of this bounding box.
 70  * @type {number}
 71  */
 72 createjs.BoundingBox.prototype.maxX = -10000;
 73 
 74 /**
 75  * The bottom position of this bounding box.
 76  * @type {number}
 77  */
 78 createjs.BoundingBox.prototype.maxY = -10000;
 79 
 80 /**
 81  * Resets all properties of this object to the initial state.
 82  * @const
 83  */
 84 createjs.BoundingBox.prototype.reset = function() {
 85   this.minX = 10000;
 86   this.minY = 10000;
 87   this.maxX = -10000;
 88   this.maxY = -10000;
 89 };
 90 
 91 /**
 92  * Returns the x position of this bounding box.
 93  * @return {number}
 94  * @const
 95  */
 96 createjs.BoundingBox.prototype.getLeft = function() {
 97   /// <returns type="number"/>
 98   return this.minX;
 99 };
100 
101 /**
102  * Returns the y position of this bounding box.
103  * @return {number}
104  * @const
105  */
106 createjs.BoundingBox.prototype.getTop = function() {
107   /// <returns type="number"/>
108   return this.minY;
109 };
110 
111 /**
112  * Returns the width of this bounding box.
113  * @return {number}
114  * @const
115  */
116 createjs.BoundingBox.prototype.getWidth = function() {
117   /// <returns type="number"/>
118   return this.maxX - this.minX;
119 };
120 
121 /**
122  * Returns the height of this bounding box.
123  * @return {number}
124  * @const
125  */
126 createjs.BoundingBox.prototype.getHeight = function() {
127   /// <returns type="number"/>
128   return this.maxY - this.minY;
129 };
130 
131 /**
132  * Returns whether this box is an empty one.
133  * @param {createjs.BoundingBox} box
134  * @return {boolean}
135  * @const
136  */
137 createjs.BoundingBox.prototype.isEqual = function(box) {
138   /// <param type="createjs.BoundingBox" name="box"/>
139   /// <returns type="boolean"/>
140   return this.minX == box.minX && this.maxX == box.maxX &&
141       this.minY == box.minY && this.maxY == box.maxY;
142 };
143 
144 /**
145  * Updates this bounding box.
146  * @param {number} x
147  * @param {number} y
148  * @const
149  */
150 createjs.BoundingBox.prototype.update = function(x, y) {
151   /// <param type="number" name="x"/>
152   /// <param type="number" name="y"/>
153   this.minX = createjs.min(x, this.minX);
154   this.minY = createjs.min(y, this.minY);
155   this.maxX = createjs.max(x, this.maxX);
156   this.maxY = createjs.max(y, this.maxY);
157 };
158 
159 /**
160  * Inflates this bounding box.
161  * @param {createjs.BoundingBox} box
162  * @const
163  */
164 createjs.BoundingBox.prototype.inflate = function(box) {
165   /// <param type="createjs.BoundingBox" name="box"/>
166   this.minX = createjs.min(box.minX, this.minX);
167   this.minY = createjs.min(box.minY, this.minY);
168   this.maxX = createjs.max(box.maxX, this.maxX);
169   this.maxY = createjs.max(box.maxY, this.maxY);
170 };
171 
172 /**
173  * Adds the specified margin to this box.
174  * @param {number} margin
175  * @const
176  */
177 createjs.BoundingBox.prototype.addMargin = function(margin) {
178   /// <param type="number" name="margin"/>
179   this.minX -= margin;
180   this.minY -= margin;
181   this.maxX += margin;
182   this.maxY += margin;
183   this.maxX = this.minX + createjs.truncate(this.maxX - this.minX);
184   this.maxY = this.minY + createjs.truncate(this.maxY - this.minY);
185 };
186 
187 /**
188  * Returns whether this box contains the specified one.
189  * @param {createjs.BoundingBox} box
190  * @return {boolean}
191  * @const
192  */
193 createjs.BoundingBox.prototype.containBox = function(box) {
194   /// <param type="createjs.BoundingBox" name="box"/>
195   /// <returns type="boolean"/>
196   createjs.assert(!this.isEmpty() && !box.isEmpty());
197   return this.minX <= box.minX && box.maxX <= this.maxX &&
198       this.minY <= box.minY && box.maxY <= this.maxY;
199 };
200 
201 /**
202  * Returns whether this box has intersection with the specified one.
203  * @param {createjs.BoundingBox} box
204  * @return {boolean}
205  * @const
206  */
207 createjs.BoundingBox.prototype.hasIntersection = function(box) {
208   /// <param type="createjs.BoundingBox" name="box"/>
209   /// <returns type="boolean"/>
210   // Two convexes have intersection only when there are not any lines in between
211   // them separated by a gap as noted by the hyperplane-separation theorem.
212   // Especially for two rectangles, their separation axises become an X-axis and
213   // a Y-axis. So, these rectangles have intersection when there are not any
214   // gaps in their projections both to the X-axis and to the Y-axis.
215   var maxMinX = createjs.max(this.minX, box.minX);
216   var minMaxX = createjs.min(this.maxX, box.maxX);
217   var maxMinY = createjs.max(this.minY, box.minY);
218   var minMaxY = createjs.min(this.maxY, box.maxY);
219   return maxMinX < minMaxX && maxMinY < minMaxY;
220 };
221 
222 /**
223  * Returns whether this box has intersections with the specified rectangle
224  * (0,0)-(width,height). This method is used by a renderer to determine if it
225  * needs to redraw its objects.
226  * @param {number} width
227  * @param {number} height
228  * @return {boolean}
229  * @const
230  */
231 createjs.BoundingBox.prototype.isDirty = function(width, height) {
232   /// <param type="number" name="width"/>
233   /// <param type="number" name="height"/>
234   /// <returns type="boolean"/>
235   if (this.isEmpty() ||
236       this.maxX <= 0 || width <= this.minX ||
237       this.maxY <= 0 || height <= this.minY) {
238     return false;
239   }
240   return true;
241 };
242 
243 /**
244  * Returns whether this box is a subset of the specified rectangle
245  * (0,0)-(width,height). This method is used by a renderer to calculate its
246  * clipping rectangle.
247  * @param {number} width
248  * @param {number} height
249  * @return {boolean}
250  * @const
251  */
252 createjs.BoundingBox.prototype.needClip = function(width, height) {
253   /// <param type="number" name="width"/>
254   /// <param type="number" name="height"/>
255   /// <returns type="boolean"/>
256   if (this.minX <= 0 && width <= this.maxX &&
257       this.maxY <= 0 && height <= this.maxY) {
258     return false;
259   }
260   // Snap this bounding box to a pixel boundary.
261   this.minX = (this.minX <= 0) ? 0 : createjs.floor(this.minX);
262   this.minY = (this.minY <= 0) ? 0 : createjs.floor(this.minY);
263   this.maxX = createjs.ceil(this.maxX);
264   this.maxY = createjs.ceil(this.maxY);
265   return true;
266 };
267 
268 /** @override */
269 createjs.BoundingBox.prototype.isEmpty = function() {
270   /// <returns type="boolean"/>
271   return this.maxX <= this.minX;
272 };
273 
274 /** @override */
275 createjs.BoundingBox.prototype.contain = function(x, y) {
276   /// <param type="number" name="x"/>
277   /// <param type="number" name="y"/>
278   /// <returns type="boolean"/>
279   return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
280 };
281