Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions

View File

@@ -0,0 +1,311 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a cubic Bezier curve.
*
* Uses the deCasteljau algorithm to compute points on the curve.
* http://en.wikipedia.org/wiki/De_Casteljau's_algorithm
*
* Currently it uses an unrolled version of the algorithm for speed. Eventually
* it may be useful to use the loop form of the algorithm in order to support
* curves of arbitrary degree.
*
* @author robbyw@google.com (Robby Walker)
* @author wcrosby@google.com (Wayne Crosby)
*/
goog.provide('goog.math.Bezier');
goog.require('goog.math');
goog.require('goog.math.Coordinate');
/**
* Object representing a cubic bezier curve.
* @param {number} x0 X coordinate of the start point.
* @param {number} y0 Y coordinate of the start point.
* @param {number} x1 X coordinate of the first control point.
* @param {number} y1 Y coordinate of the first control point.
* @param {number} x2 X coordinate of the second control point.
* @param {number} y2 Y coordinate of the second control point.
* @param {number} x3 X coordinate of the end point.
* @param {number} y3 Y coordinate of the end point.
* @constructor
*/
goog.math.Bezier = function(x0, y0, x1, y1, x2, y2, x3, y3) {
/**
* X coordinate of the first point.
* @type {number}
*/
this.x0 = x0;
/**
* Y coordinate of the first point.
* @type {number}
*/
this.y0 = y0;
/**
* X coordinate of the first control point.
* @type {number}
*/
this.x1 = x1;
/**
* Y coordinate of the first control point.
* @type {number}
*/
this.y1 = y1;
/**
* X coordinate of the second control point.
* @type {number}
*/
this.x2 = x2;
/**
* Y coordinate of the second control point.
* @type {number}
*/
this.y2 = y2;
/**
* X coordinate of the end point.
* @type {number}
*/
this.x3 = x3;
/**
* Y coordinate of the end point.
* @type {number}
*/
this.y3 = y3;
};
/**
* Constant used to approximate ellipses.
* See: http://canvaspaint.org/blog/2006/12/ellipse/
* @type {number}
*/
goog.math.Bezier.KAPPA = 4 * (Math.sqrt(2) - 1) / 3;
/**
* @return {!goog.math.Bezier} A copy of this curve.
*/
goog.math.Bezier.prototype.clone = function() {
return new goog.math.Bezier(this.x0, this.y0, this.x1, this.y1, this.x2,
this.y2, this.x3, this.y3);
};
/**
* Test if the given curve is exactly the same as this one.
* @param {goog.math.Bezier} other The other curve.
* @return {boolean} Whether the given curve is the same as this one.
*/
goog.math.Bezier.prototype.equals = function(other) {
return this.x0 == other.x0 && this.y0 == other.y0 && this.x1 == other.x1 &&
this.y1 == other.y1 && this.x2 == other.x2 && this.y2 == other.y2 &&
this.x3 == other.x3 && this.y3 == other.y3;
};
/**
* Modifies the curve in place to progress in the opposite direction.
*/
goog.math.Bezier.prototype.flip = function() {
var temp = this.x0;
this.x0 = this.x3;
this.x3 = temp;
temp = this.y0;
this.y0 = this.y3;
this.y3 = temp;
temp = this.x1;
this.x1 = this.x2;
this.x2 = temp;
temp = this.y1;
this.y1 = this.y2;
this.y2 = temp;
};
/**
* Computes the curve at a point between 0 and 1.
* @param {number} t The point on the curve to find.
* @return {!goog.math.Coordinate} The computed coordinate.
*/
goog.math.Bezier.prototype.getPoint = function(t) {
// Special case start and end
if (t == 0) {
return new goog.math.Coordinate(this.x0, this.y0);
} else if (t == 1) {
return new goog.math.Coordinate(this.x3, this.y3);
}
// Step one - from 4 points to 3
var ix0 = goog.math.lerp(this.x0, this.x1, t);
var iy0 = goog.math.lerp(this.y0, this.y1, t);
var ix1 = goog.math.lerp(this.x1, this.x2, t);
var iy1 = goog.math.lerp(this.y1, this.y2, t);
var ix2 = goog.math.lerp(this.x2, this.x3, t);
var iy2 = goog.math.lerp(this.y2, this.y3, t);
// Step two - from 3 points to 2
ix0 = goog.math.lerp(ix0, ix1, t);
iy0 = goog.math.lerp(iy0, iy1, t);
ix1 = goog.math.lerp(ix1, ix2, t);
iy1 = goog.math.lerp(iy1, iy2, t);
// Final step - last point
return new goog.math.Coordinate(goog.math.lerp(ix0, ix1, t),
goog.math.lerp(iy0, iy1, t));
};
/**
* Changes this curve in place to be the portion of itself from [t, 1].
* @param {number} t The start of the desired portion of the curve.
*/
goog.math.Bezier.prototype.subdivideLeft = function(t) {
if (t == 1) {
return;
}
// Step one - from 4 points to 3
var ix0 = goog.math.lerp(this.x0, this.x1, t);
var iy0 = goog.math.lerp(this.y0, this.y1, t);
var ix1 = goog.math.lerp(this.x1, this.x2, t);
var iy1 = goog.math.lerp(this.y1, this.y2, t);
var ix2 = goog.math.lerp(this.x2, this.x3, t);
var iy2 = goog.math.lerp(this.y2, this.y3, t);
// Collect our new x1 and y1
this.x1 = ix0;
this.y1 = iy0;
// Step two - from 3 points to 2
ix0 = goog.math.lerp(ix0, ix1, t);
iy0 = goog.math.lerp(iy0, iy1, t);
ix1 = goog.math.lerp(ix1, ix2, t);
iy1 = goog.math.lerp(iy1, iy2, t);
// Collect our new x2 and y2
this.x2 = ix0;
this.y2 = iy0;
// Final step - last point
this.x3 = goog.math.lerp(ix0, ix1, t);
this.y3 = goog.math.lerp(iy0, iy1, t);
};
/**
* Changes this curve in place to be the portion of itself from [0, t].
* @param {number} t The end of the desired portion of the curve.
*/
goog.math.Bezier.prototype.subdivideRight = function(t) {
this.flip();
this.subdivideLeft(1 - t);
this.flip();
};
/**
* Changes this curve in place to be the portion of itself from [s, t].
* @param {number} s The start of the desired portion of the curve.
* @param {number} t The end of the desired portion of the curve.
*/
goog.math.Bezier.prototype.subdivide = function(s, t) {
this.subdivideRight(s);
this.subdivideLeft((t - s) / (1 - s));
};
/**
* Computes the position t of a point on the curve given its x coordinate.
* That is, for an input xVal, finds t s.t. getPoint(t).x = xVal.
* As such, the following should always be true up to some small epsilon:
* t ~ solvePositionFromXValue(getPoint(t).x) for t in [0, 1].
* @param {number} xVal The x coordinate of the point to find on the curve.
* @return {number} The position t.
*/
goog.math.Bezier.prototype.solvePositionFromXValue = function(xVal) {
// Desired precision on the computation.
var epsilon = 1e-6;
// Initial estimate of t using linear interpolation.
var t = (xVal - this.x0) / (this.x3 - this.x0);
if (t <= 0) {
return 0;
} else if (t >= 1) {
return 1;
}
// Try gradient descent to solve for t. If it works, it is very fast.
var tMin = 0;
var tMax = 1;
for (var i = 0; i < 8; i++) {
var value = this.getPoint(t).x;
var derivative = (this.getPoint(t + epsilon).x - value) / epsilon;
if (Math.abs(value - xVal) < epsilon) {
return t;
} else if (Math.abs(derivative) < epsilon) {
break;
} else {
if (value < xVal) {
tMin = t;
} else {
tMax = t;
}
t -= (value - xVal) / derivative;
}
}
// If the gradient descent got stuck in a local minimum, e.g. because
// the derivative was close to 0, use a Dichotomy refinement instead.
// We limit the number of interations to 8.
for (var i = 0; Math.abs(value - xVal) > epsilon && i < 8; i++) {
if (value < xVal) {
tMin = t;
t = (t + tMax) / 2;
} else {
tMax = t;
t = (t + tMin) / 2;
}
value = this.getPoint(t).x;
}
return t;
};
/**
* Computes the y coordinate of a point on the curve given its x coordinate.
* @param {number} xVal The x coordinate of the point on the curve.
* @return {number} The y coordinate of the point on the curve.
*/
goog.math.Bezier.prototype.solveYValueFromXValue = function(xVal) {
return this.getPoint(this.solvePositionFromXValue(xVal)).y;
};

View File

@@ -0,0 +1,369 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing a numeric box.
*/
goog.provide('goog.math.Box');
goog.require('goog.math.Coordinate');
/**
* Class for representing a box. A box is specified as a top, right, bottom,
* and left. A box is useful for representing margins and padding.
*
* @param {number} top Top.
* @param {number} right Right.
* @param {number} bottom Bottom.
* @param {number} left Left.
* @constructor
*/
goog.math.Box = function(top, right, bottom, left) {
/**
* Top
* @type {number}
*/
this.top = top;
/**
* Right
* @type {number}
*/
this.right = right;
/**
* Bottom
* @type {number}
*/
this.bottom = bottom;
/**
* Left
* @type {number}
*/
this.left = left;
};
/**
* Creates a Box by bounding a collection of goog.math.Coordinate objects
* @param {...goog.math.Coordinate} var_args Coordinates to be included inside
* the box.
* @return {!goog.math.Box} A Box containing all the specified Coordinates.
*/
goog.math.Box.boundingBox = function(var_args) {
var box = new goog.math.Box(arguments[0].y, arguments[0].x,
arguments[0].y, arguments[0].x);
for (var i = 1; i < arguments.length; i++) {
var coord = arguments[i];
box.top = Math.min(box.top, coord.y);
box.right = Math.max(box.right, coord.x);
box.bottom = Math.max(box.bottom, coord.y);
box.left = Math.min(box.left, coord.x);
}
return box;
};
/**
* Creates a copy of the box with the same dimensions.
* @return {!goog.math.Box} A clone of this Box.
*/
goog.math.Box.prototype.clone = function() {
return new goog.math.Box(this.top, this.right, this.bottom, this.left);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing the box.
* @return {string} In the form (50t, 73r, 24b, 13l).
* @override
*/
goog.math.Box.prototype.toString = function() {
return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
this.left + 'l)';
};
}
/**
* Returns whether the box contains a coordinate or another box.
*
* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
* @return {boolean} Whether the box contains the coordinate or other box.
*/
goog.math.Box.prototype.contains = function(other) {
return goog.math.Box.contains(this, other);
};
/**
* Expands box with the given margins.
*
* @param {number|goog.math.Box} top Top margin or box with all margins.
* @param {number=} opt_right Right margin.
* @param {number=} opt_bottom Bottom margin.
* @param {number=} opt_left Left margin.
* @return {!goog.math.Box} A reference to this Box.
*/
goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
opt_left) {
if (goog.isObject(top)) {
this.top -= top.top;
this.right += top.right;
this.bottom += top.bottom;
this.left -= top.left;
} else {
this.top -= top;
this.right += opt_right;
this.bottom += opt_bottom;
this.left -= opt_left;
}
return this;
};
/**
* Expand this box to include another box.
* NOTE(user): This is used in code that needs to be very fast, please don't
* add functionality to this function at the expense of speed (variable
* arguments, accepting multiple argument types, etc).
* @param {goog.math.Box} box The box to include in this one.
*/
goog.math.Box.prototype.expandToInclude = function(box) {
this.left = Math.min(this.left, box.left);
this.top = Math.min(this.top, box.top);
this.right = Math.max(this.right, box.right);
this.bottom = Math.max(this.bottom, box.bottom);
};
/**
* Compares boxes for equality.
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A Box.
* @return {boolean} True iff the boxes are equal, or if both are null.
*/
goog.math.Box.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.top == b.top && a.right == b.right &&
a.bottom == b.bottom && a.left == b.left;
};
/**
* Returns whether a box contains a coordinate or another box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
* @return {boolean} Whether the box contains the coordinate or other box.
*/
goog.math.Box.contains = function(box, other) {
if (!box || !other) {
return false;
}
if (other instanceof goog.math.Box) {
return other.left >= box.left && other.right <= box.right &&
other.top >= box.top && other.bottom <= box.bottom;
}
// other is a Coordinate.
return other.x >= box.left && other.x <= box.right &&
other.y >= box.top && other.y <= box.bottom;
};
/**
* Returns the relative x position of a coordinate compared to a box. Returns
* zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The x position of {@code coord} relative to the nearest
* side of {@code box}, or zero if {@code coord} is inside {@code box}.
*/
goog.math.Box.relativePositionX = function(box, coord) {
if (coord.x < box.left) {
return coord.x - box.left;
} else if (coord.x > box.right) {
return coord.x - box.right;
}
return 0;
};
/**
* Returns the relative y position of a coordinate compared to a box. Returns
* zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The y position of {@code coord} relative to the nearest
* side of {@code box}, or zero if {@code coord} is inside {@code box}.
*/
goog.math.Box.relativePositionY = function(box, coord) {
if (coord.y < box.top) {
return coord.y - box.top;
} else if (coord.y > box.bottom) {
return coord.y - box.bottom;
}
return 0;
};
/**
* Returns the distance between a coordinate and the nearest corner/side of a
* box. Returns zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The distance between {@code coord} and the nearest
* corner/side of {@code box}, or zero if {@code coord} is inside
* {@code box}.
*/
goog.math.Box.distance = function(box, coord) {
var x = goog.math.Box.relativePositionX(box, coord);
var y = goog.math.Box.relativePositionY(box, coord);
return Math.sqrt(x * x + y * y);
};
/**
* Returns whether two boxes intersect.
*
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A second Box.
* @return {boolean} Whether the boxes intersect.
*/
goog.math.Box.intersects = function(a, b) {
return (a.left <= b.right && b.left <= a.right &&
a.top <= b.bottom && b.top <= a.bottom);
};
/**
* Returns whether two boxes would intersect with additional padding.
*
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A second Box.
* @param {number} padding The additional padding.
* @return {boolean} Whether the boxes intersect.
*/
goog.math.Box.intersectsWithPadding = function(a, b, padding) {
return (a.left <= b.right + padding && b.left <= a.right + padding &&
a.top <= b.bottom + padding && b.top <= a.bottom + padding);
};
/**
* Rounds the fields to the next larger integer values.
*
* @return {!goog.math.Box} This box with ceil'd fields.
*/
goog.math.Box.prototype.ceil = function() {
this.top = Math.ceil(this.top);
this.right = Math.ceil(this.right);
this.bottom = Math.ceil(this.bottom);
this.left = Math.ceil(this.left);
return this;
};
/**
* Rounds the fields to the next smaller integer values.
*
* @return {!goog.math.Box} This box with floored fields.
*/
goog.math.Box.prototype.floor = function() {
this.top = Math.floor(this.top);
this.right = Math.floor(this.right);
this.bottom = Math.floor(this.bottom);
this.left = Math.floor(this.left);
return this;
};
/**
* Rounds the fields to nearest integer values.
*
* @return {!goog.math.Box} This box with rounded fields.
*/
goog.math.Box.prototype.round = function() {
this.top = Math.round(this.top);
this.right = Math.round(this.right);
this.bottom = Math.round(this.bottom);
this.left = Math.round(this.left);
return this;
};
/**
* Translates this box by the given offsets. If a {@code goog.math.Coordinate}
* is given, then the left and right values are translated by the coordinate's
* x value and the top and bottom values are translated by the coordinate's y
* value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
* and y dimension values.
*
* @param {number|goog.math.Coordinate} tx The value to translate the x
* dimension values by or the the coordinate to translate this box by.
* @param {number=} opt_ty The value to translate y dimension values by.
* @return {!goog.math.Box} This box after translating.
*/
goog.math.Box.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.left += tx.x;
this.right += tx.x;
this.top += tx.y;
this.bottom += tx.y;
} else {
this.left += tx;
this.right += tx;
if (goog.isNumber(opt_ty)) {
this.top += opt_ty;
this.bottom += opt_ty;
}
}
return this;
};
/**
* Scales this coordinate by the given scale factors. The x and y dimension
* values are scaled by {@code sx} and {@code opt_sy} respectively.
* If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
*
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Box} This box after scaling.
*/
goog.math.Box.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.left *= sx;
this.right *= sx;
this.top *= sy;
this.bottom *= sy;
return this;
};

View File

