Path: blob/trunk/third_party/closure/goog/math/box.js
4086 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview A utility class for representing a numeric box.8*/91011goog.provide('goog.math.Box');1213goog.require('goog.asserts');14goog.require('goog.math.Coordinate');1516goog.require('goog.utils');17181920/**21* Class for representing a box. A box is specified as a top, right, bottom,22* and left. A box is useful for representing margins and padding.23*24* This class assumes 'screen coordinates': larger Y coordinates are further25* from the top of the screen.26*27* @param {number} top Top.28* @param {number} right Right.29* @param {number} bottom Bottom.30* @param {number} left Left.31* @struct32* @constructor33*/34goog.math.Box = function(top, right, bottom, left) {35'use strict';36/**37* Top38* @type {number}39*/40this.top = top;4142/**43* Right44* @type {number}45*/46this.right = right;4748/**49* Bottom50* @type {number}51*/52this.bottom = bottom;5354/**55* Left56* @type {number}57*/58this.left = left;59};606162/**63* Creates a Box by bounding a collection of goog.math.Coordinate objects64* @param {...goog.math.Coordinate} var_args Coordinates to be included inside65* the box.66* @return {!goog.math.Box} A Box containing all the specified Coordinates.67*/68goog.math.Box.boundingBox = function(var_args) {69'use strict';70var box = new goog.math.Box(71arguments[0].y, arguments[0].x, arguments[0].y, arguments[0].x);72for (var i = 1; i < arguments.length; i++) {73box.expandToIncludeCoordinate(arguments[i]);74}75return box;76};777879/**80* @return {number} width The width of this Box.81*/82goog.math.Box.prototype.getWidth = function() {83'use strict';84return this.right - this.left;85};868788/**89* @return {number} height The height of this Box.90*/91goog.math.Box.prototype.getHeight = function() {92'use strict';93return this.bottom - this.top;94};959697/**98* Creates a copy of the box with the same dimensions.99* @return {!goog.math.Box} A clone of this Box.100*/101goog.math.Box.prototype.clone = function() {102'use strict';103return new goog.math.Box(this.top, this.right, this.bottom, this.left);104};105106107if (goog.DEBUG) {108/**109* Returns a nice string representing the box.110* @return {string} In the form (50t, 73r, 24b, 13l).111* @override112*/113goog.math.Box.prototype.toString = function() {114'use strict';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) {128'use strict';129return goog.math.Box.contains(this, other);130};131132133/**134* Expands box with the given margins.135*136* @param {number|goog.math.Box} top Top margin or box with all margins.137* @param {number=} opt_right Right margin.138* @param {number=} opt_bottom Bottom margin.139* @param {number=} opt_left Left margin.140* @return {!goog.math.Box} A reference to this Box.141*/142goog.math.Box.prototype.expand = function(143top, opt_right, opt_bottom, opt_left) {144'use strict';145if (goog.utils.isObject(top)) {146this.top -= top.top;147this.right += top.right;148this.bottom += top.bottom;149this.left -= top.left;150} else {151this.top -= /** @type {number} */ (top);152this.right += Number(opt_right);153this.bottom += Number(opt_bottom);154this.left -= Number(opt_left);155}156157return this;158};159160161/**162* Expand this box to include another box.163* NOTE(user): This is used in code that needs to be very fast, please don't164* add functionality to this function at the expense of speed (variable165* arguments, accepting multiple argument types, etc).166* @param {goog.math.Box} box The box to include in this one.167*/168goog.math.Box.prototype.expandToInclude = function(box) {169'use strict';170this.left = Math.min(this.left, box.left);171this.top = Math.min(this.top, box.top);172this.right = Math.max(this.right, box.right);173this.bottom = Math.max(this.bottom, box.bottom);174};175176177/**178* Expand this box to include the coordinate.179* @param {!goog.math.Coordinate} coord The coordinate to be included180* inside the box.181*/182goog.math.Box.prototype.expandToIncludeCoordinate = function(coord) {183'use strict';184this.top = Math.min(this.top, coord.y);185this.right = Math.max(this.right, coord.x);186this.bottom = Math.max(this.bottom, coord.y);187this.left = Math.min(this.left, coord.x);188};189190191/**192* Compares boxes for equality.193* @param {goog.math.Box} a A Box.194* @param {goog.math.Box} b A Box.195* @return {boolean} True iff the boxes are equal, or if both are null.196*/197goog.math.Box.equals = function(a, b) {198'use strict';199if (a == b) {200return true;201}202if (!a || !b) {203return false;204}205return a.top == b.top && a.right == b.right && a.bottom == b.bottom &&206a.left == b.left;207};208209210/**211* Returns whether a box contains a coordinate or another box.212*213* @param {goog.math.Box} box A Box.214* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.215* @return {boolean} Whether the box contains the coordinate or other box.216*/217goog.math.Box.contains = function(box, other) {218'use strict';219if (!box || !other) {220return false;221}222223if (other instanceof goog.math.Box) {224return other.left >= box.left && other.right <= box.right &&225other.top >= box.top && other.bottom <= box.bottom;226}227228// other is a Coordinate.229return other.x >= box.left && other.x <= box.right && other.y >= box.top &&230other.y <= box.bottom;231};232233234/**235* Returns the relative x position of a coordinate compared to a box. Returns236* zero if the coordinate is inside the box.237*238* @param {goog.math.Box} box A Box.239* @param {goog.math.Coordinate} coord A Coordinate.240* @return {number} The x position of `coord` relative to the nearest241* side of `box`, or zero if `coord` is inside `box`.242*/243goog.math.Box.relativePositionX = function(box, coord) {244'use strict';245if (coord.x < box.left) {246return coord.x - box.left;247} else if (coord.x > box.right) {248return coord.x - box.right;249}250return 0;251};252253254/**255* Returns the relative y position of a coordinate compared to a box. Returns256* zero if the coordinate is inside the box.257*258* @param {goog.math.Box} box A Box.259* @param {goog.math.Coordinate} coord A Coordinate.260* @return {number} The y position of `coord` relative to the nearest261* side of `box`, or zero if `coord` is inside `box`.262*/263goog.math.Box.relativePositionY = function(box, coord) {264'use strict';265if (coord.y < box.top) {266return coord.y - box.top;267} else if (coord.y > box.bottom) {268return coord.y - box.bottom;269}270return 0;271};272273274/**275* Returns the distance between a coordinate and the nearest corner/side of a276* box. Returns zero if the coordinate is inside the box.277*278* @param {goog.math.Box} box A Box.279* @param {goog.math.Coordinate} coord A Coordinate.280* @return {number} The distance between `coord` and the nearest281* corner/side of `box`, or zero if `coord` is inside282* `box`.283*/284goog.math.Box.distance = function(box, coord) {285'use strict';286var x = goog.math.Box.relativePositionX(box, coord);287var y = goog.math.Box.relativePositionY(box, coord);288return Math.sqrt(x * x + y * y);289};290291292/**293* Returns whether two boxes intersect.294*295* @param {goog.math.Box} a A Box.296* @param {goog.math.Box} b A second Box.297* @return {boolean} Whether the boxes intersect.298*/299goog.math.Box.intersects = function(a, b) {300'use strict';301return (302a.left <= b.right && b.left <= a.right && a.top <= b.bottom &&303b.top <= a.bottom);304};305306307/**308* Returns whether two boxes would intersect with additional padding.309*310* @param {goog.math.Box} a A Box.311* @param {goog.math.Box} b A second Box.312* @param {number} padding The additional padding.313* @return {boolean} Whether the boxes intersect.314*/315goog.math.Box.intersectsWithPadding = function(a, b, padding) {316'use strict';317return (318a.left <= b.right + padding && b.left <= a.right + padding &&319a.top <= b.bottom + padding && b.top <= a.bottom + padding);320};321322323/**324* Rounds the fields to the next larger integer values.325*326* @return {!goog.math.Box} This box with ceil'd fields.327*/328goog.math.Box.prototype.ceil = function() {329'use strict';330this.top = Math.ceil(this.top);331this.right = Math.ceil(this.right);332this.bottom = Math.ceil(this.bottom);333this.left = Math.ceil(this.left);334return this;335};336337338/**339* Rounds the fields to the next smaller integer values.340*341* @return {!goog.math.Box} This box with floored fields.342*/343goog.math.Box.prototype.floor = function() {344'use strict';345this.top = Math.floor(this.top);346this.right = Math.floor(this.right);347this.bottom = Math.floor(this.bottom);348this.left = Math.floor(this.left);349return this;350};351352353/**354* Rounds the fields to nearest integer values.355*356* @return {!goog.math.Box} This box with rounded fields.357*/358goog.math.Box.prototype.round = function() {359'use strict';360this.top = Math.round(this.top);361this.right = Math.round(this.right);362this.bottom = Math.round(this.bottom);363this.left = Math.round(this.left);364return this;365};366367368/**369* Translates this box by the given offsets. If a `goog.math.Coordinate`370* is given, then the left and right values are translated by the coordinate's371* x value and the top and bottom values are translated by the coordinate's y372* value. Otherwise, `tx` and `opt_ty` are used to translate the x373* and y dimension values.374*375* @param {number|goog.math.Coordinate} tx The value to translate the x376* dimension values by or the coordinate to translate this box by.377* @param {number=} opt_ty The value to translate y dimension values by.378* @return {!goog.math.Box} This box after translating.379*/380goog.math.Box.prototype.translate = function(tx, opt_ty) {381'use strict';382if (tx instanceof goog.math.Coordinate) {383this.left += tx.x;384this.right += tx.x;385this.top += tx.y;386this.bottom += tx.y;387} else {388goog.asserts.assertNumber(tx);389this.left += tx;390this.right += tx;391if (typeof opt_ty === 'number') {392this.top += opt_ty;393this.bottom += opt_ty;394}395}396return this;397};398399400/**401* Scales this coordinate by the given scale factors. The x and y dimension402* values are scaled by `sx` and `opt_sy` respectively.403* If `opt_sy` is not given, then `sx` is used for both x and y.404*405* @param {number} sx The scale factor to use for the x dimension.406* @param {number=} opt_sy The scale factor to use for the y dimension.407* @return {!goog.math.Box} This box after scaling.408*/409goog.math.Box.prototype.scale = function(sx, opt_sy) {410'use strict';411var sy = (typeof opt_sy === 'number') ? opt_sy : sx;412this.left *= sx;413this.right *= sx;414this.top *= sy;415this.bottom *= sy;416return this;417};418419420