Path: blob/trunk/third_party/closure/goog/math/box.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview A utility class for representing a numeric box.16*/171819goog.provide('goog.math.Box');2021goog.require('goog.asserts');22goog.require('goog.math.Coordinate');23242526/**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.29*30* This class assumes 'screen coordinates': larger Y coordinates are further31* from the top of the screen.32*33* @param {number} top Top.34* @param {number} right Right.35* @param {number} bottom Bottom.36* @param {number} left Left.37* @struct38* @constructor39*/40goog.math.Box = function(top, right, bottom, left) {41/**42* Top43* @type {number}44*/45this.top = top;4647/**48* Right49* @type {number}50*/51this.right = right;5253/**54* Bottom55* @type {number}56*/57this.bottom = bottom;5859/**60* Left61* @type {number}62*/63this.left = left;64};656667/**68* Creates a Box by bounding a collection of goog.math.Coordinate objects69* @param {...goog.math.Coordinate} var_args Coordinates to be included inside70* the box.71* @return {!goog.math.Box} A Box containing all the specified Coordinates.72*/73goog.math.Box.boundingBox = function(var_args) {74var box = new goog.math.Box(75arguments[0].y, arguments[0].x, arguments[0].y, arguments[0].x);76for (var i = 1; i < arguments.length; i++) {77box.expandToIncludeCoordinate(arguments[i]);78}79return box;80};818283/**84* @return {number} width The width of this Box.85*/86goog.math.Box.prototype.getWidth = function() {87return this.right - this.left;88};899091/**92* @return {number} height The height of this Box.93*/94goog.math.Box.prototype.getHeight = function() {95return this.bottom - this.top;96};979899/**100* Creates a copy of the box with the same dimensions.101* @return {!goog.math.Box} A clone of this Box.102*/103goog.math.Box.prototype.clone = function() {104return new goog.math.Box(this.top, this.right, this.bottom, this.left);105};106107108if (goog.DEBUG) {109/**110* Returns a nice string representing the box.111* @return {string} In the form (50t, 73r, 24b, 13l).112* @override113*/114goog.math.Box.prototype.toString = function() {115return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +116this.left + 'l)';117};118}119120121/**122* Returns whether the box contains a coordinate or another box.123*124* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.125* @return {boolean} Whether the box contains the coordinate or other box.126*/127goog.math.Box.prototype.contains = function(other) {128return goog.math.Box.contains(this, other);129};130131132/**133* Expands box with the given margins.134*135* @param {number|goog.math.Box} top Top margin or box with all margins.136* @param {number=} opt_right Right margin.137* @param {number=} opt_bottom Bottom margin.138* @param {number=} opt_left Left margin.139* @return {!goog.math.Box} A reference to this Box.140*/141goog.math.Box.prototype.expand = function(142top, opt_right, opt_bottom, opt_left) {143if (goog.isObject(top)) {144this.top -= top.top;145this.right += top.right;146this.bottom += top.bottom;147this.left -= top.left;148} else {149this.top -= /** @type {number} */ (top);150this.right += Number(opt_right);151this.bottom += Number(opt_bottom);152this.left -= Number(opt_left);153}154155return this;156};157158159/**160* Expand this box to include another box.161* NOTE(user): This is used in code that needs to be very fast, please don't162* add functionality to this function at the expense of speed (variable163* arguments, accepting multiple argument types, etc).164* @param {goog.math.Box} box The box to include in this one.165*/166goog.math.Box.prototype.expandToInclude = function(box) {167this.left = Math.min(this.left, box.left);168this.top = Math.min(this.top, box.top);169this.right = Math.max(this.right, box.right);170this.bottom = Math.max(this.bottom, box.bottom);171};172173174/**175* Expand this box to include the coordinate.176* @param {!goog.math.Coordinate} coord The coordinate to be included177* inside the box.178*/179goog.math.Box.prototype.expandToIncludeCoordinate = function(coord) {180this.top = Math.min(this.top, coord.y);181this.right = Math.max(this.right, coord.x);182this.bottom = Math.max(this.bottom, coord.y);183this.left = Math.min(this.left, coord.x);184};185186187/**188* Compares boxes for equality.189* @param {goog.math.Box} a A Box.190* @param {goog.math.Box} b A Box.191* @return {boolean} True iff the boxes are equal, or if both are null.192*/193goog.math.Box.equals = function(a, b) {194if (a == b) {195return true;196}197if (!a || !b) {198return false;199}200return a.top == b.top && a.right == b.right && a.bottom == b.bottom &&201a.left == b.left;202};203204205/**206* Returns whether a box contains a coordinate or another box.207*208* @param {goog.math.Box} box A Box.209* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.210* @return {boolean} Whether the box contains the coordinate or other box.211*/212goog.math.Box.contains = function(box, other) {213if (!box || !other) {214return false;215}216217if (other instanceof goog.math.Box) {218return other.left >= box.left && other.right <= box.right &&219other.top >= box.top && other.bottom <= box.bottom;220}221222// other is a Coordinate.223return other.x >= box.left && other.x <= box.right && other.y >= box.top &&224other.y <= box.bottom;225};226227228/**229* Returns the relative x position of a coordinate compared to a box. Returns230* zero if the coordinate is inside the box.231*232* @param {goog.math.Box} box A Box.233* @param {goog.math.Coordinate} coord A Coordinate.234* @return {number} The x position of {@code coord} relative to the nearest235* side of {@code box}, or zero if {@code coord} is inside {@code box}.236*/237goog.math.Box.relativePositionX = function(box, coord) {238if (coord.x < box.left) {239return coord.x - box.left;240} else if (coord.x > box.right) {241return coord.x - box.right;242}243return 0;244};245246247/**248* Returns the relative y position of a coordinate compared to a box. Returns249* zero if the coordinate is inside the box.250*251* @param {goog.math.Box} box A Box.252* @param {goog.math.Coordinate} coord A Coordinate.253* @return {number} The y position of {@code coord} relative to the nearest254* side of {@code box}, or zero if {@code coord} is inside {@code box}.255*/256goog.math.Box.relativePositionY = function(box, coord) {257if (coord.y < box.top) {258return coord.y - box.top;259} else if (coord.y > box.bottom) {260return coord.y - box.bottom;261}262return 0;263};264265266/**267* Returns the distance between a coordinate and the nearest corner/side of a268* box. Returns zero if the coordinate is inside the box.269*270* @param {goog.math.Box} box A Box.271* @param {goog.math.Coordinate} coord A Coordinate.272* @return {number} The distance between {@code coord} and the nearest273* corner/side of {@code box}, or zero if {@code coord} is inside274* {@code box}.275*/276goog.math.Box.distance = function(box, coord) {277var x = goog.math.Box.relativePositionX(box, coord);278var y = goog.math.Box.relativePositionY(box, coord);279return Math.sqrt(x * x + y * y);280};281282283/**284* Returns whether two boxes intersect.285*286* @param {goog.math.Box} a A Box.287* @param {goog.math.Box} b A second Box.288* @return {boolean} Whether the boxes intersect.289*/290goog.math.Box.intersects = function(a, b) {291return (292a.left <= b.right && b.left <= a.right && a.top <= b.bottom &&293b.top <= a.bottom);294};295296297/**298* Returns whether two boxes would intersect with additional padding.299*300* @param {goog.math.Box} a A Box.301* @param {goog.math.Box} b A second Box.302* @param {number} padding The additional padding.303* @return {boolean} Whether the boxes intersect.304*/305goog.math.Box.intersectsWithPadding = function(a, b, padding) {306return (307a.left <= b.right + padding && b.left <= a.right + padding &&308a.top <= b.bottom + padding && b.top <= a.bottom + padding);309};310311312/**313* Rounds the fields to the next larger integer values.314*315* @return {!goog.math.Box} This box with ceil'd fields.316*/317goog.math.Box.prototype.ceil = function() {318this.top = Math.ceil(this.top);319this.right = Math.ceil(this.right);320this.bottom = Math.ceil(this.bottom);321this.left = Math.ceil(this.left);322return this;323};324325326/**327* Rounds the fields to the next smaller integer values.328*329* @return {!goog.math.Box} This box with floored fields.330*/331goog.math.Box.prototype.floor = function() {332this.top = Math.floor(this.top);333this.right = Math.floor(this.right);334this.bottom = Math.floor(this.bottom);335this.left = Math.floor(this.left);336return this;337};338339340/**341* Rounds the fields to nearest integer values.342*343* @return {!goog.math.Box} This box with rounded fields.344*/345goog.math.Box.prototype.round = function() {346this.top = Math.round(this.top);347this.right = Math.round(this.right);348this.bottom = Math.round(this.bottom);349this.left = Math.round(this.left);350return this;351};352353354/**355* Translates this box by the given offsets. If a {@code goog.math.Coordinate}356* is given, then the left and right values are translated by the coordinate's357* x value and the top and bottom values are translated by the coordinate's y358* value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x359* and y dimension values.360*361* @param {number|goog.math.Coordinate} tx The value to translate the x362* dimension values by or the the coordinate to translate this box by.363* @param {number=} opt_ty The value to translate y dimension values by.364* @return {!goog.math.Box} This box after translating.365*/366goog.math.Box.prototype.translate = function(tx, opt_ty) {367if (tx instanceof goog.math.Coordinate) {368this.left += tx.x;369this.right += tx.x;370this.top += tx.y;371this.bottom += tx.y;372} else {373goog.asserts.assertNumber(tx);374this.left += tx;375this.right += tx;376if (goog.isNumber(opt_ty)) {377this.top += opt_ty;378this.bottom += opt_ty;379}380}381return this;382};383384385/**386* Scales this coordinate by the given scale factors. The x and y dimension387* values are scaled by {@code sx} and {@code opt_sy} respectively.388* If {@code opt_sy} is not given, then {@code sx} is used for both x and y.389*390* @param {number} sx The scale factor to use for the x dimension.391* @param {number=} opt_sy The scale factor to use for the y dimension.392* @return {!goog.math.Box} This box after scaling.393*/394goog.math.Box.prototype.scale = function(sx, opt_sy) {395var sy = goog.isNumber(opt_sy) ? opt_sy : sx;396this.left *= sx;397this.right *= sx;398this.top *= sy;399this.bottom *= sy;400return this;401};402403404