@@ -0,0 +1,233 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing two-dimensional positions.
*/
goog.provide('goog.math.Coordinate');
goog.require('goog.math');
/**
* Class for representing coordinates and positions.
* @param {number=} opt_x Left, defaults to 0.
* @param {number=} opt_y Top, defaults to 0.
* @constructor
*/
goog.math.Coordinate = function(opt_x, opt_y) {
/**
* X-value
* @type {number}
*/
this.x = goog.isDef(opt_x) ? opt_x : 0;
/**
* Y-value
* @type {number}
*/
this.y = goog.isDef(opt_y) ? opt_y : 0;
};
/**
* Returns a new copy of the coordinate.
* @return {!goog.math.Coordinate} A clone of this coordinate.
*/
goog.math.Coordinate.prototype.clone = function() {
return new goog.math.Coordinate(this.x, this.y);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing the coordinate.
* @return {string} In the form (50, 73).
* @override
*/
goog.math.Coordinate.prototype.toString = function() {
return '(' + this.x + ', ' + this.y + ')';
};
}
/**
* Compares coordinates for equality.
* @param {goog.math.Coordinate} a A Coordinate.
* @param {goog.math.Coordinate} b A Coordinate.
* @return {boolean} True iff the coordinates are equal, or if both are null.
*/
goog.math.Coordinate.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.x == b.x && a.y == b.y;
};
/**
* Returns the distance between two coordinates.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {number} The distance between {@code a} and {@code b}.
*/
goog.math.Coordinate.distance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
};
/**
* Returns the magnitude of a coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @return {number} The distance between the origin and {@code a}.
*/
goog.math.Coordinate.magnitude = function(a) {
return Math.sqrt(a.x * a.x + a.y * a.y);
};
/**
* Returns the angle from the origin to a coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @return {number} The angle, in degrees, clockwise from the positive X
* axis to {@code a}.
*/
goog.math.Coordinate.azimuth = function(a) {
return goog.math.angle(0, 0, a.x, a.y);
};
/**
* Returns the squared distance between two coordinates. Squared distances can
* be used for comparisons when the actual value is not required.
*
* Performance note: eliminating the square root is an optimization often used
* in lower-level languages, but the speed difference is not nearly as
* pronounced in JavaScript (only a few percent.)
*
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {number} The squared distance between {@code a} and {@code b}.
*/
goog.math.Coordinate.squaredDistance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return dx * dx + dy * dy;
};
/**
* Returns the difference between two coordinates as a new
* goog.math.Coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {!goog.math.Coordinate} A Coordinate representing the difference
* between {@code a} and {@code b}.
*/
goog.math.Coordinate.difference = function(a, b) {
return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
};
/**
* Returns the sum of two coordinates as a new goog.math.Coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
* coordinates.
*/
goog.math.Coordinate.sum = function(a, b) {
return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
};
/**
* Rounds the x and y fields to the next larger integer values.
* @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
*/
goog.math.Coordinate.prototype.ceil = function() {
this.x = Math.ceil(this.x);
this.y = Math.ceil(this.y);
return this;
};
/**
* Rounds the x and y fields to the next smaller integer values.
* @return {!goog.math.Coordinate} This coordinate with floored fields.
*/
goog.math.Coordinate.prototype.floor = function() {
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
return this;
};
/**
* Rounds the x and y fields to the nearest integer values.
* @return {!goog.math.Coordinate} This coordinate with rounded fields.
*/
goog.math.Coordinate.prototype.round = function() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
};
/**
* Translates this box by the given offsets. If a {@code goog.math.Coordinate}
* is given, then the x and y values are translated by the coordinate's x and y.
* Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
* respectively.
* @param {number|goog.math.Coordinate} tx The value to translate x by or the
* the coordinate to translate this coordinate by.
* @param {number=} opt_ty The value to translate y by.
* @return {!goog.math.Coordinate} This coordinate after translating.
*/
goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.x += tx.x;
this.y += tx.y;
} else {
this.x += tx;
if (goog.isNumber(opt_ty)) {
this.y += opt_ty;
}
}
return this;
};
/**
* Scales this coordinate by the given scale factors. The x and y values are
* scaled by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy}
* is not given, then {@code sx} is used for both x and y.
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Coordinate} This coordinate after scaling.
*/
goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.x *= sx;
this.y *= sy;
return this;
};

View File

@@ -0,0 +1,169 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing three-dimensional points.
*
* Based heavily on coordinate.js by:
*/
goog.provide('goog.math.Coordinate3');
/**
* Class for representing coordinates and positions in 3 dimensions.
*
* @param {number=} opt_x X coordinate, defaults to 0.
* @param {number=} opt_y Y coordinate, defaults to 0.
* @param {number=} opt_z Z coordinate, defaults to 0.
* @constructor
*/
goog.math.Coordinate3 = function(opt_x, opt_y, opt_z) {
/**
* X-value
* @type {number}
*/
this.x = goog.isDef(opt_x) ? opt_x : 0;
/**
* Y-value
* @type {number}
*/
this.y = goog.isDef(opt_y) ? opt_y : 0;
/**
* Z-value
* @type {number}
*/
this.z = goog.isDef(opt_z) ? opt_z : 0;
};
/**
* Returns a new copy of the coordinate.
*
* @return {!goog.math.Coordinate3} A clone of this coordinate.
*/
goog.math.Coordinate3.prototype.clone = function() {
return new goog.math.Coordinate3(this.x, this.y, this.z);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing the coordinate.
*
* @return {string} In the form (50, 73, 31).
* @override
*/
goog.math.Coordinate3.prototype.toString = function() {
return '(' + this.x + ', ' + this.y + ', ' + this.z + ')';
};
}
/**
* Compares coordinates for equality.
*
* @param {goog.math.Coordinate3} a A Coordinate3.
* @param {goog.math.Coordinate3} b A Coordinate3.
* @return {boolean} True iff the coordinates are equal, or if both are null.
*/
goog.math.Coordinate3.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.x == b.x && a.y == b.y && a.z == b.z;
};
/**
* Returns the distance between two coordinates.
*
* @param {goog.math.Coordinate3} a A Coordinate3.
* @param {goog.math.Coordinate3} b A Coordinate3.
* @return {number} The distance between {@code a} and {@code b}.
*/
goog.math.Coordinate3.distance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dz = a.z - b.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
};
/**
* Returns the squared distance between two coordinates. Squared distances can
* be used for comparisons when the actual value is not required.
*
* Performance note: eliminating the square root is an optimization often used
* in lower-level languages, but the speed difference is not nearly as
* pronounced in JavaScript (only a few percent.)
*
* @param {goog.math.Coordinate3} a A Coordinate3.
* @param {goog.math.Coordinate3} b A Coordinate3.
* @return {number} The squared distance between {@code a} and {@code b}.
*/
goog.math.Coordinate3.squaredDistance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dz = a.z - b.z;
return dx * dx + dy * dy + dz * dz;
};
/**
* Returns the difference between two coordinates as a new
* goog.math.Coordinate3.
*
* @param {goog.math.Coordinate3} a A Coordinate3.
* @param {goog.math.Coordinate3} b A Coordinate3.
* @return {!goog.math.Coordinate3} A Coordinate3 representing the difference
* between {@code a} and {@code b}.
*/
goog.math.Coordinate3.difference = function(a, b) {
return new goog.math.Coordinate3(a.x - b.x, a.y - b.y, a.z - b.z);
};
/**
* Returns the contents of this coordinate as a 3 value Array.
*
* @return {!Array.<number>} A new array.
*/
goog.math.Coordinate3.prototype.toArray = function() {
return [this.x, this.y, this.z];
};
/**
* Converts a three element array into a Coordinate3 object. If the value
* passed in is not an array, not array-like, or not of the right length, an
* error is thrown.
*
* @param {Array.<number>} a Array of numbers to become a coordinate.
* @return {!goog.math.Coordinate3} A new coordinate from the array values.
* @throws {Error} When the oject passed in is not valid.
*/
goog.math.Coordinate3.fromArray = function(a) {
if (a.length <= 3) {
return new goog.math.Coordinate3(a[0], a[1], a[2]);
}
throw Error('Conversion from an array requires an array of length 3');
};

View File

@@ -0,0 +1,103 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility class to manage the mathematics behind computing an
* exponential backoff model. Given an initial backoff value and a maximum
* backoff value, every call to backoff() will double the value until maximum
* backoff value is reached.
*
*/
goog.provide('goog.math.ExponentialBackoff');
goog.require('goog.asserts');
/**
* @constructor
*
* @param {number} initialValue The initial backoff value.
* @param {number} maxValue The maximum backoff value.
*/
goog.math.ExponentialBackoff = function(initialValue, maxValue) {
goog.asserts.assert(initialValue > 0,
'Initial value must be greater than zero.');
goog.asserts.assert(maxValue >= initialValue,
'Max value should be at least as large as initial value.');
/**
* @type {number}
* @private
*/
this.initialValue_ = initialValue;
/**
* @type {number}
* @private
*/
this.maxValue_ = maxValue;
/**
* The current backoff value.
* @type {number}
* @private
*/
this.currValue_ = initialValue;
};
/**
* The number of backoffs that have happened.
* @type {number}
* @private
*/
goog.math.ExponentialBackoff.prototype.currCount_ = 0;
/**
* Resets the backoff value to its initial value.
*/
goog.math.ExponentialBackoff.prototype.reset = function() {
this.currValue_ = this.initialValue_;
this.currCount_ = 0;
};
/**
* @return {number} The current backoff value.
*/
goog.math.ExponentialBackoff.prototype.getValue = function() {
return this.currValue_;
};
/**
* @return {number} The number of times this class has backed off.
*/
goog.math.ExponentialBackoff.prototype.getBackoffCount = function() {
return this.currCount_;
};
/**
* Initiates a backoff.
*/
goog.math.ExponentialBackoff.prototype.backoff = function() {
this.currValue_ = Math.min(this.maxValue_, this.currValue_ * 2);
this.currCount_++;
};

View File

@@ -0,0 +1,737 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines an Integer class for representing (potentially)
* infinite length two's-complement integer values.
*
* For the specific case of 64-bit integers, use goog.math.Long, which is more
* efficient.
*
*/
goog.provide('goog.math.Integer');
/**
* Constructs a two's-complement integer an array containing bits of the
* integer in 32-bit (signed) pieces, given in little-endian order (i.e.,
* lowest-order bits in the first piece), and the sign of -1 or 0.
*
* See the from* functions below for other convenient ways of constructing
* Integers.
*
* The internal representation of an integer is an array of 32-bit signed
* pieces, along with a sign (0 or -1) that indicates the contents of all the
* other 32-bit pieces out to infinity. We use 32-bit pieces because these are
* the size of integers on which Javascript performs bit-operations. For
* operations like addition and multiplication, we split each number into 16-bit
* pieces, which can easily be multiplied within Javascript's floating-point
* representation without overflow or change in sign.
*
* @constructor
* @param {Array.<number>} bits Array containing the bits of the number.
* @param {number} sign The sign of the number: -1 for negative and 0 positive.
*/
goog.math.Integer = function(bits, sign) {
/**
* @type {!Array.<number>}
* @private
*/
this.bits_ = [];
/**
* @type {number}
* @private
*/
this.sign_ = sign;
// Copy the 32-bit signed integer values passed in. We prune out those at the
// top that equal the sign since they are redundant.
var top = true;
for (var i = bits.length - 1; i >= 0; i--) {
var val = bits[i] | 0;
if (!top || val != sign) {
this.bits_[i] = val;
top = false;
}
}
};
// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
// from* methods on which they depend.
/**
* A cache of the Integer representations of small integer values.
* @type {!Object}
* @private
*/
goog.math.Integer.IntCache_ = {};
/**
* Returns an Integer representing the given (32-bit) integer value.
* @param {number} value A 32-bit integer value.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromInt = function(value) {
if (-128 <= value && value < 128) {
var cachedObj = goog.math.Integer.IntCache_[value];
if (cachedObj) {
return cachedObj;
}
}
var obj = new goog.math.Integer([value | 0], value < 0 ? -1 : 0);
if (-128 <= value && value < 128) {
goog.math.Integer.IntCache_[value] = obj;
}
return obj;
};
/**
* Returns an Integer representing the given value, provided that it is a finite
* number. Otherwise, zero is returned.
* @param {number} value The value in question.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromNumber = function(value) {
if (isNaN(value) || !isFinite(value)) {
return goog.math.Integer.ZERO;
} else if (value < 0) {
return goog.math.Integer.fromNumber(-value).negate();
} else {
var bits = [];
var pow = 1;
for (var i = 0; value >= pow; i++) {
bits[i] = (value / pow) | 0;
pow *= goog.math.Integer.TWO_PWR_32_DBL_;
}
return new goog.math.Integer(bits, 0);
}
};
/**
* Returns a Integer representing the value that comes by concatenating the
* given entries, each is assumed to be 32 signed bits, given in little-endian
* order (lowest order bits in the lowest index), and sign-extending the highest
* order 32-bit value.
* @param {Array.<number>} bits The bits of the number, in 32-bit signed pieces,
* in little-endian order.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromBits = function(bits) {
var high = bits[bits.length - 1];
return new goog.math.Integer(bits, high & (1 << 31) ? -1 : 0);
};
/**
* Returns an Integer representation of the given string, written using the
* given radix.
* @param {string} str The textual representation of the Integer.
* @param {number=} opt_radix The radix in which the text is written.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromString = function(str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return goog.math.Integer.fromString(str.substring(1), radix).negate();
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character');
}
// Do several (8) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 8));
var result = goog.math.Integer.ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = goog.math.Integer.fromNumber(Math.pow(radix, size));
result = result.multiply(power).add(goog.math.Integer.fromNumber(value));
} else {
result = result.multiply(radixToPower);
result = result.add(goog.math.Integer.fromNumber(value));
}
}
return result;
};
/**
* A number used repeatedly in calculations. This must appear before the first
* call to the from* functions below.
* @type {number}
* @private
*/
goog.math.Integer.TWO_PWR_32_DBL_ = (1 << 16) * (1 << 16);
/** @type {!goog.math.Integer} */
goog.math.Integer.ZERO = goog.math.Integer.fromInt(0);
/** @type {!goog.math.Integer} */
goog.math.Integer.ONE = goog.math.Integer.fromInt(1);
/**
* @type {!goog.math.Integer}
* @private
*/
goog.math.Integer.TWO_PWR_24_ = goog.math.Integer.fromInt(1 << 24);
/**
* Returns the value, assuming it is a 32-bit integer.
* @return {number} The corresponding int value.
*/
goog.math.Integer.prototype.toInt = function() {
return this.bits_.length > 0 ? this.bits_[0] : this.sign_;
};
/** @return {number} The closest floating-point representation to this value. */
goog.math.Integer.prototype.toNumber = function() {
if (this.isNegative()) {
return -this.negate().toNumber();
} else {
var val = 0;
var pow = 1;
for (var i = 0; i < this.bits_.length; i++) {
val += this.getBitsUnsigned(i) * pow;
pow *= goog.math.Integer.TWO_PWR_32_DBL_;
}
return val;
}
};
/**
* @param {number=} opt_radix The radix in which the text should be written.
* @return {string} The textual representation of this value.
* @override
*/
goog.math.Integer.prototype.toString = function(opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (this.isZero()) {
return '0';
} else if (this.isNegative()) {
return '-' + this.negate().toString(radix);
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 6));
var rem = this;
var result = '';
while (true) {
var remDiv = rem.divide(radixToPower);
var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
var digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero()) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
/**
* Returns the index-th 32-bit (signed) piece of the Integer according to
* little-endian order (i.e., index 0 contains the smallest bits).
* @param {number} index The index in question.
* @return {number} The requested 32-bits as a signed number.
*/
goog.math.Integer.prototype.getBits = function(index) {
if (index < 0) {
return 0; // Allowing this simplifies bit shifting operations below...
} else if (index < this.bits_.length) {
return this.bits_[index];
} else {
return this.sign_;
}
};
/**
* Returns the index-th 32-bit piece as an unsigned number.
* @param {number} index The index in question.
* @return {number} The requested 32-bits as an unsigned number.
*/
goog.math.Integer.prototype.getBitsUnsigned = function(index) {
var val = this.getBits(index);
return val >= 0 ? val : goog.math.Integer.TWO_PWR_32_DBL_ + val;
};
/** @return {number} The sign bit of this number, -1 or 0. */
goog.math.Integer.prototype.getSign = function() {
return this.sign_;
};
/** @return {boolean} Whether this value is zero. */
goog.math.Integer.prototype.isZero = function() {
if (this.sign_ != 0) {
return false;
}
for (var i = 0; i < this.bits_.length; i++) {
if (this.bits_[i] != 0) {
return false;
}
}
return true;
};
/** @return {boolean} Whether this value is negative. */
goog.math.Integer.prototype.isNegative = function() {
return this.sign_ == -1;
};
/** @return {boolean} Whether this value is odd. */
goog.math.Integer.prototype.isOdd = function() {
return (this.bits_.length == 0) && (this.sign_ == -1) ||
(this.bits_.length > 0) && ((this.bits_[0] & 1) != 0);
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer equals the other.
*/
goog.math.Integer.prototype.equals = function(other) {
if (this.sign_ != other.sign_) {
return false;
}
var len = Math.max(this.bits_.length, other.bits_.length);
for (var i = 0; i < len; i++) {
if (this.getBits(i) != other.getBits(i)) {
return false;
}
}
return true;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer does not equal the other.
*/
goog.math.Integer.prototype.notEquals = function(other) {
return !this.equals(other);
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is greater than the other.
*/
goog.math.Integer.prototype.greaterThan = function(other) {
return this.compare(other) > 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is greater than or equal to the other.
*/
goog.math.Integer.prototype.greaterThanOrEqual = function(other) {
return this.compare(other) >= 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is less than the other.
*/
goog.math.Integer.prototype.lessThan = function(other) {
return this.compare(other) < 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is less than or equal to the other.
*/
goog.math.Integer.prototype.lessThanOrEqual = function(other) {
return this.compare(other) <= 0;
};
/**
* Compares this Integer with the given one.
* @param {goog.math.Integer} other Integer to compare against.
* @return {number} 0 if they are the same, 1 if the this is greater, and -1
* if the given one is greater.
*/
goog.math.Integer.prototype.compare = function(other) {
var diff = this.subtract(other);
if (diff.isNegative()) {
return -1;
} else if (diff.isZero()) {
return 0;
} else {
return +1;
}
};
/**
* Returns an integer with only the first numBits bits of this value, sign
* extended from the final bit.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} The shorted integer value.
*/
goog.math.Integer.prototype.shorten = function(numBits) {
var arr_index = (numBits - 1) >> 5;
var bit_index = (numBits - 1) % 32;
var bits = [];
for (var i = 0; i < arr_index; i++) {
bits[i] = this.getBits(i);
}
var sigBits = bit_index == 31 ? 0xFFFFFFFF : (1 << (bit_index + 1)) - 1;
var val = this.getBits(arr_index) & sigBits;
if (val & (1 << bit_index)) {
val |= 0xFFFFFFFF - sigBits;
bits[arr_index] = val;
return new goog.math.Integer(bits, -1);
} else {
bits[arr_index] = val;
return new goog.math.Integer(bits, 0);
}
};
/** @return {!goog.math.Integer} The negation of this value. */
goog.math.Integer.prototype.negate = function() {
return this.not().add(goog.math.Integer.ONE);
};
/**
* Returns the sum of this and the given Integer.
* @param {goog.math.Integer} other The Integer to add to this.
* @return {!goog.math.Integer} The Integer result.
*/
goog.math.Integer.prototype.add = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
var carry = 0;
for (var i = 0; i <= len; i++) {
var a1 = this.getBits(i) >>> 16;
var a0 = this.getBits(i) & 0xFFFF;
var b1 = other.getBits(i) >>> 16;
var b0 = other.getBits(i) & 0xFFFF;
var c0 = carry + a0 + b0;
var c1 = (c0 >>> 16) + a1 + b1;
carry = c1 >>> 16;
c0 &= 0xFFFF;
c1 &= 0xFFFF;
arr[i] = (c1 << 16) | c0;
}
return goog.math.Integer.fromBits(arr);
};
/**
* Returns the difference of this and the given Integer.
* @param {goog.math.Integer} other The Integer to subtract from this.
* @return {!goog.math.Integer} The Integer result.
*/
goog.math.Integer.prototype.subtract = function(other) {
return this.add(other.negate());
};
/**
* Returns the product of this and the given Integer.
* @param {goog.math.Integer} other The Integer to multiply against this.
* @return {!goog.math.Integer} The product of this and the other.
*/
goog.math.Integer.prototype.multiply = function(other) {
if (this.isZero()) {
return goog.math.Integer.ZERO;
} else if (other.isZero()) {
return goog.math.Integer.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().multiply(other.negate());
} else {
return this.negate().multiply(other).negate();
}
} else if (other.isNegative()) {
return this.multiply(other.negate()).negate();
}
// If both numbers are small, use float multiplication
if (this.lessThan(goog.math.Integer.TWO_PWR_24_) &&
other.lessThan(goog.math.Integer.TWO_PWR_24_)) {
return goog.math.Integer.fromNumber(this.toNumber() * other.toNumber());
}
// Fill in an array of 16-bit products.
var len = this.bits_.length + other.bits_.length;
var arr = [];
for (var i = 0; i < 2 * len; i++) {
arr[i] = 0;
}
for (var i = 0; i < this.bits_.length; i++) {
for (var j = 0; j < other.bits_.length; j++) {
var a1 = this.getBits(i) >>> 16;
var a0 = this.getBits(i) & 0xFFFF;
var b1 = other.getBits(j) >>> 16;
var b0 = other.getBits(j) & 0xFFFF;
arr[2 * i + 2 * j] += a0 * b0;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j);
arr[2 * i + 2 * j + 1] += a1 * b0;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 1] += a0 * b1;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 2] += a1 * b1;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 2);
}
}
// Combine the 16-bit values into 32-bit values.
for (var i = 0; i < len; i++) {
arr[i] = (arr[2 * i + 1] << 16) | arr[2 * i];
}
for (var i = len; i < 2 * len; i++) {
arr[i] = 0;
}
return new goog.math.Integer(arr, 0);
};
/**
* Carries any overflow from the given index into later entries.
* @param {Array.<number>} bits Array of 16-bit values in little-endian order.
* @param {number} index The index in question.
* @private
*/
goog.math.Integer.carry16_ = function(bits, index) {
while ((bits[index] & 0xFFFF) != bits[index]) {
bits[index + 1] += bits[index] >>> 16;
bits[index] &= 0xFFFF;
}
};
/**
* Returns this Integer divided by the given one.
* @param {goog.math.Integer} other Th Integer to divide this by.
* @return {!goog.math.Integer} This value divided by the given one.
*/
goog.math.Integer.prototype.divide = function(other) {
if (other.isZero()) {
throw Error('division by zero');
} else if (this.isZero()) {
return goog.math.Integer.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().divide(other.negate());
} else {
return this.negate().divide(other).negate();
}
} else if (other.isNegative()) {
return this.divide(other.negate()).negate();
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = goog.math.Integer.ZERO;
var rem = this;
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = goog.math.Integer.fromNumber(approx);
var approxRem = approxRes.multiply(other);
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta;
approxRes = goog.math.Integer.fromNumber(approx);
approxRem = approxRes.multiply(other);
}
// We know the answer can't be zero... and actually, zero would cause
// infinite recursion since we would make no progress.
if (approxRes.isZero()) {
approxRes = goog.math.Integer.ONE;
}
res = res.add(approxRes);
rem = rem.subtract(approxRem);
}
return res;
};
/**
* Returns this Integer modulo the given one.
* @param {goog.math.Integer} other The Integer by which to mod.
* @return {!goog.math.Integer} This value modulo the given one.
*/
goog.math.Integer.prototype.modulo = function(other) {
return this.subtract(this.divide(other).multiply(other));
};
/** @return {!goog.math.Integer} The bitwise-NOT of this value. */
goog.math.Integer.prototype.not = function() {
var len = this.bits_.length;
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = ~this.bits_[i];
}
return new goog.math.Integer(arr, ~this.sign_);
};
/**
* Returns the bitwise-AND of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to AND with this.
* @return {!goog.math.Integer} The bitwise-AND of this and the other.
*/
goog.math.Integer.prototype.and = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) & other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ & other.sign_);
};
/**
* Returns the bitwise-OR of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to OR with this.
* @return {!goog.math.Integer} The bitwise-OR of this and the other.
*/
goog.math.Integer.prototype.or = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) | other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ | other.sign_);
};
/**
* Returns the bitwise-XOR of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to XOR with this.
* @return {!goog.math.Integer} The bitwise-XOR of this and the other.
*/
goog.math.Integer.prototype.xor = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) ^ other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ ^ other.sign_);
};
/**
* Returns this value with bits shifted to the left by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} This shifted to the left by the given amount.
*/
goog.math.Integer.prototype.shiftLeft = function(numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = this.bits_.length + arr_delta + (bit_delta > 0 ? 1 : 0);
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (this.getBits(i - arr_delta) << bit_delta) |
(this.getBits(i - arr_delta - 1) >>> (32 - bit_delta));
} else {
arr[i] = this.getBits(i - arr_delta);
}
}
return new goog.math.Integer(arr, this.sign_);
};
/**
* Returns this value with bits shifted to the right by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} This shifted to the right by the given amount.
*/
goog.math.Integer.prototype.shiftRight = function(numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = this.bits_.length - arr_delta;
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (this.getBits(i + arr_delta) >>> bit_delta) |
(this.getBits(i + arr_delta + 1) << (32 - bit_delta));
} else {
arr[i] = this.getBits(i + arr_delta);
}
}
return new goog.math.Integer(arr, this.sign_);
};

