1 // Copyright 2006 The Closure Library Authors. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
16 * @fileoverview A utility class for representing a numeric box.
20 goog.provide('goog.math.Box');
22 goog.require('goog.math.Coordinate');
27 * Class for representing a box. A box is specified as a top, right, bottom,
28 * and left. A box is useful for representing margins and padding.
30 * This class assumes 'screen coordinates': larger Y coordinates are further
31 * from the top of the screen.
33 * @param {number} top Top.
34 * @param {number} right Right.
35 * @param {number} bottom Bottom.
36 * @param {number} left Left.
39 goog.math.Box = function(top, right, bottom, left) {
67 * Creates a Box by bounding a collection of goog.math.Coordinate objects
68 * @param {...goog.math.Coordinate} var_args Coordinates to be included inside
70 * @return {!goog.math.Box} A Box containing all the specified Coordinates.
72 goog.math.Box.boundingBox = function(var_args) {
73 var box = new goog.math.Box(arguments[0].y, arguments[0].x,
74 arguments[0].y, arguments[0].x);
75 for (var i = 1; i < arguments.length; i++) {
76 var coord = arguments[i];
77 box.top = Math.min(box.top, coord.y);
78 box.right = Math.max(box.right, coord.x);
79 box.bottom = Math.max(box.bottom, coord.y);
80 box.left = Math.min(box.left, coord.x);
87 * @return {number} width The width of this Box.
89 goog.math.Box.prototype.getWidth = function() {
90 return this.right - this.left;
95 * @return {number} height The height of this Box.
97 goog.math.Box.prototype.getHeight = function() {
98 return this.bottom - this.top;
103 * Creates a copy of the box with the same dimensions.
104 * @return {!goog.math.Box} A clone of this Box.
106 goog.math.Box.prototype.clone = function() {
107 return new goog.math.Box(this.top, this.right, this.bottom, this.left);
113 * Returns a nice string representing the box.
114 * @return {string} In the form (50t, 73r, 24b, 13l).
117 goog.math.Box.prototype.toString = function() {
118 return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
125 * Returns whether the box contains a coordinate or another box.
127 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
128 * @return {boolean} Whether the box contains the coordinate or other box.
130 goog.math.Box.prototype.contains = function(other) {
131 return goog.math.Box.contains(this, other);
136 * Expands box with the given margins.
138 * @param {number|goog.math.Box} top Top margin or box with all margins.
139 * @param {number=} opt_right Right margin.
140 * @param {number=} opt_bottom Bottom margin.
141 * @param {number=} opt_left Left margin.
142 * @return {!goog.math.Box} A reference to this Box.
144 goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
146 if (goog.isObject(top)) {
148 this.right += top.right;
149 this.bottom += top.bottom;
150 this.left -= top.left;
153 this.right += opt_right;
154 this.bottom += opt_bottom;
155 this.left -= opt_left;
163 * Expand this box to include another box.
164 * NOTE(user): This is used in code that needs to be very fast, please don't
165 * add functionality to this function at the expense of speed (variable
166 * arguments, accepting multiple argument types, etc).
167 * @param {goog.math.Box} box The box to include in this one.
169 goog.math.Box.prototype.expandToInclude = function(box) {
170 this.left = Math.min(this.left, box.left);
171 this.top = Math.min(this.top, box.top);
172 this.right = Math.max(this.right, box.right);
173 this.bottom = Math.max(this.bottom, box.bottom);
178 * Compares boxes for equality.
179 * @param {goog.math.Box} a A Box.
180 * @param {goog.math.Box} b A Box.
181 * @return {boolean} True iff the boxes are equal, or if both are null.
183 goog.math.Box.equals = function(a, b) {
190 return a.top == b.top && a.right == b.right &&
191 a.bottom == b.bottom && a.left == b.left;
196 * Returns whether a box contains a coordinate or another box.
198 * @param {goog.math.Box} box A Box.
199 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
200 * @return {boolean} Whether the box contains the coordinate or other box.
202 goog.math.Box.contains = function(box, other) {
203 if (!box || !other) {
207 if (other instanceof goog.math.Box) {
208 return other.left >= box.left && other.right <= box.right &&
209 other.top >= box.top && other.bottom <= box.bottom;
212 // other is a Coordinate.
213 return other.x >= box.left && other.x <= box.right &&
214 other.y >= box.top && other.y <= box.bottom;
219 * Returns the relative x position of a coordinate compared to a box. Returns
220 * zero if the coordinate is inside the box.
222 * @param {goog.math.Box} box A Box.
223 * @param {goog.math.Coordinate} coord A Coordinate.
224 * @return {number} The x position of {@code coord} relative to the nearest
225 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
227 goog.math.Box.relativePositionX = function(box, coord) {
228 if (coord.x < box.left) {
229 return coord.x - box.left;
230 } else if (coord.x > box.right) {
231 return coord.x - box.right;
238 * Returns the relative y position of a coordinate compared to a box. Returns
239 * zero if the coordinate is inside the box.
241 * @param {goog.math.Box} box A Box.
242 * @param {goog.math.Coordinate} coord A Coordinate.
243 * @return {number} The y position of {@code coord} relative to the nearest
244 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
246 goog.math.Box.relativePositionY = function(box, coord) {
247 if (coord.y < box.top) {
248 return coord.y - box.top;
249 } else if (coord.y > box.bottom) {
250 return coord.y - box.bottom;
257 * Returns the distance between a coordinate and the nearest corner/side of a
258 * box. Returns zero if the coordinate is inside the box.
260 * @param {goog.math.Box} box A Box.
261 * @param {goog.math.Coordinate} coord A Coordinate.
262 * @return {number} The distance between {@code coord} and the nearest
263 * corner/side of {@code box}, or zero if {@code coord} is inside
266 goog.math.Box.distance = function(box, coord) {
267 var x = goog.math.Box.relativePositionX(box, coord);
268 var y = goog.math.Box.relativePositionY(box, coord);
269 return Math.sqrt(x * x + y * y);
274 * Returns whether two boxes intersect.
276 * @param {goog.math.Box} a A Box.
277 * @param {goog.math.Box} b A second Box.
278 * @return {boolean} Whether the boxes intersect.
280 goog.math.Box.intersects = function(a, b) {
281 return (a.left <= b.right && b.left <= a.right &&
282 a.top <= b.bottom && b.top <= a.bottom);
287 * Returns whether two boxes would intersect with additional padding.
289 * @param {goog.math.Box} a A Box.
290 * @param {goog.math.Box} b A second Box.
291 * @param {number} padding The additional padding.
292 * @return {boolean} Whether the boxes intersect.
294 goog.math.Box.intersectsWithPadding = function(a, b, padding) {
295 return (a.left <= b.right + padding && b.left <= a.right + padding &&
296 a.top <= b.bottom + padding && b.top <= a.bottom + padding);
301 * Rounds the fields to the next larger integer values.
303 * @return {!goog.math.Box} This box with ceil'd fields.
305 goog.math.Box.prototype.ceil = function() {
306 this.top = Math.ceil(this.top);
307 this.right = Math.ceil(this.right);
308 this.bottom = Math.ceil(this.bottom);
309 this.left = Math.ceil(this.left);
315 * Rounds the fields to the next smaller integer values.
317 * @return {!goog.math.Box} This box with floored fields.
319 goog.math.Box.prototype.floor = function() {
320 this.top = Math.floor(this.top);
321 this.right = Math.floor(this.right);
322 this.bottom = Math.floor(this.bottom);
323 this.left = Math.floor(this.left);
329 * Rounds the fields to nearest integer values.
331 * @return {!goog.math.Box} This box with rounded fields.
333 goog.math.Box.prototype.round = function() {
334 this.top = Math.round(this.top);
335 this.right = Math.round(this.right);
336 this.bottom = Math.round(this.bottom);
337 this.left = Math.round(this.left);
343 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
344 * is given, then the left and right values are translated by the coordinate's
345 * x value and the top and bottom values are translated by the coordinate's y
346 * value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
347 * and y dimension values.
349 * @param {number|goog.math.Coordinate} tx The value to translate the x
350 * dimension values by or the the coordinate to translate this box by.
351 * @param {number=} opt_ty The value to translate y dimension values by.
352 * @return {!goog.math.Box} This box after translating.
354 goog.math.Box.prototype.translate = function(tx, opt_ty) {
355 if (tx instanceof goog.math.Coordinate) {
363 if (goog.isNumber(opt_ty)) {
365 this.bottom += opt_ty;
373 * Scales this coordinate by the given scale factors. The x and y dimension
374 * values are scaled by {@code sx} and {@code opt_sy} respectively.
375 * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
377 * @param {number} sx The scale factor to use for the x dimension.
378 * @param {number=} opt_sy The scale factor to use for the y dimension.
379 * @return {!goog.math.Box} This box after scaling.
381 goog.math.Box.prototype.scale = function(sx, opt_sy) {
382 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;