View File

@@ -0,0 +1,64 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The base interface for one-dimensional data interpolation.
*
*/
goog.provide('goog.math.interpolator.Interpolator1');
/**
* An interface for one dimensional data interpolation.
* @interface
*/
goog.math.interpolator.Interpolator1 = function() {
};
/**
* Sets the data to be interpolated. Note that the data points are expected
* to be sorted according to their abscissa values and not have duplicate
* values. E.g. calling setData([0, 0, 1], [1, 1, 3]) may give undefined
* results, the correct call should be setData([0, 1], [1, 3]).
* Calling setData multiple times does not merge the data samples. The last
* call to setData is the one used when computing the interpolation.
* @param {!Array.<number>} x The abscissa of the data points.
* @param {!Array.<number>} y The ordinate of the data points.
*/
goog.math.interpolator.Interpolator1.prototype.setData;
/**
* Computes the interpolated value at abscissa x. If x is outside the range
* of the data points passed in setData, the value is extrapolated.
* @param {number} x The abscissa to sample at.
* @return {number} The interpolated value at abscissa x.
*/
goog.math.interpolator.Interpolator1.prototype.interpolate;
/**
* Computes the inverse interpolator. That is, it returns invInterp s.t.
* this.interpolate(invInterp.interpolate(t))) = t. Note that the inverse
* interpolator is only well defined if the data being interpolated is
* 'invertible', i.e. it represents a bijective function.
* In addition, the returned interpolator is only guaranteed to give the exact
* inverse at the input data passed in getData.
* If 'this' has no data, the returned Interpolator will be empty as well.
* @return {!goog.math.interpolator.Interpolator1} The inverse interpolator.
*/
goog.math.interpolator.Interpolator1.prototype.getInverse;

View File

@@ -0,0 +1,82 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A one dimensional linear interpolator.
*
*/
goog.provide('goog.math.interpolator.Linear1');
goog.require('goog.array');
goog.require('goog.math');
goog.require('goog.math.interpolator.Interpolator1');
/**
* A one dimensional linear interpolator.
* @implements {goog.math.interpolator.Interpolator1}
* @constructor
*/
goog.math.interpolator.Linear1 = function() {
/**
* The abscissa of the data points.
* @type {!Array.<number>}
* @private
*/
this.x_ = [];
/**
* The ordinate of the data points.
* @type {!Array.<number>}
* @private
*/
this.y_ = [];
};
/** @override */
goog.math.interpolator.Linear1.prototype.setData = function(x, y) {
goog.asserts.assert(x.length == y.length,
'input arrays to setData should have the same length');
if (x.length == 1) {
this.x_ = [x[0], x[0] + 1];
this.y_ = [y[0], y[0]];
} else {
this.x_ = x.slice();
this.y_ = y.slice();
}
};
/** @override */
goog.math.interpolator.Linear1.prototype.interpolate = function(x) {
var pos = goog.array.binarySearch(this.x_, x);
if (pos < 0) {
pos = -pos - 2;
}
pos = goog.math.clamp(pos, 0, this.x_.length - 2);
var progress = (x - this.x_[pos]) / (this.x_[pos + 1] - this.x_[pos]);
return goog.math.lerp(this.y_[pos], this.y_[pos + 1], progress);
};
/** @override */
goog.math.interpolator.Linear1.prototype.getInverse = function() {
var interpolator = new goog.math.interpolator.Linear1();
interpolator.setData(this.y_, this.x_);
return interpolator;
};

View File

@@ -0,0 +1,81 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A one dimensional monotone cubic spline interpolator.
*
* See http://en.wikipedia.org/wiki/Monotone_cubic_interpolation.
*
*/
goog.provide('goog.math.interpolator.Pchip1');
goog.require('goog.math');
goog.require('goog.math.interpolator.Spline1');
/**
* A one dimensional monotone cubic spline interpolator.
* @extends {goog.math.interpolator.Spline1}
* @constructor
*/
goog.math.interpolator.Pchip1 = function() {
goog.base(this);
};
goog.inherits(goog.math.interpolator.Pchip1, goog.math.interpolator.Spline1);
/** @override */
goog.math.interpolator.Pchip1.prototype.computeDerivatives = function(
dx, slope) {
var len = dx.length;
var deriv = new Array(len + 1);
for (var i = 1; i < len; ++i) {
if (goog.math.sign(slope[i - 1]) * goog.math.sign(slope[i]) <= 0) {
deriv[i] = 0;
} else {
var w1 = 2 * dx[i] + dx[i - 1];
var w2 = dx[i] + 2 * dx[i - 1];
deriv[i] = (w1 + w2) / (w1 / slope[i - 1] + w2 / slope[i]);
}
}
deriv[0] = this.computeDerivativeAtBoundary_(
dx[0], dx[1], slope[0], slope[1]);
deriv[len] = this.computeDerivativeAtBoundary_(
dx[len - 1], dx[len - 2], slope[len - 1], slope[len - 2]);
return deriv;
};
/**
* Computes the derivative of a data point at a boundary.
* @param {number} dx0 The spacing of the 1st data point.
* @param {number} dx1 The spacing of the 2nd data point.
* @param {number} slope0 The slope of the 1st data point.
* @param {number} slope1 The slope of the 2nd data point.
* @return {number} The derivative at the 1st data point.
* @private
*/
goog.math.interpolator.Pchip1.prototype.computeDerivativeAtBoundary_ = function(
dx0, dx1, slope0, slope1) {
var deriv = ((2 * dx0 + dx1) * slope0 - dx0 * slope1) / (dx0 + dx1);
if (goog.math.sign(deriv) != goog.math.sign(slope0)) {
deriv = 0;
} else if (goog.math.sign(slope0) != goog.math.sign(slope1) &&
Math.abs(deriv) > Math.abs(3 * slope0)) {
deriv = 3 * slope0;
}
return deriv;
};

View File

@@ -0,0 +1,202 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A one dimensional cubic spline interpolator with not-a-knot
* boundary conditions.
*
* See http://en.wikipedia.org/wiki/Spline_interpolation.
*
*/
goog.provide('goog.math.interpolator.Spline1');
goog.require('goog.array');
goog.require('goog.math');
goog.require('goog.math.interpolator.Interpolator1');
goog.require('goog.math.tdma');
/**
* A one dimensional cubic spline interpolator with natural boundary conditions.
* @implements {goog.math.interpolator.Interpolator1}
* @constructor
*/
goog.math.interpolator.Spline1 = function() {
/**
* The abscissa of the data points.
* @type {!Array.<number>}
* @private
*/
this.x_ = [];
/**
* The spline interval coefficients.
* Note that, in general, the length of coeffs and x is not the same.
* @type {!Array.<!Array.<number>>}
* @private
*/
this.coeffs_ = [[0, 0, 0, Number.NaN]];
};
/** @override */
goog.math.interpolator.Spline1.prototype.setData = function(x, y) {
goog.asserts.assert(x.length == y.length,
'input arrays to setData should have the same length');
if (x.length > 0) {
this.coeffs_ = this.computeSplineCoeffs_(x, y);
this.x_ = x.slice();
} else {
this.coeffs_ = [[0, 0, 0, Number.NaN]];
this.x_ = [];
}
};
/** @override */
goog.math.interpolator.Spline1.prototype.interpolate = function(x) {
var pos = goog.array.binarySearch(this.x_, x);
if (pos < 0) {
pos = -pos - 2;
}
pos = goog.math.clamp(pos, 0, this.coeffs_.length - 1);
var d = x - this.x_[pos];
var d2 = d * d;
var d3 = d2 * d;
var coeffs = this.coeffs_[pos];
return coeffs[0] * d3 + coeffs[1] * d2 + coeffs[2] * d + coeffs[3];
};
/**
* Solve for the spline coefficients such that the spline precisely interpolates
* the data points.
* @param {Array.<number>} x The abscissa of the spline data points.
* @param {Array.<number>} y The ordinate of the spline data points.
* @return {!Array.<!Array.<number>>} The spline interval coefficients.
* @private
*/
goog.math.interpolator.Spline1.prototype.computeSplineCoeffs_ = function(x, y) {
var nIntervals = x.length - 1;
var dx = new Array(nIntervals);
var delta = new Array(nIntervals);
for (var i = 0; i < nIntervals; ++i) {
dx[i] = x[i + 1] - x[i];
delta[i] = (y[i + 1] - y[i]) / dx[i];
}
// Compute the spline coefficients from the 1st order derivatives.
var coeffs = [];
if (nIntervals == 0) {
// Nearest neighbor interpolation.
coeffs[0] = [0, 0, 0, y[0]];
} else if (nIntervals == 1) {
// Straight line interpolation.
coeffs[0] = [0, 0, delta[0], y[0]];
} else if (nIntervals == 2) {
// Parabola interpolation.
var c3 = 0;
var c2 = (delta[1] - delta[0]) / (dx[0] + dx[1]);
var c1 = delta[0] - c2 * dx[0];
var c0 = y[0];
coeffs[0] = [c3, c2, c1, c0];
} else {
// General Spline interpolation. Compute the 1st order derivatives from
// the Spline equations.
var deriv = this.computeDerivatives(dx, delta);
for (var i = 0; i < nIntervals; ++i) {
var c3 = (deriv[i] - 2 * delta[i] + deriv[i + 1]) / (dx[i] * dx[i]);
var c2 = (3 * delta[i] - 2 * deriv[i] - deriv[i + 1]) / dx[i];
var c1 = deriv[i];
var c0 = y[i];
coeffs[i] = [c3, c2, c1, c0];
}
}
return coeffs;
};
/**
* Computes the derivative at each point of the spline such that
* the curve is C2. It uses not-a-knot boundary conditions.
* @param {Array.<number>} dx The spacing between consecutive data points.
* @param {Array.<number>} slope The slopes between consecutive data points.
* @return {Array.<number>} The Spline derivative at each data point.
* @protected
*/
goog.math.interpolator.Spline1.prototype.computeDerivatives = function(
dx, slope) {
var nIntervals = dx.length;
// Compute the main diagonal of the system of equations.
var mainDiag = new Array(nIntervals + 1);
mainDiag[0] = dx[1];
for (var i = 1; i < nIntervals; ++i) {
mainDiag[i] = 2 * (dx[i] + dx[i - 1]);
}
mainDiag[nIntervals] = dx[nIntervals - 2];
// Compute the sub diagonal of the system of equations.
var subDiag = new Array(nIntervals);
for (var i = 0; i < nIntervals; ++i) {
subDiag[i] = dx[i + 1];
}
subDiag[nIntervals - 1] = dx[nIntervals - 2] + dx[nIntervals - 1];
// Compute the super diagonal of the system of equations.
var supDiag = new Array(nIntervals);
supDiag[0] = dx[0] + dx[1];
for (var i = 1; i < nIntervals; ++i) {
supDiag[i] = dx[i - 1];
}
// Compute the right vector of the system of equations.
var vecRight = new Array(nIntervals + 1);
vecRight[0] = ((dx[0] + 2 * supDiag[0]) * dx[1] * slope[0] +
dx[0] * dx[0] * slope[1]) / supDiag[0];
for (var i = 1; i < nIntervals; ++i) {
vecRight[i] = 3 * (dx[i] * slope[i - 1] + dx[i - 1] * slope[i]);
}
vecRight[nIntervals] = (dx[nIntervals - 1] * dx[nIntervals - 1] *
slope[nIntervals - 2] + (2 * subDiag[nIntervals - 1] +
dx[nIntervals - 1]) * dx[nIntervals - 2] * slope[nIntervals - 1]) /
subDiag[nIntervals - 1];
// Solve the system of equations.
var deriv = goog.math.tdma.solve(
subDiag, mainDiag, supDiag, vecRight);
return deriv;
};
/**
* Note that the inverse of a cubic spline is not a cubic spline in general.
* As a result the inverse implementation is only approximate. In
* particular, it only guarantees the exact inverse at the original input data
* points passed to setData.
* @override
*/
goog.math.interpolator.Spline1.prototype.getInverse = function() {
var interpolator = new goog.math.interpolator.Spline1();
var y = [];
for (var i = 0; i < this.x_.length; i++) {
y[i] = this.interpolate(this.x_[i]);
}
interpolator.setData(y, this.x_);
return interpolator;
};

View File

@@ -0,0 +1,177 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a line in 2D space.
*
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.math.Line');
goog.require('goog.math');
goog.require('goog.math.Coordinate');
/**
* Object representing a line.
* @param {number} x0 X coordinate of the start point.
* @param {number} y0 Y coordinate of the start point.
* @param {number} x1 X coordinate of the end point.
* @param {number} y1 Y coordinate of the end point.
* @constructor
*/
goog.math.Line = function(x0, y0, x1, y1) {
/**
* X coordinate of the first point.
* @type {number}
*/
this.x0 = x0;
/**
* Y coordinate of the first point.
* @type {number}
*/
this.y0 = y0;
/**
* X coordinate of the first control point.
* @type {number}
*/
this.x1 = x1;
/**
* Y coordinate of the first control point.
* @type {number}
*/
this.y1 = y1;
};
/**
* @return {!goog.math.Line} A copy of this line.
*/
goog.math.Line.prototype.clone = function() {
return new goog.math.Line(this.x0, this.y0, this.x1, this.y1);
};
/**
* Tests whether the given line is exactly the same as this one.
* @param {goog.math.Line} other The other line.
* @return {boolean} Whether the given line is the same as this one.
*/
goog.math.Line.prototype.equals = function(other) {
return this.x0 == other.x0 && this.y0 == other.y0 &&
this.x1 == other.x1 && this.y1 == other.y1;
};
/**
* @return {number} The squared length of the line segment used to define the
* line.
*/
goog.math.Line.prototype.getSegmentLengthSquared = function() {
var xdist = this.x1 - this.x0;
var ydist = this.y1 - this.y0;
return xdist * xdist + ydist * ydist;
};
/**
* @return {number} The length of the line segment used to define the line.
*/
goog.math.Line.prototype.getSegmentLength = function() {
return Math.sqrt(this.getSegmentLengthSquared());
};
/**
* Computes the interpolation parameter for the point on the line closest to
* a given point.
* @param {number|goog.math.Coordinate} x The x coordinate of the point, or
* a coordinate object.
* @param {number=} opt_y The y coordinate of the point - required if x is a
* number, ignored if x is a goog.math.Coordinate.
* @return {number} The interpolation parameter of the point on the line
* closest to the given point.
* @private
*/
goog.math.Line.prototype.getClosestLinearInterpolation_ = function(x, opt_y) {
var y;
if (x instanceof goog.math.Coordinate) {
y = x.y;
x = x.x;
} else {
y = opt_y;
}
var x0 = this.x0;
var y0 = this.y0;
var xChange = this.x1 - x0;
var yChange = this.y1 - y0;
return ((x - x0) * xChange + (y - y0) * yChange) /
this.getSegmentLengthSquared();
};
/**
* Returns the point on the line segment proportional to t, where for t = 0 we
* return the starting point and for t = 1 we return the end point. For t < 0
* or t > 1 we extrapolate along the line defined by the line segment.
* @param {number} t The interpolation parameter along the line segment.
* @return {!goog.math.Coordinate} The point on the line segment at t.
*/
goog.math.Line.prototype.getInterpolatedPoint = function(t) {
return new goog.math.Coordinate(
goog.math.lerp(this.x0, this.x1, t),
goog.math.lerp(this.y0, this.y1, t));
};
/**
* Computes the point on the line closest to a given point. Note that a line
* in this case is defined as the infinite line going through the start and end
* points. To find the closest point on the line segment itself see
* {@see #getClosestSegmentPoint}.
* @param {number|goog.math.Coordinate} x The x coordinate of the point, or
* a coordinate object.
* @param {number=} opt_y The y coordinate of the point - required if x is a
* number, ignored if x is a goog.math.Coordinate.
* @return {!goog.math.Coordinate} The point on the line closest to the given
* point.
*/
goog.math.Line.prototype.getClosestPoint = function(x, opt_y) {
return this.getInterpolatedPoint(
this.getClosestLinearInterpolation_(x, opt_y));
};
/**
* Computes the point on the line segment closest to a given point.
* @param {number|goog.math.Coordinate} x The x coordinate of the point, or
* a coordinate object.
* @param {number=} opt_y The y coordinate of the point - required if x is a
* number, ignored if x is a goog.math.Coordinate.
* @return {!goog.math.Coordinate} The point on the line segment closest to the
* given point.
*/
goog.math.Line.prototype.getClosestSegmentPoint = function(x, opt_y) {
return this.getInterpolatedPoint(
goog.math.clamp(this.getClosestLinearInterpolation_(x, opt_y), 0, 1));
};

View File

@@ -0,0 +1,802 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines a Long class for representing a 64-bit two's-complement
* integer value, which faithfully simulates the behavior of a Java "long". This
* implementation is derived from LongLib in GWT.
*
*/
goog.provide('goog.math.Long');
/**
* Constructs a 64-bit two's-complement integer, given its low and high 32-bit
* values as *signed* integers. See the from* functions below for more
* convenient ways of constructing Longs.
*
* The internal representation of a long is the two given signed, 32-bit values.
* We use 32-bit pieces because these are the size of integers on which
* Javascript performs bit-operations. For operations like addition and
* multiplication, we split each number into 16-bit pieces, which can easily be
* multiplied within Javascript's floating-point representation without overflow
* or change in sign.
*
* In the algorithms below, we frequently reduce the negative case to the
* positive case by negating the input(s) and then post-processing the result.
* Note that we must ALWAYS check specially whether those values are MIN_VALUE
* (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
* a positive number, it overflows back into a negative). Not handling this
* case would often result in infinite recursion.
*
* @param {number} low The low (signed) 32 bits of the long.
* @param {number} high The high (signed) 32 bits of the long.
* @constructor
*/
goog.math.Long = function(low, high) {
/**
* @type {number}
* @private
*/
this.low_ = low | 0; // force into 32 signed bits.
/**
* @type {number}
* @private
*/
this.high_ = high | 0; // force into 32 signed bits.
};
// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
// from* methods on which they depend.
/**
* A cache of the Long representations of small integer values.
* @type {!Object}
* @private
*/
goog.math.Long.IntCache_ = {};
/**
* Returns a Long representing the given (32-bit) integer value.
* @param {number} value The 32-bit integer in question.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromInt = function(value) {
if (-128 <= value && value < 128) {
var cachedObj = goog.math.Long.IntCache_[value];
if (cachedObj) {
return cachedObj;
}
}
var obj = new goog.math.Long(value | 0, value < 0 ? -1 : 0);
if (-128 <= value && value < 128) {
goog.math.Long.IntCache_[value] = obj;
}
return obj;
};
/**
* Returns a Long representing the given value, provided that it is a finite
* number. Otherwise, zero is returned.
* @param {number} value The number in question.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromNumber = function(value) {
if (isNaN(value) || !isFinite(value)) {
return goog.math.Long.ZERO;
} else if (value <= -goog.math.Long.TWO_PWR_63_DBL_) {
return goog.math.Long.MIN_VALUE;
} else if (value + 1 >= goog.math.Long.TWO_PWR_63_DBL_) {
return goog.math.Long.MAX_VALUE;
} else if (value < 0) {
return goog.math.Long.fromNumber(-value).negate();
} else {
return new goog.math.Long(
(value % goog.math.Long.TWO_PWR_32_DBL_) | 0,
(value / goog.math.Long.TWO_PWR_32_DBL_) | 0);
}
};
/**
* Returns a Long representing the 64-bit integer that comes by concatenating
* the given high and low bits. Each is assumed to use 32 bits.
* @param {number} lowBits The low 32-bits.
* @param {number} highBits The high 32-bits.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromBits = function(lowBits, highBits) {
return new goog.math.Long(lowBits, highBits);
};
/**
* Returns a Long representation of the given string, written using the given
* radix.
* @param {string} str The textual representation of the Long.
* @param {number=} opt_radix The radix in which the text is written.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromString = function(str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return goog.math.Long.fromString(str.substring(1), radix).negate();
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character: ' + str);
}
// Do several (8) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 8));
var result = goog.math.Long.ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = goog.math.Long.fromNumber(Math.pow(radix, size));
result = result.multiply(power).add(goog.math.Long.fromNumber(value));
} else {
result = result.multiply(radixToPower);
result = result.add(goog.math.Long.fromNumber(value));
}
}
return result;
};
// NOTE: the compiler should inline these constant values below and then remove
// these variables, so there should be no runtime penalty for these.
/**
* Number used repeated below in calculations. This must appear before the
* first call to any from* function below.
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_16_DBL_ = 1 << 16;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_24_DBL_ = 1 << 24;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_32_DBL_ =
goog.math.Long.TWO_PWR_16_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_31_DBL_ =
goog.math.Long.TWO_PWR_32_DBL_ / 2;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_48_DBL_ =
goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_64_DBL_ =
goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_32_DBL_;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_63_DBL_ =
goog.math.Long.TWO_PWR_64_DBL_ / 2;
/** @type {!goog.math.Long} */
goog.math.Long.ZERO = goog.math.Long.fromInt(0);
/** @type {!goog.math.Long} */
goog.math.Long.ONE = goog.math.Long.fromInt(1);
/** @type {!goog.math.Long} */
goog.math.Long.NEG_ONE = goog.math.Long.fromInt(-1);
/** @type {!goog.math.Long} */
goog.math.Long.MAX_VALUE =
goog.math.Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
/** @type {!goog.math.Long} */
goog.math.Long.MIN_VALUE = goog.math.Long.fromBits(0, 0x80000000 | 0);
/**
* @type {!goog.math.Long}
* @private
*/
goog.math.Long.TWO_PWR_24_ = goog.math.Long.fromInt(1 << 24);
/** @return {number} The value, assuming it is a 32-bit integer. */
goog.math.Long.prototype.toInt = function() {
return this.low_;
};
/** @return {number} The closest floating-point representation to this value. */
goog.math.Long.prototype.toNumber = function() {
return this.high_ * goog.math.Long.TWO_PWR_32_DBL_ +
this.getLowBitsUnsigned();
};
/**
* @param {number=} opt_radix The radix in which the text should be written.
* @return {string} The textual representation of this value.
* @override
*/
goog.math.Long.prototype.toString = function(opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (this.isZero()) {
return '0';
}
if (this.isNegative()) {
if (this.equals(goog.math.Long.MIN_VALUE)) {
// We need to change the Long value before it can be negated, so we remove
// the bottom-most digit in this base and then recurse to do the rest.
var radixLong = goog.math.Long.fromNumber(radix);
var div = this.div(radixLong);
var rem = div.multiply(radixLong).subtract(this);
return div.toString(radix) + rem.toInt().toString(radix);
} else {
return '-' + this.negate().toString(radix);
}
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 6));
var rem = this;
var result = '';
while (true) {
var remDiv = rem.div(radixToPower);
var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
var digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero()) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
/** @return {number} The high 32-bits as a signed value. */
goog.math.Long.prototype.getHighBits = function() {
return this.high_;
};
/** @return {number} The low 32-bits as a signed value. */
goog.math.Long.prototype.getLowBits = function() {
return this.low_;
};
/** @return {number} The low 32-bits as an unsigned value. */
goog.math.Long.prototype.getLowBitsUnsigned = function() {
return (this.low_ >= 0) ?
this.low_ : goog.math.Long.TWO_PWR_32_DBL_ + this.low_;
};
/**
* @return {number} Returns the number of bits needed to represent the absolute
* value of this Long.
*/
goog.math.Long.prototype.getNumBitsAbs = function() {
if (this.isNegative()) {
if (this.equals(goog.math.Long.MIN_VALUE)) {
return 64;
} else {
return this.negate().getNumBitsAbs();
}
} else {
var val = this.high_ != 0 ? this.high_ : this.low_;
for (var bit = 31; bit > 0; bit--) {
if ((val & (1 << bit)) != 0) {
break;
}
}
return this.high_ != 0 ? bit + 33 : bit + 1;
}
};
/** @return {boolean} Whether this value is zero. */
goog.math.Long.prototype.isZero = function() {
return this.high_ == 0 && this.low_ == 0;
};
/** @return {boolean} Whether this value is negative. */
goog.math.Long.prototype.isNegative = function() {
return this.high_ < 0;
};
/** @return {boolean} Whether this value is odd. */
goog.math.Long.prototype.isOdd = function() {
return (this.low_ & 1) == 1;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long equals the other.
*/
goog.math.Long.prototype.equals = function(other) {
return (this.high_ == other.high_) && (this.low_ == other.low_);
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long does not equal the other.
*/
goog.math.Long.prototype.notEquals = function(other) {
return (this.high_ != other.high_) || (this.low_ != other.low_);
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is less than the other.
*/
goog.math.Long.prototype.lessThan = function(other) {
return this.compare(other) < 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is less than or equal to the other.
*/
goog.math.Long.prototype.lessThanOrEqual = function(other) {
return this.compare(other) <= 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is greater than the other.
*/
goog.math.Long.prototype.greaterThan = function(other) {
return this.compare(other) > 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is greater than or equal to the other.
*/
goog.math.Long.prototype.greaterThanOrEqual = function(other) {
return this.compare(other) >= 0;
};
/**
* Compares this Long with the given one.
* @param {goog.math.Long} other Long to compare against.
* @return {number} 0 if they are the same, 1 if the this is greater, and -1
* if the given one is greater.
*/
goog.math.Long.prototype.compare = function(other) {
if (this.equals(other)) {
return 0;
}
var thisNeg = this.isNegative();
var otherNeg = other.isNegative();
if (thisNeg && !otherNeg) {
return -1;
}
if (!thisNeg && otherNeg) {
return 1;
}
// at this point, the signs are the same, so subtraction will not overflow
if (this.subtract(other).isNegative()) {
return -1;
} else {
return 1;
}
};
/** @return {!goog.math.Long} The negation of this value. */
goog.math.Long.prototype.negate = function() {
if (this.equals(goog.math.Long.MIN_VALUE)) {
return goog.math.Long.MIN_VALUE;
} else {
return this.not().add(goog.math.Long.ONE);
}
};
/**
* Returns the sum of this and the given Long.
* @param {goog.math.Long} other Long to add to this one.
* @return {!goog.math.Long} The sum of this and the given Long.
*/
goog.math.Long.prototype.add = function(other) {
// Divide each number into 4 chunks of 16 bits, and then sum the chunks.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns the difference of this and the given Long.
* @param {goog.math.Long} other Long to subtract from this.
* @return {!goog.math.Long} The difference of this and the given Long.
*/
goog.math.Long.prototype.subtract = function(other) {
return this.add(other.negate());
};
/**
* Returns the product of this and the given long.
* @param {goog.math.Long} other Long to multiply with this.
* @return {!goog.math.Long} The product of this and the other.
*/
goog.math.Long.prototype.multiply = function(other) {
if (this.isZero()) {
return goog.math.Long.ZERO;
} else if (other.isZero()) {
return goog.math.Long.ZERO;
}
if (this.equals(goog.math.Long.MIN_VALUE)) {
return other.isOdd() ? goog.math.Long.MIN_VALUE : goog.math.Long.ZERO;
} else if (other.equals(goog.math.Long.MIN_VALUE)) {
return this.isOdd() ? goog.math.Long.MIN_VALUE : goog.math.Long.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().multiply(other.negate());
} else {
return this.negate().multiply(other).negate();
}
} else if (other.isNegative()) {
return this.multiply(other.negate()).negate();
}
// If both longs are small, use float multiplication
if (this.lessThan(goog.math.Long.TWO_PWR_24_) &&
other.lessThan(goog.math.Long.TWO_PWR_24_)) {
return goog.math.Long.fromNumber(this.toNumber() * other.toNumber());
}
// Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
// We can skip products that would overflow.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 * b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 * b00;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c16 += a00 * b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 * b00;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a16 * b16;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a00 * b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
c48 &= 0xFFFF;
return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns this Long divided by the given one.
* @param {goog.math.Long} other Long by which to divide.
* @return {!goog.math.Long} This Long divided by the given one.
*/
goog.math.Long.prototype.div = function(other) {
if (other.isZero()) {
throw Error('division by zero');
} else if (this.isZero()) {
return goog.math.Long.ZERO;
}
if (this.equals(goog.math.Long.MIN_VALUE)) {
if (other.equals(goog.math.Long.ONE) ||
other.equals(goog.math.Long.NEG_ONE)) {
return goog.math.Long.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
} else if (other.equals(goog.math.Long.MIN_VALUE)) {
return goog.math.Long.ONE;
} else {
// At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
var halfThis = this.shiftRight(1);
var approx = halfThis.div(other).shiftLeft(1);
if (approx.equals(goog.math.Long.ZERO)) {
return other.isNegative() ? goog.math.Long.ONE : goog.math.Long.NEG_ONE;
} else {
var rem = this.subtract(other.multiply(approx));
var result = approx.add(rem.div(other));
return result;
}
}
} else if (other.equals(goog.math.Long.MIN_VALUE)) {
return goog.math.Long.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().div(other.negate());
} else {
return this.negate().div(other).negate();
}
} else if (other.isNegative()) {
return this.div(other.negate()).negate();
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = goog.math.Long.ZERO;
var rem = this;
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = goog.math.Long.fromNumber(approx);
var approxRem = approxRes.multiply(other);
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta;
approxRes = goog.math.Long.fromNumber(approx);
approxRem = approxRes.multiply(other);
}
// We know the answer can't be zero... and actually, zero would cause
// infinite recursion since we would make no progress.
if (approxRes.isZero()) {
approxRes = goog.math.Long.ONE;
}
res = res.add(approxRes);
rem = rem.subtract(approxRem);
}
return res;
};
/**
* Returns this Long modulo the given one.
* @param {goog.math.Long} other Long by which to mod.
* @return {!goog.math.Long} This Long modulo the given one.
*/
goog.math.Long.prototype.modulo = function(other) {
return this.subtract(this.div(other).multiply(other));
};
/** @return {!goog.math.Long} The bitwise-NOT of this value. */
goog.math.Long.prototype.not = function() {
return goog.math.Long.fromBits(~this.low_, ~this.high_);
};
/**
* Returns the bitwise-AND of this Long and the given one.
* @param {goog.math.Long} other The Long with which to AND.
* @return {!goog.math.Long} The bitwise-AND of this and the other.
*/
goog.math.Long.prototype.and = function(other) {
return goog.math.Long.fromBits(this.low_ & other.low_,
this.high_ & other.high_);
};
/**
* Returns the bitwise-OR of this Long and the given one.
* @param {goog.math.Long} other The Long with which to OR.
* @return {!goog.math.Long} The bitwise-OR of this and the other.
*/
goog.math.Long.prototype.or = function(other) {
return goog.math.Long.fromBits(this.low_ | other.low_,
this.high_ | other.high_);
};
/**
* Returns the bitwise-XOR of this Long and the given one.
* @param {goog.math.Long} other The Long with which to XOR.
* @return {!goog.math.Long} The bitwise-XOR of this and the other.
*/
goog.math.Long.prototype.xor = function(other) {
return goog.math.Long.fromBits(this.low_ ^ other.low_,
this.high_ ^ other.high_);
};
/**
* Returns this Long with bits shifted to the left by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the left by the given amount.
*/
goog.math.Long.prototype.shiftLeft = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var low = this.low_;
if (numBits < 32) {
var high = this.high_;
return goog.math.Long.fromBits(
low << numBits,
(high << numBits) | (low >>> (32 - numBits)));
} else {
return goog.math.Long.fromBits(0, low << (numBits - 32));
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the right by the given amount.
*/
goog.math.Long.prototype.shiftRight = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return goog.math.Long.fromBits(
(low >>> numBits) | (high << (32 - numBits)),
high >> numBits);
} else {
return goog.math.Long.fromBits(
high >> (numBits - 32),
high >= 0 ? 0 : -1);
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount, with
* zeros placed into the new leading bits.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the right by the given amount, with
* zeros placed into the new leading bits.
*/
goog.math.Long.prototype.shiftRightUnsigned = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return goog.math.Long.fromBits(
(low >>> numBits) | (high << (32 - numBits)),
high >>> numBits);
} else if (numBits == 32) {
return goog.math.Long.fromBits(high, 0);
} else {
return goog.math.Long.fromBits(high >>> (numBits - 32), 0);
}
}
};

View File

@@ -0,0 +1,387 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Additional mathematical functions.
*/
goog.provide('goog.math');
goog.require('goog.array');
goog.require('goog.asserts');
/**
* Returns a random integer greater than or equal to 0 and less than {@code a}.
* @param {number} a The upper bound for the random integer (exclusive).
* @return {number} A random integer N such that 0 <= N < a.
*/
goog.math.randomInt = function(a) {
return Math.floor(Math.random() * a);
};
/**
* Returns a random number greater than or equal to {@code a} and less than
* {@code b}.
* @param {number} a The lower bound for the random number (inclusive).
* @param {number} b The upper bound for the random number (exclusive).
* @return {number} A random number N such that a <= N < b.
*/
goog.math.uniformRandom = function(a, b) {
return a + Math.random() * (b - a);
};
/**
* Takes a number and clamps it to within the provided bounds.
* @param {number} value The input number.
* @param {number} min The minimum value to return.
* @param {number} max The maximum value to return.
* @return {number} The input number if it is within bounds, or the nearest
* number within the bounds.
*/
goog.math.clamp = function(value, min, max) {
return Math.min(Math.max(value, min), max);
};
/**
* The % operator in JavaScript returns the remainder of a / b, but differs from
* some other languages in that the result will have the same sign as the
* dividend. For example, -1 % 8 == -1, whereas in some other languages
* (such as Python) the result would be 7. This function emulates the more
* correct modulo behavior, which is useful for certain applications such as
* calculating an offset index in a circular list.
*
* @param {number} a The dividend.
* @param {number} b The divisor.
* @return {number} a % b where the result is between 0 and b (either 0 <= x < b
* or b < x <= 0, depending on the sign of b).
*/
goog.math.modulo = function(a, b) {
var r = a % b;
// If r and b differ in sign, add b to wrap the result to the correct sign.
return (r * b < 0) ? r + b : r;
};
/**
* Performs linear interpolation between values a and b. Returns the value
* between a and b proportional to x (when x is between 0 and 1. When x is
* outside this range, the return value is a linear extrapolation).
* @param {number} a A number.
* @param {number} b A number.
* @param {number} x The proportion between a and b.
* @return {number} The interpolated value between a and b.
*/
goog.math.lerp = function(a, b, x) {
return a + x * (b - a);
};
/**
* Tests whether the two values are equal to each other, within a certain
* tolerance to adjust for floating pount errors.
* @param {number} a A number.
* @param {number} b A number.
* @param {number=} opt_tolerance Optional tolerance range. Defaults
* to 0.000001. If specified, should be greater than 0.
* @return {boolean} Whether {@code a} and {@code b} are nearly equal.
*/
goog.math.nearlyEquals = function(a, b, opt_tolerance) {
return Math.abs(a - b) <= (opt_tolerance || 0.000001);
};
/**
* Standardizes an angle to be in range [0-360). Negative angles become
* positive, and values greater than 360 are returned modulo 360.
* @param {number} angle Angle in degrees.
* @return {number} Standardized angle.
*/
goog.math.standardAngle = function(angle) {
return goog.math.modulo(angle, 360);
};
/**
* Converts degrees to radians.
* @param {number} angleDegrees Angle in degrees.
* @return {number} Angle in radians.
*/
goog.math.toRadians = function(angleDegrees) {
return angleDegrees * Math.PI / 180;
};
/**
* Converts radians to degrees.
* @param {number} angleRadians Angle in radians.
* @return {number} Angle in degrees.
*/
goog.math.toDegrees = function(angleRadians) {
return angleRadians * 180 / Math.PI;
};
/**
* For a given angle and radius, finds the X portion of the offset.
* @param {number} degrees Angle in degrees (zero points in +X direction).
* @param {number} radius Radius.
* @return {number} The x-distance for the angle and radius.
*/
goog.math.angleDx = function(degrees, radius) {
return radius * Math.cos(goog.math.toRadians(degrees));
};
/**
* For a given angle and radius, finds the Y portion of the offset.
* @param {number} degrees Angle in degrees (zero points in +X direction).
* @param {number} radius Radius.
* @return {number} The y-distance for the angle and radius.
*/
goog.math.angleDy = function(degrees, radius) {
return radius * Math.sin(goog.math.toRadians(degrees));
};
/**
* Computes the angle between two points (x1,y1) and (x2,y2).
* Angle zero points in the +X direction, 90 degrees points in the +Y
* direction (down) and from there we grow clockwise towards 360 degrees.
* @param {number} x1 x of first point.
* @param {number} y1 y of first point.
* @param {number} x2 x of second point.
* @param {number} y2 y of second point.
* @return {number} Standardized angle in degrees of the vector from
* x1,y1 to x2,y2.
*/
goog.math.angle = function(x1, y1, x2, y2) {
return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(y2 - y1,
x2 - x1)));
};
/**
* Computes the difference between startAngle and endAngle (angles in degrees).
* @param {number} startAngle Start angle in degrees.
* @param {number} endAngle End angle in degrees.
* @return {number} The number of degrees that when added to
* startAngle will result in endAngle. Positive numbers mean that the
* direction is clockwise. Negative numbers indicate a counter-clockwise
* direction.
* The shortest route (clockwise vs counter-clockwise) between the angles
* is used.
* When the difference is 180 degrees, the function returns 180 (not -180)
* angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
* angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
*/
goog.math.angleDifference = function(startAngle, endAngle) {
var d = goog.math.standardAngle(endAngle) -
goog.math.standardAngle(startAngle);
if (d > 180) {
d = d - 360;
} else if (d <= -180) {
d = 360 + d;
}
return d;
};
/**
* Returns the sign of a number as per the "sign" or "signum" function.
* @param {number} x The number to take the sign of.
* @return {number} -1 when negative, 1 when positive, 0 when 0.
*/
goog.math.sign = function(x) {
return x == 0 ? 0 : (x < 0 ? -1 : 1);
};
/**
* JavaScript implementation of Longest Common Subsequence problem.
* http://en.wikipedia.org/wiki/Longest_common_subsequence
*
* Returns the longest possible array that is subarray of both of given arrays.
*
* @param {Array.<Object>} array1 First array of objects.
* @param {Array.<Object>} array2 Second array of objects.
* @param {Function=} opt_compareFn Function that acts as a custom comparator
* for the array ojects. Function should return true if objects are equal,
* otherwise false.
* @param {Function=} opt_collectorFn Function used to decide what to return
* as a result subsequence. It accepts 2 arguments: index of common element
* in the first array and index in the second. The default function returns
* element from the first array.
* @return {Array.<Object>} A list of objects that are common to both arrays
* such that there is no common subsequence with size greater than the
* length of the list.
*/
goog.math.longestCommonSubsequence = function(
array1, array2, opt_compareFn, opt_collectorFn) {
var compare = opt_compareFn || function(a, b) {
return a == b;
};
var collect = opt_collectorFn || function(i1, i2) {
return array1[i1];
};
var length1 = array1.length;
var length2 = array2.length;
var arr = [];
for (var i = 0; i < length1 + 1; i++) {
arr[i] = [];
arr[i][0] = 0;
}
for (var j = 0; j < length2 + 1; j++) {
arr[0][j] = 0;
}
for (i = 1; i <= length1; i++) {
for (j = 1; j <= length2; j++) {
if (compare(array1[i - 1], array2[j - 1])) {
arr[i][j] = arr[i - 1][j - 1] + 1;
} else {
arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
}
}
}
// Backtracking
var result = [];
var i = length1, j = length2;
while (i > 0 && j > 0) {
if (compare(array1[i - 1], array2[j - 1])) {
result.unshift(collect(i - 1, j - 1));
i--;
j--;
} else {
if (arr[i - 1][j] > arr[i][j - 1]) {
i--;
} else {
j--;
}
}
}
return result;
};
/**
* Returns the sum of the arguments.
* @param {...number} var_args Numbers to add.
* @return {number} The sum of the arguments (0 if no arguments were provided,
* {@code NaN} if any of the arguments is not a valid number).
*/
goog.math.sum = function(var_args) {
return /** @type {number} */ (goog.array.reduce(arguments,
function(sum, value) {
return sum + value;
}, 0));
};
/**
* Returns the arithmetic mean of the arguments.
* @param {...number} var_args Numbers to average.
* @return {number} The average of the arguments ({@code NaN} if no arguments
* were provided or any of the arguments is not a valid number).
*/
goog.math.average = function(var_args) {
return goog.math.sum.apply(null, arguments) / arguments.length;
};
/**
* Returns the sample standard deviation of the arguments. For a definition of
* sample standard deviation, see e.g.
* http://en.wikipedia.org/wiki/Standard_deviation
* @param {...number} var_args Number samples to analyze.
* @return {number} The sample standard deviation of the arguments (0 if fewer
* than two samples were provided, or {@code NaN} if any of the samples is
* not a valid number).
*/
goog.math.standardDeviation = function(var_args) {
var sampleSize = arguments.length;
if (sampleSize < 2) {
return 0;
}
var mean = goog.math.average.apply(null, arguments);
var variance = goog.math.sum.apply(null, goog.array.map(arguments,
function(val) {
return Math.pow(val - mean, 2);
})) / (sampleSize - 1);
return Math.sqrt(variance);
};
/**
* Returns whether the supplied number represents an integer, i.e. that is has
* no fractional component. No range-checking is performed on the number.
* @param {number} num The number to test.
* @return {boolean} Whether {@code num} is an integer.
*/
goog.math.isInt = function(num) {
return isFinite(num) && num % 1 == 0;
};
/**
* Returns whether the supplied number is finite and not NaN.
* @param {number} num The number to test.
* @return {boolean} Whether {@code num} is a finite number.
*/
goog.math.isFiniteNumber = function(num) {
return isFinite(num) && !isNaN(num);
};
/**
* A tweaked variant of {@code Math.floor} which tolerates if the passed number
* is infinitesimally smaller than the closest integer. It often happens with
* the results of floating point calculations because of the finite precision
* of the intermediate results. For example {@code Math.floor(Math.log(1000) /
* Math.LN10) == 2}, not 3 as one would expect.
* @param {number} num A number.
* @param {number=} opt_epsilon An infinitesimally small positive number, the
* rounding error to tolerate.
* @return {number} The largest integer less than or equal to {@code num}.
*/
goog.math.safeFloor = function(num, opt_epsilon) {
goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
return Math.floor(num + (opt_epsilon || 2e-15));
};
/**
* A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
* details.
* @param {number} num A number.
* @param {number=} opt_epsilon An infinitesimally small positive number, the
* rounding error to tolerate.
* @return {number} The smallest integer greater than or equal to {@code num}.
*/
goog.math.safeCeil = function(num, opt_epsilon) {
goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
return Math.ceil(num - (opt_epsilon || 2e-15));
};

View File

@@ -0,0 +1,675 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class for representing matrices and static helper functions.
*/
goog.provide('goog.math.Matrix');
goog.require('goog.array');
goog.require('goog.math');
goog.require('goog.math.Size');
goog.require('goog.string');
/**
* Class for representing and manipulating matrices.
*
* The entry that lies in the i-th row and the j-th column of a matrix is
* typically referred to as the i,j entry of the matrix.
*
* The m-by-n matrix A would have its entries referred to as:
* [ a0,0 a0,1 a0,2 ... a0,j ... a0,n ]
* [ a1,0 a1,1 a1,2 ... a1,j ... a1,n ]
* [ a2,0 a2,1 a2,2 ... a2,j ... a2,n ]
* [ . . . . . ]
* [ . . . . . ]
* [ . . . . . ]
* [ ai,0 ai,1 ai,2 ... ai,j ... ai,n ]
* [ . . . . . ]
* [ . . . . . ]
* [ . . . . . ]
* [ am,0 am,1 am,2 ... am,j ... am,n ]
*
* @param {goog.math.Matrix|Array.<Array.<number>>|goog.math.Size|number} m
* A matrix to copy, a 2D-array to take as a template, a size object for
* dimensions, or the number of rows.
* @param {number=} opt_n Number of columns of the matrix (only applicable if
* the first argument is also numeric).
* @constructor
*/
goog.math.Matrix = function(m, opt_n) {
if (m instanceof goog.math.Matrix) {
this.array_ = m.toArray();
} else if (goog.isArrayLike(m) &&
goog.math.Matrix.isValidArray(/** @type {!Array} */ (m))) {
this.array_ = goog.array.clone(/** @type {!Array.<!Array.<number>>} */ (m));
} else if (m instanceof goog.math.Size) {
this.array_ = goog.math.Matrix.createZeroPaddedArray_(m.height, m.width);
} else if (goog.isNumber(m) && goog.isNumber(opt_n) && m > 0 && opt_n > 0) {
this.array_ = goog.math.Matrix.createZeroPaddedArray_(
/** @type {number} */ (m), opt_n);
} else {
throw Error('Invalid argument(s) for Matrix contructor');
}
this.size_ = new goog.math.Size(this.array_[0].length, this.array_.length);
};
/**
* Creates a square identity matrix. i.e. for n = 3:
* <pre>
* [ 1 0 0 ]
* [ 0 1 0 ]
* [ 0 0 1 ]
* </pre>
* @param {number} n The size of the square identity matrix.
* @return {!goog.math.Matrix} Identity matrix of width and height {@code n}.
*/
goog.math.Matrix.createIdentityMatrix = function(n) {
var rv = [];
for (var i = 0; i < n; i++) {
rv[i] = [];
for (var j = 0; j < n; j++) {
rv[i][j] = i == j ? 1 : 0;
}
}
return new goog.math.Matrix(rv);
};
/**
* Calls a function for each cell in a matrix.
* @param {goog.math.Matrix} matrix The matrix to iterate over.
* @param {Function} fn The function to call for every element. This function
* takes 4 arguments (value, i, j, and the matrix)
* and the return value is irrelevant.
* @param {Object=} opt_obj The object to be used as the value of 'this'
* within {@code fn}.
*/
goog.math.Matrix.forEach = function(matrix, fn, opt_obj) {
for (var i = 0; i < matrix.getSize().height; i++) {
for (var j = 0; j < matrix.getSize().width; j++) {
fn.call(opt_obj, matrix.array_[i][j], i, j, matrix);
}
}
};
/**
* Tests whether an array is a valid matrix. A valid array is an array of
* arrays where all arrays are of the same length and all elements are numbers.
* @param {Array} arr An array to test.
* @return {boolean} Whether the array is a valid matrix.
*/
goog.math.Matrix.isValidArray = function(arr) {
var len = 0;
for (var i = 0; i < arr.length; i++) {
if (!goog.isArrayLike(arr[i]) || len > 0 && arr[i].length != len) {
return false;
}
for (var j = 0; j < arr[i].length; j++) {
if (!goog.isNumber(arr[i][j])) {
return false;
}
}
if (len == 0) {
len = arr[i].length;
}
}
return len != 0;
};
/**
* Calls a function for every cell in a matrix and inserts the result into a
* new matrix of equal dimensions.
* @param {goog.math.Matrix} matrix The matrix to iterate over.
* @param {Function} fn The function to call for every element. This function
* takes 4 arguments (value, i, j and the matrix)
* and should return something. The result will be inserted
* into a new matrix.
* @param {Object=} opt_obj The object to be used as the value of 'this'
* within {@code fn}.
* @return {!goog.math.Matrix} A new matrix with the results from {@code fn}.
*/
goog.math.Matrix.map = function(matrix, fn, opt_obj) {
var m = new goog.math.Matrix(matrix.getSize());
goog.math.Matrix.forEach(matrix, function(value, i, j) {
m.array_[i][j] = fn.call(opt_obj, value, i, j, matrix);
});
return m;
};
/**
* Creates a new zero padded matix.
* @param {number} m Height of matrix.
* @param {number} n Width of matrix.
* @return {!Array.<!Array.<number>>} The new zero padded matrix.
* @private
*/
goog.math.Matrix.createZeroPaddedArray_ = function(m, n) {
var rv = [];
for (var i = 0; i < m; i++) {
rv[i] = [];
for (var j = 0; j < n; j++) {
rv[i][j] = 0;
}
}
return rv;
};
/**
* Internal array representing the matrix.
* @type {!Array.<!Array.<number>>}
* @private
*/
goog.math.Matrix.prototype.array_;
/**
* After construction the Matrix's size is constant and stored in this object.
* @type {!goog.math.Size}
* @private
*/
goog.math.Matrix.prototype.size_;
/**
* Returns a new matrix that is the sum of this and the provided matrix.
* @param {goog.math.Matrix} m The matrix to add to this one.
* @return {!goog.math.Matrix} Resultant sum.
*/
goog.math.Matrix.prototype.add = function(m) {
if (!goog.math.Size.equals(this.size_, m.getSize())) {
throw Error('Matrix summation is only supported on arrays of equal size');
}
return goog.math.Matrix.map(this, function(val, i, j) {
return val + m.array_[i][j];
});
};
/**
* Appends the given matrix to the right side of this matrix.
* @param {goog.math.Matrix} m The matrix to augment this matrix with.
* @return {!goog.math.Matrix} A new matrix with additional columns on the
* right.
*/
goog.math.Matrix.prototype.appendColumns = function(m) {
if (this.size_.height != m.getSize().height) {
throw Error('The given matrix has height ' + m.size_.height + ', but ' +
' needs to have height ' + this.size_.height + '.');
}
var result = new goog.math.Matrix(this.size_.height,
this.size_.width + m.size_.width);
goog.math.Matrix.forEach(this, function(value, i, j) {
result.array_[i][j] = value;
});
goog.math.Matrix.forEach(m, function(value, i, j) {
result.array_[i][this.size_.width + j] = value;
}, this);
return result;
};
/**
* Appends the given matrix to the bottom of this matrix.
* @param {goog.math.Matrix} m The matrix to augment this matrix with.
* @return {!goog.math.Matrix} A new matrix with added columns on the bottom.
*/
goog.math.Matrix.prototype.appendRows = function(m) {
if (this.size_.width != m.getSize().width) {
throw Error('The given matrix has width ' + m.size_.width + ', but ' +
' needs to have width ' + this.size_.width + '.');
}
var result = new goog.math.Matrix(this.size_.height + m.size_.height,
this.size_.width);
goog.math.Matrix.forEach(this, function(value, i, j) {
result.array_[i][j] = value;
});
goog.math.Matrix.forEach(m, function(value, i, j) {
result.array_[this.size_.height + i][j] = value;
}, this);
return result;
};
/**
* Returns whether the given matrix equals this matrix.
* @param {goog.math.Matrix} m The matrix to compare to this one.
* @param {number=} opt_tolerance The tolerance when comparing array entries.
* @return {boolean} Whether the given matrix equals this matrix.
*/
goog.math.Matrix.prototype.equals = function(m, opt_tolerance) {
if (this.size_.width != m.size_.width) {
return false;
}
if (this.size_.height != m.size_.height) {
return false;
}
var tolerance = opt_tolerance || 0;
for (var i = 0; i < this.size_.height; i++) {
for (var j = 0; j < this.size_.width; j++) {
if (!goog.math.nearlyEquals(this.array_[i][j], m.array_[i][j],
tolerance)) {
return false;
}
}
}
return true;
};
/**
* Returns the determinant of this matrix. The determinant of a matrix A is
* often denoted as |A| and can only be applied to a square matrix.
* @return {number} The determinant of this matrix.
*/
goog.math.Matrix.prototype.getDeterminant = function() {
if (!this.isSquare()) {
throw Error('A determinant can only be take on a square matrix');
}
return this.getDeterminant_();
};
/**
* Returns the inverse of this matrix if it exists or null if the matrix is
* not invertible.
* @return {goog.math.Matrix} A new matrix which is the inverse of this matrix.
*/
goog.math.Matrix.prototype.getInverse = function() {
if (!this.isSquare()) {
throw Error('An inverse can only be taken on a square matrix.');
}
if (this.getSize().width == 1) {
var a = this.getValueAt(0, 0);
return a == 0 ? null : new goog.math.Matrix([[1 / a]]);
}
var identity = goog.math.Matrix.createIdentityMatrix(this.size_.height);
var mi = this.appendColumns(identity).getReducedRowEchelonForm();
var i = mi.getSubmatrixByCoordinates_(
0, 0, identity.size_.width - 1, identity.size_.height - 1);
if (!i.equals(identity)) {
return null; // This matrix was not invertible
}
return mi.getSubmatrixByCoordinates_(0, identity.size_.width);
};
/**
* Transforms this matrix into reduced row echelon form.
* @return {!goog.math.Matrix} A new matrix reduced row echelon form.
*/
goog.math.Matrix.prototype.getReducedRowEchelonForm = function() {
var result = new goog.math.Matrix(this);
var col = 0;
// Each iteration puts one row in reduced row echelon form
for (var row = 0; row < result.size_.height; row++) {
if (col >= result.size_.width) {
return result;
}
// Scan each column starting from this row on down for a non-zero value
var i = row;
while (result.array_[i][col] == 0) {
i++;
if (i == result.size_.height) {
i = row;
col++;
if (col == result.size_.width) {
return result;
}
}
}
// Make the row we found the current row with a leading 1
this.swapRows_(i, row);
var divisor = result.array_[row][col];
for (var j = col; j < result.size_.width; j++) {
result.array_[row][j] = result.array_[row][j] / divisor;
}
// Subtract a multiple of this row from each other row
// so that all the other entries in this column are 0
for (i = 0; i < result.size_.height; i++) {
if (i != row) {
var multiple = result.array_[i][col];
for (var j = col; j < result.size_.width; j++) {
result.array_[i][j] -= multiple * result.array_[row][j];
}
}
}
// Move on to the next column
col++;
}
return result;
};
/**
* @return {!goog.math.Size} The dimensions of the matrix.
*/
goog.math.Matrix.prototype.getSize = function() {
return this.size_;
};
/**
* Return the transpose of this matrix. For an m-by-n matrix, the transpose
* is the n-by-m matrix which results from turning rows into columns and columns
* into rows
* @return {!goog.math.Matrix} A new matrix A^T.
*/
goog.math.Matrix.prototype.getTranspose = function() {
var m = new goog.math.Matrix(this.size_.width, this.size_.height);
goog.math.Matrix.forEach(this, function(value, i, j) {
m.array_[j][i] = value;
});
return m;
};
/**
* Retrieves the value of a particular coordinate in the matrix or null if the
* requested coordinates are out of range.
* @param {number} i The i index of the coordinate.
* @param {number} j The j index of the coordinate.
* @return {?number} The value at the specified coordinate.
*/
goog.math.Matrix.prototype.getValueAt = function(i, j) {
if (!this.isInBounds_(i, j)) {
return null;
}
return this.array_[i][j];
};
/**
* @return {boolean} Whether the horizontal and vertical dimensions of this
* matrix are the same.
*/
goog.math.Matrix.prototype.isSquare = function() {
return this.size_.width == this.size_.height;
};
/**
* Sets the value at a particular coordinate (if the coordinate is within the
* bounds of the matrix).
* @param {number} i The i index of the coordinate.
* @param {number} j The j index of the coordinate.
* @param {number} value The new value for the coordinate.
*/
goog.math.Matrix.prototype.setValueAt = function(i, j, value) {
if (!this.isInBounds_(i, j)) {
throw Error(
'Index out of bounds when setting matrix value, (' + i + ',' + j +
') in size (' + this.size_.height + ',' + this.size_.width + ')');
}
this.array_[i][j] = value;
};
/**
* Performs matrix or scalar multiplication on a matrix and returns the
* resultant matrix.
*
* Matrix multiplication is defined between two matrices only if the number of
* columns of the first matrix is the same as the number of rows of the second
* matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
* product AB is an m-by-p matrix
*
* Scalar multiplication returns a matrix of the same size as the original,
* each value multiplied by the given value.
*
* @param {goog.math.Matrix|number} m Matrix/number to multiply the matrix by.
* @return {!goog.math.Matrix} Resultant product.
*/
goog.math.Matrix.prototype.multiply = function(m) {
if (m instanceof goog.math.Matrix) {
if (this.size_.width != m.getSize().height) {
throw Error('Invalid matrices for multiplication. Second matrix ' +
'should have the same number of rows as the first has columns.');
}
return this.matrixMultiply_(/** @type {!goog.math.Matrix} */ (m));
} else if (goog.isNumber(m)) {
return this.scalarMultiply_(/** @type {number} */ (m));
} else {
throw Error('A matrix can only be multiplied by' +
' a number or another matrix.');
}
};
/**
* Returns a new matrix that is the difference of this and the provided matrix.
* @param {goog.math.Matrix} m The matrix to subtract from this one.
* @return {!goog.math.Matrix} Resultant difference.
*/
goog.math.Matrix.prototype.subtract = function(m) {
if (!goog.math.Size.equals(this.size_, m.getSize())) {
throw Error(
'Matrix subtraction is only supported on arrays of equal size.');
}
return goog.math.Matrix.map(this, function(val, i, j) {
return val - m.array_[i][j];
});
};
/**
* @return {!Array.<!Array.<number>>} A 2D internal array representing this
* matrix. Not a clone.
*/
goog.math.Matrix.prototype.toArray = function() {
return this.array_;
};
if (goog.DEBUG) {
/**
* Returns a string representation of the matrix. e.g.
* <pre>
* [ 12 5 9 1 ]
* [ 4 16 0 17 ]
* [ 12 5 1 23 ]
* </pre>
*
* @return {string} A string representation of this matrix.
* @override
*/
goog.math.Matrix.prototype.toString = function() {
// Calculate correct padding for optimum display of matrix
var maxLen = 0;
goog.math.Matrix.forEach(this, function(val) {
var len = String(val).length;
if (len > maxLen) {
maxLen = len;
}
});
// Build the string
var sb = [];
goog.array.forEach(this.array_, function(row, x) {
sb.push('[ ');
goog.array.forEach(row, function(val, y) {
var strval = String(val);
sb.push(goog.string.repeat(' ', maxLen - strval.length) + strval + ' ');
});
sb.push(']\n');
});
return sb.join('');
};
}
/**
* Returns the signed minor.
* @param {number} i The row index.
* @param {number} j The column index.
* @return {number} The cofactor C[i,j] of this matrix.
* @private
*/
goog.math.Matrix.prototype.getCofactor_ = function(i, j) {
return (i + j % 2 == 0 ? 1 : -1) * this.getMinor_(i, j);
};
/**
* Returns the determinant of this matrix. The determinant of a matrix A is
* often denoted as |A| and can only be applied to a square matrix. Same as
* public method but without validation. Implemented using Laplace's formula.
* @return {number} The determinant of this matrix.
* @private
*/
goog.math.Matrix.prototype.getDeterminant_ = function() {
if (this.getSize().area() == 1) {
return this.array_[0][0];
}
// We might want to use matrix decomposition to improve running time
// For now we'll do a Laplace expansion along the first row
var determinant = 0;
for (var j = 0; j < this.size_.width; j++) {
determinant += (this.array_[0][j] * this.getCofactor_(0, j));
}
return determinant;
};
/**
* Returns the determinant of the submatrix resulting from the deletion of row i
* and column j.
* @param {number} i The row to delete.
* @param {number} j The column to delete.
* @return {number} The first minor M[i,j] of this matrix.
* @private
*/
goog.math.Matrix.prototype.getMinor_ = function(i, j) {
return this.getSubmatrixByDeletion_(i, j).getDeterminant_();
};
/**
* Returns a submatrix contained within this matrix.
* @param {number} i1 The upper row index.
* @param {number} j1 The left column index.
* @param {number=} opt_i2 The lower row index.
* @param {number=} opt_j2 The right column index.
* @return {!goog.math.Matrix} The submatrix contained within the given bounds.
* @private
*/
goog.math.Matrix.prototype.getSubmatrixByCoordinates_ =
function(i1, j1, opt_i2, opt_j2) {
var i2 = opt_i2 ? opt_i2 : this.size_.height - 1;
var j2 = opt_j2 ? opt_j2 : this.size_.width - 1;
var result = new goog.math.Matrix(i2 - i1 + 1, j2 - j1 + 1);
goog.math.Matrix.forEach(result, function(value, i, j) {
result.array_[i][j] = this.array_[i1 + i][j1 + j];
}, this);
return result;
};
/**
* Returns a new matrix equal to this one, but with row i and column j deleted.
* @param {number} i The row index of the coordinate.
* @param {number} j The column index of the coordinate.
* @return {!goog.math.Matrix} The value at the specified coordinate.
* @private
*/
goog.math.Matrix.prototype.getSubmatrixByDeletion_ = function(i, j) {
var m = new goog.math.Matrix(this.size_.width - 1, this.size_.height - 1);
goog.math.Matrix.forEach(m, function(value, x, y) {
m.setValueAt(x, y, this.array_[x >= i ? x + 1 : x][y >= j ? y + 1 : y]);
}, this);
return m;
};
/**
* Returns whether the given coordinates are contained within the bounds of the
* matrix.
* @param {number} i The i index of the coordinate.
* @param {number} j The j index of the coordinate.
* @return {boolean} The value at the specified coordinate.
* @private
*/
goog.math.Matrix.prototype.isInBounds_ = function(i, j) {
return i >= 0 && i < this.size_.height &&
j >= 0 && j < this.size_.width;
};
/**
* Matrix multiplication is defined between two matrices only if the number of
* columns of the first matrix is the same as the number of rows of the second
* matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
* product AB is an m-by-p matrix
*
* @param {goog.math.Matrix} m Matrix to multiply the matrix by.
* @return {!goog.math.Matrix} Resultant product.
* @private
*/
goog.math.Matrix.prototype.matrixMultiply_ = function(m) {
var resultMatrix = new goog.math.Matrix(this.size_.height, m.getSize().width);
goog.math.Matrix.forEach(resultMatrix, function(val, x, y) {
var newVal = 0;
for (var i = 0; i < this.size_.width; i++) {
newVal += this.getValueAt(x, i) * m.getValueAt(i, y);
}
resultMatrix.setValueAt(x, y, newVal);
}, this);
return resultMatrix;
};
/**
* Scalar multiplication returns a matrix of the same size as the original,
* each value multiplied by the given value.
*
* @param {number} m number to multiply the matrix by.
* @return {!goog.math.Matrix} Resultant product.
* @private
*/
goog.math.Matrix.prototype.scalarMultiply_ = function(m) {
return goog.math.Matrix.map(this, function(val, x, y) {
return val * m;
});
};
/**
* Swaps two rows.
* @param {number} i1 The index of the first row to swap.
* @param {number} i2 The index of the second row to swap.
* @private
*/
goog.math.Matrix.prototype.swapRows_ = function(i1, i2) {
var tmp = this.array_[i1];
this.array_[i1] = this.array_[i2];
this.array_[i2] = tmp;
};

View File

@@ -0,0 +1,144 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing a numeric range.
*/
goog.provide('goog.math.Range');
/**
* A number range.
* @param {number} a One end of the range.
* @param {number} b The other end of the range.
* @constructor
*/
goog.math.Range = function(a, b) {
/**
* The lowest value in the range.
* @type {number}
*/
this.start = a < b ? a : b;
/**
* The highest value in the range.
* @type {number}
*/
this.end = a < b ? b : a;
};
/**
* @return {!goog.math.Range} A clone of this Range.
*/
goog.math.Range.prototype.clone = function() {
return new goog.math.Range(this.start, this.end);
};
if (goog.DEBUG) {
/**
* Returns a string representing the range.
* @return {string} In the form [-3.5, 8.13].
* @override
*/
goog.math.Range.prototype.toString = function() {
return '[' + this.start + ', ' + this.end + ']';
};
}
/**
* Compares ranges for equality.
* @param {goog.math.Range} a A Range.
* @param {goog.math.Range} b A Range.
* @return {boolean} True iff both the starts and the ends of the ranges are
* equal, or if both ranges are null.
*/
goog.math.Range.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.start == b.start && a.end == b.end;
};
/**
* Given two ranges on the same dimension, this method returns the intersection
* of those ranges.
* @param {goog.math.Range} a A Range.
* @param {goog.math.Range} b A Range.
* @return {goog.math.Range} A new Range representing the intersection of two
* ranges, or null if there is no intersection. Ranges are assumed to
* include their end points, and the intersection can be a point.
*/
goog.math.Range.intersection = function(a, b) {
var c0 = Math.max(a.start, b.start);
var c1 = Math.min(a.end, b.end);
return (c0 <= c1) ? new goog.math.Range(c0, c1) : null;
};
/**
* Given two ranges on the same dimension, determines whether they intersect.
* @param {goog.math.Range} a A Range.
* @param {goog.math.Range} b A Range.
* @return {boolean} Whether they intersect.
*/
goog.math.Range.hasIntersection = function(a, b) {
return Math.max(a.start, b.start) <= Math.min(a.end, b.end);
};
/**
* Given two ranges on the same dimension, this returns a range that covers
* both ranges.
* @param {goog.math.Range} a A Range.
* @param {goog.math.Range} b A Range.
* @return {!goog.math.Range} A new Range representing the bounding
* range.
*/
goog.math.Range.boundingRange = function(a, b) {
return new goog.math.Range(Math.min(a.start, b.start),
Math.max(a.end, b.end));
};
/**
* Given two ranges, returns true if the first range completely overlaps the
* second.
* @param {goog.math.Range} a The first Range.
* @param {goog.math.Range} b The second Range.
* @return {boolean} True if b is contained inside a, false otherwise.
*/
goog.math.Range.contains = function(a, b) {
return a.start <= b.start && a.end >= b.end;
};
/**
* Given a range and a point, returns true if the range contains the point.
* @param {goog.math.Range} range The range.
* @param {number} p The point.
* @return {boolean} True if p is contained inside range, false otherwise.
*/
goog.math.Range.containsPoint = function(range, p) {
return range.start <= p && range.end >= p;
};

View File

@@ -0,0 +1,394 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A RangeSet is a structure that manages a list of ranges.
* Numeric ranges may be added and removed from the RangeSet, and the set may
* be queried for the presence or absence of individual values or ranges of
* values.
*
* This may be used, for example, to track the availability of sparse elements
* in an array without iterating over the entire array.
*
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('goog.math.RangeSet');
goog.require('goog.array');
goog.require('goog.iter.Iterator');
goog.require('goog.iter.StopIteration');
goog.require('goog.math.Range');
/**
* Constructs a new RangeSet, which can store numeric ranges.
*
* Ranges are treated as half-closed: that is, they are exclusive of their end
* value [start, end).
*
* New ranges added to the set which overlap the values in one or more existing
* ranges will be merged.
*
* @constructor
*/
goog.math.RangeSet = function() {
/**
* A sorted list of ranges that represent the values in the set.
* @type {!Array.<!goog.math.Range>}
* @private
*/
this.ranges_ = [];
};
if (goog.DEBUG) {
/**
* @return {string} A debug string in the form [[1, 5], [8, 9], [15, 30]].
* @override
*/
goog.math.RangeSet.prototype.toString = function() {
return '[' + this.ranges_.join(', ') + ']';
};
}
/**
* Compares two sets for equality.
*
* @param {goog.math.RangeSet} a A range set.
* @param {goog.math.RangeSet} b A range set.
* @return {boolean} Whether both sets contain the same values.
*/
goog.math.RangeSet.equals = function(a, b) {
// Fast check for object equality. Also succeeds if a and b are both null.
return a == b || !!(a && b && goog.array.equals(a.ranges_, b.ranges_,
goog.math.Range.equals));
};
/**
* @return {!goog.math.RangeSet} A new RangeSet containing the same values as
* this one.
*/
goog.math.RangeSet.prototype.clone = function() {
var set = new goog.math.RangeSet();
for (var i = this.ranges_.length; i--;) {
set.ranges_[i] = this.ranges_[i].clone();
}
return set;
};
/**
* Adds a range to the set. If the new range overlaps existing values, those
* ranges will be merged.
*
* @param {goog.math.Range} a The range to add.
*/
goog.math.RangeSet.prototype.add = function(a) {
if (a.end <= a.start) {
// Empty ranges are ignored.
return;
}
a = a.clone();
// Find the insertion point.
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (a.start <= b.end) {
a.start = Math.min(a.start, b.start);
break;
}
}
var insertionPoint = i;
for (; b = this.ranges_[i]; i++) {
if (a.end < b.start) {
break;
}
a.end = Math.max(a.end, b.end);
}
this.ranges_.splice(insertionPoint, i - insertionPoint, a);
};
/**
* Removes a range of values from the set.
*
* @param {goog.math.Range} a The range to remove.
*/
goog.math.RangeSet.prototype.remove = function(a) {
if (a.end <= a.start) {
// Empty ranges are ignored.
return;
}
// Find the insertion point.
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (a.start < b.end) {
break;
}
}
if (!b || a.end < b.start) {
// The range being removed doesn't overlap any existing range. Exit early.
return;
}
var insertionPoint = i;
if (a.start > b.start) {
// There is an overlap with the nearest range. Modify it accordingly.
insertionPoint++;
if (a.end < b.end) {
goog.array.insertAt(this.ranges_,
new goog.math.Range(a.end, b.end),
insertionPoint);
}
b.end = a.start;
}
for (i = insertionPoint; b = this.ranges_[i]; i++) {
b.start = Math.max(a.end, b.start);
if (a.end < b.end) {
break;
}
}
this.ranges_.splice(insertionPoint, i - insertionPoint);
};
/**
* Determines whether a given range is in the set. Only succeeds if the entire
* range is available.
*
* @param {goog.math.Range} a The query range.
* @return {boolean} Whether the entire requested range is set.
*/
goog.math.RangeSet.prototype.contains = function(a) {
if (a.end <= a.start) {
return false;
}
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (a.start < b.end) {
if (a.end >= b.start) {
return goog.math.Range.contains(b, a);
}
break;
}
}
return false;
};
/**
* Determines whether a given value is set in the RangeSet.
*
* @param {number} value The value to test.
* @return {boolean} Whether the given value is in the set.
*/
goog.math.RangeSet.prototype.containsValue = function(value) {
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (value < b.end) {
if (value >= b.start) {
return true;
}
break;
}
}
return false;
};
/**
* Returns the union of this RangeSet with another.
*
* @param {goog.math.RangeSet} set Another RangeSet.
* @return {!goog.math.RangeSet} A new RangeSet containing all values from
* either set.
*/
goog.math.RangeSet.prototype.union = function(set) {
// TODO(brenneman): A linear-time merge would be preferable if it is ever a
// bottleneck.
set = set.clone();
for (var i = 0, a; a = this.ranges_[i]; i++) {
set.add(a);
}
return set;
};
/**
* Subtracts the ranges of another set from this one, returning the result
* as a new RangeSet.
*
* @param {!goog.math.RangeSet} set The RangeSet to subtract.
* @return {!goog.math.RangeSet} A new RangeSet containing all values in this
* set minus the values of the input set.
*/
goog.math.RangeSet.prototype.difference = function(set) {
var ret = this.clone();
for (var i = 0, a; a = set.ranges_[i]; i++) {
ret.remove(a);
}
return ret;
};
/**
* Intersects this RangeSet with another.
*
* @param {goog.math.RangeSet} set The RangeSet to intersect with.
* @return {!goog.math.RangeSet} A new RangeSet containing all values set in
* both this and the input set.
*/
goog.math.RangeSet.prototype.intersection = function(set) {
if (this.isEmpty() || set.isEmpty()) {
return new goog.math.RangeSet();
}
return this.difference(set.inverse(this.getBounds()));
};
/**
* Creates a subset of this set over the input range.
*
* @param {goog.math.Range} range The range to copy into the slice.
* @return {!goog.math.RangeSet} A new RangeSet with a copy of the values in the
* input range.
*/
goog.math.RangeSet.prototype.slice = function(range) {
var set = new goog.math.RangeSet();
if (range.start >= range.end) {
return set;
}
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (b.end <= range.start) {
continue;
}
if (b.start > range.end) {
break;
}
set.add(new goog.math.Range(Math.max(range.start, b.start),
Math.min(range.end, b.end)));
}
return set;
};
/**
* Creates an inverted slice of this set over the input range.
*
* @param {goog.math.Range} range The range to copy into the slice.
* @return {!goog.math.RangeSet} A new RangeSet containing inverted values from
* the original over the input range.
*/
goog.math.RangeSet.prototype.inverse = function(range) {
var set = new goog.math.RangeSet();
set.add(range);
for (var i = 0, b; b = this.ranges_[i]; i++) {
if (range.start >= b.end) {
continue;
}
if (range.end < b.start) {
break;
}
set.remove(b);
}
return set;
};
/**
* @return {number} The sum of the lengths of ranges covered in the set.
*/
goog.math.RangeSet.prototype.coveredLength = function() {
return /** @type {number} */ (goog.array.reduce(
this.ranges_,
function(res, range) {
return res + range.end - range.start;
}, 0));
};
/**
* @return {goog.math.Range} The total range this set covers, ignoring any
* gaps between ranges.
*/
goog.math.RangeSet.prototype.getBounds = function() {
if (this.ranges_.length) {
return new goog.math.Range(this.ranges_[0].start,
goog.array.peek(this.ranges_).end);
}
return null;
};
/**
* @return {boolean} Whether any ranges are currently in the set.
*/
goog.math.RangeSet.prototype.isEmpty = function() {
return this.ranges_.length == 0;
};
/**
* Removes all values in the set.
*/
goog.math.RangeSet.prototype.clear = function() {
this.ranges_.length = 0;
};
/**
* Returns an iterator that iterates over the ranges in the RangeSet.
*
* @param {boolean=} opt_keys Ignored for RangeSets.
* @return {!goog.iter.Iterator} An iterator over the values in the set.
*/
goog.math.RangeSet.prototype.__iterator__ = function(opt_keys) {
var i = 0;
var list = this.ranges_;
var iterator = new goog.iter.Iterator();
iterator.next = function() {
if (i >= list.length) {
throw goog.iter.StopIteration;
}
return list[i++].clone();
};
return iterator;
};

View File

@@ -0,0 +1,463 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing rectangles.
*/
goog.provide('goog.math.Rect');
goog.require('goog.math.Box');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Size');
/**
* Class for representing rectangular regions.
* @param {number} x Left.
* @param {number} y Top.
* @param {number} w Width.
* @param {number} h Height.
* @constructor
*/
goog.math.Rect = function(x, y, w, h) {
/** @type {number} */
this.left = x;
/** @type {number} */
this.top = y;
/** @type {number} */
this.width = w;
/** @type {number} */
this.height = h;
};
/**
* @return {!goog.math.Rect} A new copy of this Rectangle.
*/
goog.math.Rect.prototype.clone = function() {
return new goog.math.Rect(this.left, this.top, this.width, this.height);
};
/**
* Returns a new Box object with the same position and dimensions as this
* rectangle.
* @return {!goog.math.Box} A new Box representation of this Rectangle.
*/
goog.math.Rect.prototype.toBox = function() {
var right = this.left + this.width;
var bottom = this.top + this.height;
return new goog.math.Box(this.top,
right,
bottom,
this.left);
};
/**
* Creates a new Rect object with the same position and dimensions as a given
* Box. Note that this is only the inverse of toBox if left/top are defined.
* @param {goog.math.Box} box A box.
* @return {!goog.math.Rect} A new Rect initialized with the box's position
* and size.
*/
goog.math.Rect.createFromBox = function(box) {
return new goog.math.Rect(box.left, box.top,
box.right - box.left, box.bottom - box.top);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing size and dimensions of rectangle.
* @return {string} In the form (50, 73 - 75w x 25h).
* @override
*/
goog.math.Rect.prototype.toString = function() {
return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
this.height + 'h)';
};
}
/**
* Compares rectangles for equality.
* @param {goog.math.Rect} a A Rectangle.
* @param {goog.math.Rect} b A Rectangle.
* @return {boolean} True iff the rectangles have the same left, top, width,
* and height, or if both are null.
*/
goog.math.Rect.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.left == b.left && a.width == b.width &&
a.top == b.top && a.height == b.height;
};
/**
* Computes the intersection of this rectangle and the rectangle parameter. If
* there is no intersection, returns false and leaves this rectangle as is.
* @param {goog.math.Rect} rect A Rectangle.
* @return {boolean} True iff this rectangle intersects with the parameter.
*/
goog.math.Rect.prototype.intersection = function(rect) {
var x0 = Math.max(this.left, rect.left);
var x1 = Math.min(this.left + this.width, rect.left + rect.width);
if (x0 <= x1) {
var y0 = Math.max(this.top, rect.top);
var y1 = Math.min(this.top + this.height, rect.top + rect.height);
if (y0 <= y1) {
this.left = x0;
this.top = y0;
this.width = x1 - x0;
this.height = y1 - y0;
return true;
}
}
return false;
};
/**
* Returns the intersection of two rectangles. Two rectangles intersect if they
* touch at all, for example, two zero width and height rectangles would
* intersect if they had the same top and left.
* @param {goog.math.Rect} a A Rectangle.
* @param {goog.math.Rect} b A Rectangle.
* @return {goog.math.Rect} A new intersection rect (even if width and height
* are 0), or null if there is no intersection.
*/
goog.math.Rect.intersection = function(a, b) {
// There is no nice way to do intersection via a clone, because any such
// clone might be unnecessary if this function returns null. So, we duplicate
// code from above.
var x0 = Math.max(a.left, b.left);
var x1 = Math.min(a.left + a.width, b.left + b.width);
if (x0 <= x1) {
var y0 = Math.max(a.top, b.top);
var y1 = Math.min(a.top + a.height, b.top + b.height);
if (y0 <= y1) {
return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
}
}
return null;
};
/**
* Returns whether two rectangles intersect. Two rectangles intersect if they
* touch at all, for example, two zero width and height rectangles would
* intersect if they had the same top and left.
* @param {goog.math.Rect} a A Rectangle.
* @param {goog.math.Rect} b A Rectangle.
* @return {boolean} Whether a and b intersect.
*/
goog.math.Rect.intersects = function(a, b) {
return (a.left <= b.left + b.width && b.left <= a.left + a.width &&
a.top <= b.top + b.height && b.top <= a.top + a.height);
};
/**
* Returns whether a rectangle intersects this rectangle.
* @param {goog.math.Rect} rect A rectangle.
* @return {boolean} Whether rect intersects this rectangle.
*/
goog.math.Rect.prototype.intersects = function(rect) {
return goog.math.Rect.intersects(this, rect);
};
/**
* Computes the difference regions between two rectangles. The return value is
* an array of 0 to 4 rectangles defining the remaining regions of the first
* rectangle after the second has been subtracted.
* @param {goog.math.Rect} a A Rectangle.
* @param {goog.math.Rect} b A Rectangle.
* @return {!Array.<!goog.math.Rect>} An array with 0 to 4 rectangles which
* together define the difference area of rectangle a minus rectangle b.
*/
goog.math.Rect.difference = function(a, b) {
var intersection = goog.math.Rect.intersection(a, b);
if (!intersection || !intersection.height || !intersection.width) {
return [a.clone()];
}
var result = [];
var top = a.top;
var height = a.height;
var ar = a.left + a.width;
var ab = a.top + a.height;
var br = b.left + b.width;
var bb = b.top + b.height;
// Subtract off any area on top where A extends past B
if (b.top > a.top) {
result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
top = b.top;
// If we're moving the top down, we also need to subtract the height diff.
height -= b.top - a.top;
}
// Subtract off any area on bottom where A extends past B
if (bb < ab) {
result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
height = bb - top;
}
// Subtract any area on left where A extends past B
if (b.left > a.left) {
result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
}
// Subtract any area on right where A extends past B
if (br < ar) {
result.push(new goog.math.Rect(br, top, ar - br, height));
}
return result;
};
/**
* Computes the difference regions between this rectangle and {@code rect}. The
* return value is an array of 0 to 4 rectangles defining the remaining regions
* of this rectangle after the other has been subtracted.
* @param {goog.math.Rect} rect A Rectangle.
* @return {!Array.<!goog.math.Rect>} An array with 0 to 4 rectangles which
* together define the difference area of rectangle a minus rectangle b.
*/
goog.math.Rect.prototype.difference = function(rect) {
return goog.math.Rect.difference(this, rect);
};
/**
* Expand this rectangle to also include the area of the given rectangle.
* @param {goog.math.Rect} rect The other rectangle.
*/
goog.math.Rect.prototype.boundingRect = function(rect) {
// We compute right and bottom before we change left and top below.
var right = Math.max(this.left + this.width, rect.left + rect.width);
var bottom = Math.max(this.top + this.height, rect.top + rect.height);
this.left = Math.min(this.left, rect.left);
this.top = Math.min(this.top, rect.top);
this.width = right - this.left;
this.height = bottom - this.top;
};
/**
* Returns a new rectangle which completely contains both input rectangles.
* @param {goog.math.Rect} a A rectangle.
* @param {goog.math.Rect} b A rectangle.
* @return {goog.math.Rect} A new bounding rect, or null if either rect is
* null.
*/
goog.math.Rect.boundingRect = function(a, b) {
if (!a || !b) {
return null;
}
var clone = a.clone();
clone.boundingRect(b);
return clone;
};
/**
* Tests whether this rectangle entirely contains another rectangle or
* coordinate.
*
* @param {goog.math.Rect|goog.math.Coordinate} another The rectangle or
* coordinate to test for containment.
* @return {boolean} Whether this rectangle contains given rectangle or
* coordinate.
*/
goog.math.Rect.prototype.contains = function(another) {
if (another instanceof goog.math.Rect) {
return this.left <= another.left &&
this.left + this.width >= another.left + another.width &&
this.top <= another.top &&
this.top + this.height >= another.top + another.height;
} else { // (another instanceof goog.math.Coordinate)
return another.x >= this.left &&
another.x <= this.left + this.width &&
another.y >= this.top &&
another.y <= this.top + this.height;
}
};
/**
* @param {!goog.math.Coordinate} point A coordinate.
* @return {number} The squared distance between the point and the closest
* point inside the rectangle. Returns 0 if the point is inside the
* rectangle.
*/
goog.math.Rect.prototype.squaredDistance = function(point) {
var dx = point.x < this.left ?
this.left - point.x : Math.max(point.x - (this.left + this.width), 0);
var dy = point.y < this.top ?
this.top - point.y : Math.max(point.y - (this.top + this.height), 0);
return dx * dx + dy * dy;
};
/**
* @param {!goog.math.Coordinate} point A coordinate.
* @return {number} The distance between the point and the closest point
* inside the rectangle. Returns 0 if the point is inside the rectangle.
*/
goog.math.Rect.prototype.distance = function(point) {
return Math.sqrt(this.squaredDistance(point));
};
/**
* @return {!goog.math.Size} The size of this rectangle.
*/
goog.math.Rect.prototype.getSize = function() {
return new goog.math.Size(this.width, this.height);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
* the rectangle.
*/
goog.math.Rect.prototype.getTopLeft = function() {
return new goog.math.Coordinate(this.left, this.top);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the center of the
* rectangle.
*/
goog.math.Rect.prototype.getCenter = function() {
return new goog.math.Coordinate(
this.left + this.width / 2, this.top + this.height / 2);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
* of the rectangle.
*/
goog.math.Rect.prototype.getBottomRight = function() {
return new goog.math.Coordinate(
this.left + this.width, this.top + this.height);
};
/**
* Rounds the fields to the next larger integer values.
* @return {!goog.math.Rect} This rectangle with ceil'd fields.
*/
goog.math.Rect.prototype.ceil = function() {
this.left = Math.ceil(this.left);
this.top = Math.ceil(this.top);
this.width = Math.ceil(this.width);
this.height = Math.ceil(this.height);
return this;
};
/**
* Rounds the fields to the next smaller integer values.
* @return {!goog.math.Rect} This rectangle with floored fields.
*/
goog.math.Rect.prototype.floor = function() {
this.left = Math.floor(this.left);
this.top = Math.floor(this.top);
this.width = Math.floor(this.width);
this.height = Math.floor(this.height);
return this;
};
/**
* Rounds the fields to nearest integer values.
* @return {!goog.math.Rect} This rectangle with rounded fields.
*/
goog.math.Rect.prototype.round = function() {
this.left = Math.round(this.left);
this.top = Math.round(this.top);
this.width = Math.round(this.width);
this.height = Math.round(this.height);
return this;
};
/**
* Translates this rectangle by the given offsets. If a
* {@code goog.math.Coordinate} is given, then the left and top values are
* translated by the coordinate's x and y values. Otherwise, top and left are
* translated by {@code tx} and {@code opt_ty} respectively.
* @param {number|goog.math.Coordinate} tx The value to translate left by or the
* the coordinate to translate this rect by.
* @param {number=} opt_ty The value to translate top by.
* @return {!goog.math.Rect} This rectangle after translating.
*/
goog.math.Rect.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.left += tx.x;
this.top += tx.y;
} else {
this.left += tx;
if (goog.isNumber(opt_ty)) {
this.top += opt_ty;
}
}
return this;
};
/**
* Scales this rectangle by the given scale factors. The left and width values
* are scaled by {@code sx} and the top and height values are scaled by
* {@code opt_sy}. If {@code opt_sy} is not given, then all fields are scaled
* by {@code sx}.
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Rect} This rectangle after scaling.
*/
goog.math.Rect.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.left *= sx;
this.width *= sx;
this.top *= sy;
this.height *= sy;
return this;
};

View File

@@ -0,0 +1,206 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing two-dimensional sizes.
*/
goog.provide('goog.math.Size');
/**
* Class for representing sizes consisting of a width and height. Undefined
* width and height support is deprecated and results in compiler warning.
* @param {number} width Width.
* @param {number} height Height.
* @constructor
*/
goog.math.Size = function(width, height) {
/**
* Width
* @type {number}
*/
this.width = width;
/**
* Height
* @type {number}
*/
this.height = height;
};
/**
* Compares sizes for equality.
* @param {goog.math.Size} a A Size.
* @param {goog.math.Size} b A Size.
* @return {boolean} True iff the sizes have equal widths and equal
* heights, or if both are null.
*/
goog.math.Size.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.width == b.width && a.height == b.height;
};
/**
* @return {!goog.math.Size} A new copy of the Size.
*/
goog.math.Size.prototype.clone = function() {
return new goog.math.Size(this.width, this.height);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing size.
* @return {string} In the form (50 x 73).
* @override
*/
goog.math.Size.prototype.toString = function() {
return '(' + this.width + ' x ' + this.height + ')';
};
}
/**
* @return {number} The longer of the two dimensions in the size.
*/
goog.math.Size.prototype.getLongest = function() {
return Math.max(this.width, this.height);
};
/**
* @return {number} The shorter of the two dimensions in the size.
*/
goog.math.Size.prototype.getShortest = function() {
return Math.min(this.width, this.height);
};
/**
* @return {number} The area of the size (width * height).
*/
goog.math.Size.prototype.area = function() {
return this.width * this.height;
};
/**
* @return {number} The perimeter of the size (width + height) * 2.
*/
goog.math.Size.prototype.perimeter = function() {
return (this.width + this.height) * 2;
};
/**
* @return {number} The ratio of the size's width to its height.
*/
goog.math.Size.prototype.aspectRatio = function() {
return this.width / this.height;
};
/**
* @return {boolean} True if the size has zero area, false if both dimensions
* are non-zero numbers.
*/
goog.math.Size.prototype.isEmpty = function() {
return !this.area();
};
/**
* Clamps the width and height parameters upward to integer values.
* @return {!goog.math.Size} This size with ceil'd components.
*/
goog.math.Size.prototype.ceil = function() {
this.width = Math.ceil(this.width);
this.height = Math.ceil(this.height);
return this;
};
/**
* @param {!goog.math.Size} target The target size.
* @return {boolean} True if this Size is the same size or smaller than the
* target size in both dimensions.
*/
goog.math.Size.prototype.fitsInside = function(target) {
return this.width <= target.width && this.height <= target.height;
};
/**
* Clamps the width and height parameters downward to integer values.
* @return {!goog.math.Size} This size with floored components.
*/
goog.math.Size.prototype.floor = function() {
this.width = Math.floor(this.width);
this.height = Math.floor(this.height);
return this;
};
/**
* Rounds the width and height parameters to integer values.
* @return {!goog.math.Size} This size with rounded components.
*/
goog.math.Size.prototype.round = function() {
this.width = Math.round(this.width);
this.height = Math.round(this.height);
return this;
};
/**
* Scales this size by the given scale factors. The width and height are scaled
* by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy} is not
* given, then {@code sx} is used for both the width and height.
* @param {number} sx The scale factor to use for the width.
* @param {number=} opt_sy The scale factor to use for the height.
* @return {!goog.math.Size} This Size object after scaling.
*/
goog.math.Size.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.width *= sx;
this.height *= sy;
return this;
};
/**
* Uniformly scales the size to fit inside the dimensions of a given size. The
* original aspect ratio will be preserved.
*
* This function assumes that both Sizes contain strictly positive dimensions.
* @param {!goog.math.Size} target The target size.
* @return {!goog.math.Size} This Size object, after optional scaling.
*/
goog.math.Size.prototype.scaleToFit = function(target) {
var s = this.aspectRatio() > target.aspectRatio() ?
target.width / this.width :
target.height / this.height;
return this.scale(s);
};

View File

@@ -0,0 +1,73 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The Tridiagonal matrix algorithm solver solves a special
* version of a sparse linear system Ax = b where A is tridiagonal.
*
* See http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
*
*/
goog.provide('goog.math.tdma');
/**
* Solves a linear system where the matrix is square tri-diagonal. That is,
* given a system of equations:
*
* A * result = vecRight,
*
* this class computes result = inv(A) * vecRight, where A has the special form
* of a tri-diagonal matrix:
*
* |dia(0) sup(0) 0 0 ... 0|
* |sub(0) dia(1) sup(1) 0 ... 0|
* A =| ... |
* |0 ... 0 sub(n-2) dia(n-1) sup(n-1)|
* |0 ... 0 0 sub(n-1) dia(n)|
*
* @param {!Array.<number>} subDiag The sub diagonal of the matrix.
* @param {!Array.<number>} mainDiag The main diagonal of the matrix.
* @param {!Array.<number>} supDiag The super diagonal of the matrix.
* @param {!Array.<number>} vecRight The right vector of the system
* of equations.
* @param {Array.<number>=} opt_result The optional array to store the result.
* @return {!Array.<number>} The vector that is the solution to the system.
*/
goog.math.tdma.solve = function(
subDiag, mainDiag, supDiag, vecRight, opt_result) {
// Make a local copy of the main diagonal and the right vector.
mainDiag = mainDiag.slice();
vecRight = vecRight.slice();
// The dimension of the matrix.
var nDim = mainDiag.length;
// Construct a modified linear system of equations with the same solution
// as the input one.
for (var i = 1; i < nDim; ++i) {
var m = subDiag[i - 1] / mainDiag[i - 1];
mainDiag[i] = mainDiag[i] - m * supDiag[i - 1];
vecRight[i] = vecRight[i] - m * vecRight[i - 1];
}
// Solve the new system of equations by simple back-substitution.
var result = opt_result || new Array(vecRight.length);
result[nDim - 1] = vecRight[nDim - 1] / mainDiag[nDim - 1];
for (i = nDim - 2; i >= 0; --i) {
result[i] = (vecRight[i] - supDiag[i] * result[i + 1]) / mainDiag[i];
}
return result;
};

View File

@@ -0,0 +1,283 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines a 2-element vector class that can be used for
* coordinate math, useful for animation systems and point manipulation.
*
* Vec2 objects inherit from goog.math.Coordinate and may be used wherever a
* Coordinate is required. Where appropriate, Vec2 functions accept both Vec2
* and Coordinate objects as input.
*
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('goog.math.Vec2');
goog.require('goog.math');
goog.require('goog.math.Coordinate');
/**
* Class for a two-dimensional vector object and assorted functions useful for
* manipulating points.
*
* @param {number} x The x coordinate for the vector.
* @param {number} y The y coordinate for the vector.
* @constructor
* @extends {goog.math.Coordinate}
*/
goog.math.Vec2 = function(x, y) {
/**
* X-value
* @type {number}
*/
this.x = x;
/**
* Y-value
* @type {number}
*/
this.y = y;
};
goog.inherits(goog.math.Vec2, goog.math.Coordinate);
/**
* @return {!goog.math.Vec2} A random unit-length vector.
*/
goog.math.Vec2.randomUnit = function() {
var angle = Math.random() * Math.PI * 2;
return new goog.math.Vec2(Math.cos(angle), Math.sin(angle));
};
/**
* @return {!goog.math.Vec2} A random vector inside the unit-disc.
*/
goog.math.Vec2.random = function() {
var mag = Math.sqrt(Math.random());
var angle = Math.random() * Math.PI * 2;
return new goog.math.Vec2(Math.cos(angle) * mag, Math.sin(angle) * mag);
};
/**
* Returns a new Vec2 object from a given coordinate.
* @param {!goog.math.Coordinate} a The coordinate.
* @return {!goog.math.Vec2} A new vector object.
*/
goog.math.Vec2.fromCoordinate = function(a) {
return new goog.math.Vec2(a.x, a.y);
};
/**
* @return {!goog.math.Vec2} A new vector with the same coordinates as this one.
* @override
*/
goog.math.Vec2.prototype.clone = function() {
return new goog.math.Vec2(this.x, this.y);
};
/**
* Returns the magnitude of the vector measured from the origin.
* @return {number} The length of the vector.
*/
goog.math.Vec2.prototype.magnitude = function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
/**
* Returns the squared magnitude of the vector measured from the origin.
* NOTE(brenneman): Leaving out the square root is not a significant
* optimization in JavaScript.
* @return {number} The length of the vector, squared.
*/
goog.math.Vec2.prototype.squaredMagnitude = function() {
return this.x * this.x + this.y * this.y;
};
/**
* @return {!goog.math.Vec2} This coordinate after scaling.
* @override
*/
goog.math.Vec2.prototype.scale =
/** @type {function(number, number=):!goog.math.Vec2} */
(goog.math.Coordinate.prototype.scale);
/**
* Reverses the sign of the vector. Equivalent to scaling the vector by -1.
* @return {!goog.math.Vec2} The inverted vector.
*/
goog.math.Vec2.prototype.invert = function() {
this.x = -this.x;
this.y = -this.y;
return this;
};
/**
* Normalizes the current vector to have a magnitude of 1.
* @return {!goog.math.Vec2} The normalized vector.
*/
goog.math.Vec2.prototype.normalize = function() {
return this.scale(1 / this.magnitude());
};
/**
* Adds another vector to this vector in-place.
* @param {!goog.math.Coordinate} b The vector to add.
* @return {!goog.math.Vec2} This vector with {@code b} added.
*/
goog.math.Vec2.prototype.add = function(b) {
this.x += b.x;
this.y += b.y;
return this;
};
/**
* Subtracts another vector from this vector in-place.
* @param {!goog.math.Coordinate} b The vector to subtract.
* @return {!goog.math.Vec2} This vector with {@code b} subtracted.
*/
goog.math.Vec2.prototype.subtract = function(b) {
this.x -= b.x;
this.y -= b.y;
return this;
};
/**
* Rotates this vector in-place by a given angle, specified in radians.
* @param {number} angle The angle, in radians.
* @return {!goog.math.Vec2} This vector rotated {@code angle} radians.
*/
goog.math.Vec2.prototype.rotate = function(angle) {
var cos = Math.cos(angle);
var sin = Math.sin(angle);
var newX = this.x * cos - this.y * sin;
var newY = this.y * cos + this.x * sin;
this.x = newX;
this.y = newY;
return this;
};
/**
* Rotates a vector by a given angle, specified in radians, relative to a given
* axis rotation point. The returned vector is a newly created instance - no
* in-place changes are done.
* @param {!goog.math.Vec2} v A vector.
* @param {!goog.math.Vec2} axisPoint The rotation axis point.
* @param {number} angle The angle, in radians.
* @return {!goog.math.Vec2} The rotated vector in a newly created instance.
*/
goog.math.Vec2.rotateAroundPoint = function(v, axisPoint, angle) {
var res = v.clone();
return res.subtract(axisPoint).rotate(angle).add(axisPoint);
};
/**
* Compares this vector with another for equality.
* @param {!goog.math.Vec2} b The other vector.
* @return {boolean} Whether this vector has the same x and y as the given
* vector.
*/
goog.math.Vec2.prototype.equals = function(b) {
return this == b || !!b && this.x == b.x && this.y == b.y;
};
/**
* Returns the distance between two vectors.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {number} The distance.
*/
goog.math.Vec2.distance = goog.math.Coordinate.distance;
/**
* Returns the squared distance between two vectors.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {number} The squared distance.
*/
goog.math.Vec2.squaredDistance = goog.math.Coordinate.squaredDistance;
/**
* Compares vectors for equality.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {boolean} Whether the vectors have the same x and y coordinates.
*/
goog.math.Vec2.equals = goog.math.Coordinate.equals;
/**
* Returns the sum of two vectors as a new Vec2.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {!goog.math.Vec2} The sum vector.
*/
goog.math.Vec2.sum = function(a, b) {
return new goog.math.Vec2(a.x + b.x, a.y + b.y);
};
/**
* Returns the difference between two vectors as a new Vec2.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {!goog.math.Vec2} The difference vector.
*/
goog.math.Vec2.difference = function(a, b) {
return new goog.math.Vec2(a.x - b.x, a.y - b.y);
};
/**
* Returns the dot-product of two vectors.
* @param {!goog.math.Coordinate} a The first vector.
* @param {!goog.math.Coordinate} b The second vector.
* @return {number} The dot-product of the two vectors.
*/
goog.math.Vec2.dot = function(a, b) {
return a.x * b.x + a.y * b.y;
};
/**
* Returns a new Vec2 that is the linear interpolant between vectors a and b at
* scale-value x.
* @param {!goog.math.Coordinate} a Vector a.
* @param {!goog.math.Coordinate} b Vector b.
* @param {number} x The proportion between a and b.
* @return {!goog.math.Vec2} The interpolated vector.
*/
goog.math.Vec2.lerp = function(a, b, x) {
return new goog.math.Vec2(goog.math.lerp(a.x, b.x, x),
goog.math.lerp(a.y, b.y, x));
};

View File

@@ -0,0 +1,308 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines a 3-element vector class that can be used for
* coordinate math, useful for animation systems and point manipulation.
*
* Based heavily on code originally by:
*/
goog.provide('goog.math.Vec3');
goog.require('goog.math');
goog.require('goog.math.Coordinate3');
/**
* Class for a three-dimensional vector object and assorted functions useful for
* manipulation.
*
* Inherits from goog.math.Coordinate3 so that a Vec3 may be passed in to any
* function that requires a Coordinate.
*
* @param {number} x The x value for the vector.
* @param {number} y The y value for the vector.
* @param {number} z The z value for the vector.
* @constructor
* @extends {goog.math.Coordinate3}
*/
goog.math.Vec3 = function(x, y, z) {
/**
* X-value
* @type {number}
*/
this.x = x;
/**
* Y-value
* @type {number}
*/
this.y = y;
/**
* Z-value
* @type {number}
*/
this.z = z;
};
goog.inherits(goog.math.Vec3, goog.math.Coordinate3);
/**
* Generates a random unit vector.
*
* http://mathworld.wolfram.com/SpherePointPicking.html
* Using (6), (7), and (8) to generate coordinates.
* @return {!goog.math.Vec3} A random unit-length vector.
*/
goog.math.Vec3.randomUnit = function() {
var theta = Math.random() * Math.PI * 2;
var phi = Math.random() * Math.PI * 2;
var z = Math.cos(phi);
var x = Math.sqrt(1 - z * z) * Math.cos(theta);
var y = Math.sqrt(1 - z * z) * Math.sin(theta);
return new goog.math.Vec3(x, y, z);
};
/**
* Generates a random vector inside the unit sphere.
*
* @return {!goog.math.Vec3} A random vector.
*/
goog.math.Vec3.random = function() {
return goog.math.Vec3.randomUnit().scale(Math.random());
};
/**
* Returns a new Vec3 object from a given coordinate.
*
* @param {goog.math.Coordinate3} a The coordinate.
* @return {!goog.math.Vec3} A new vector object.
*/
goog.math.Vec3.fromCoordinate3 = function(a) {
return new goog.math.Vec3(a.x, a.y, a.z);
};
/**
* Creates a new copy of this Vec3.
*
* @return {!goog.math.Vec3} A new vector with the same coordinates as this one.
* @override
*/
goog.math.Vec3.prototype.clone = function() {
return new goog.math.Vec3(this.x, this.y, this.z);
};
/**
* Returns the magnitude of the vector measured from the origin.
*
* @return {number} The length of the vector.
*/
goog.math.Vec3.prototype.magnitude = function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};
/**
* Returns the squared magnitude of the vector measured from the origin.
* NOTE(brenneman): Leaving out the square root is not a significant
* optimization in JavaScript.
*
* @return {number} The length of the vector, squared.
*/
goog.math.Vec3.prototype.squaredMagnitude = function() {
return this.x * this.x + this.y * this.y + this.z * this.z;
};
/**
* Scales the current vector by a constant.
*
* @param {number} s The scale factor.
* @return {!goog.math.Vec3} This vector, scaled.
*/
goog.math.Vec3.prototype.scale = function(s) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
};
/**
* Reverses the sign of the vector. Equivalent to scaling the vector by -1.
*
* @return {!goog.math.Vec3} This vector, inverted.
*/
goog.math.Vec3.prototype.invert = function() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
return this;
};
/**
* Normalizes the current vector to have a magnitude of 1.
*
* @return {!goog.math.Vec3} This vector, normalized.
*/
goog.math.Vec3.prototype.normalize = function() {
return this.scale(1 / this.magnitude());
};
/**
* Adds another vector to this vector in-place.
*
* @param {goog.math.Vec3} b The vector to add.
* @return {!goog.math.Vec3} This vector with {@code b} added.
*/
goog.math.Vec3.prototype.add = function(b) {
this.x += b.x;
this.y += b.y;
this.z += b.z;
return this;
};
/**
* Subtracts another vector from this vector in-place.
*
* @param {goog.math.Vec3} b The vector to subtract.
* @return {!goog.math.Vec3} This vector with {@code b} subtracted.
*/
goog.math.Vec3.prototype.subtract = function(b) {
this.x -= b.x;
this.y -= b.y;
this.z -= b.z;
return this;
};
/**
* Compares this vector with another for equality.
*
* @param {goog.math.Vec3} b The other vector.
* @return {boolean} True if this vector's x, y and z equal the given vector's
* x, y, and z, respectively.
*/
goog.math.Vec3.prototype.equals = function(b) {
return this == b || !!b && this.x == b.x && this.y == b.y && this.z == b.z;
};
/**
* Returns the distance between two vectors.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {number} The distance.
*/
goog.math.Vec3.distance = goog.math.Coordinate3.distance;
/**
* Returns the squared distance between two vectors.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {number} The squared distance.
*/
goog.math.Vec3.squaredDistance = goog.math.Coordinate3.squaredDistance;
/**
* Compares vectors for equality.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {boolean} True if the vectors have equal x, y, and z coordinates.
*/
goog.math.Vec3.equals = goog.math.Coordinate3.equals;
/**
* Returns the sum of two vectors as a new Vec3.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {!goog.math.Vec3} The sum vector.
*/
goog.math.Vec3.sum = function(a, b) {
return new goog.math.Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
};
/**
* Returns the difference of two vectors as a new Vec3.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {!goog.math.Vec3} The difference vector.
*/
goog.math.Vec3.difference = function(a, b) {
return new goog.math.Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
};
/**
* Returns the dot-product of two vectors.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {number} The dot-product of the two vectors.
*/
goog.math.Vec3.dot = function(a, b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
};
/**
* Returns the cross-product of two vectors.
*
* @param {goog.math.Vec3} a The first vector.
* @param {goog.math.Vec3} b The second vector.
* @return {!goog.math.Vec3} The cross-product of the two vectors.
*/
goog.math.Vec3.cross = function(a, b) {
return new goog.math.Vec3(a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x);
};
/**
* Returns a new Vec3 that is the linear interpolant between vectors a and b at
* scale-value x.
*
* @param {goog.math.Vec3} a Vector a.
* @param {goog.math.Vec3} b Vector b.
* @param {number} x The proportion between a and b.
* @return {!goog.math.Vec3} The interpolated vector.
*/
goog.math.Vec3.lerp = function(a, b, x) {
return new goog.math.Vec3(goog.math.lerp(a.x, b.x, x),
goog.math.lerp(a.y, b.y, x),
goog.math.lerp(a.z, b.z, x));
};