UNPKG

kld-affine

Version:

A collection of classes used in affine geometry

1,038 lines (935 loc) 24.9 kB
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /** * Point2D.js * @module Point2D * @copyright 2001-2019 Kevin Lindsey */ /** * Point2D * * @memberof module:kld-affine */ var Point2D = /*#__PURE__*/ function () { /** * Point2D * * @param {number} x * @param {number} y * @returns {module:kld-affine.Point2D} */ function Point2D() { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; _classCallCheck(this, Point2D); this.x = x; this.y = y; } /** * clone * * @returns {module:kld-affine.Point2D} */ _createClass(Point2D, [{ key: "clone", value: function clone() { return new this.constructor(this.x, this.y); } /** * add * * @param {module:kld-affine.Point2D} that * @returns {module:kld-affine.Point2D} */ }, { key: "add", value: function add(that) { return new this.constructor(this.x + that.x, this.y + that.y); } /** * subtract * * @param {module:kld-affine.Point2D} that * @returns {module:kld-affine.Point2D} */ }, { key: "subtract", value: function subtract(that) { return new this.constructor(this.x - that.x, this.y - that.y); } /** * multiply * * @param {number} scalar * @returns {module:kld-affine.Point2D} */ }, { key: "multiply", value: function multiply(scalar) { return new this.constructor(this.x * scalar, this.y * scalar); } /** * divide * * @param {number} scalar * @returns {module:kld-affine.Point2D} */ }, { key: "divide", value: function divide(scalar) { return new this.constructor(this.x / scalar, this.y / scalar); } /** * equals * * @param {module:kld-affine.Point2D} that * @returns {boolean} */ }, { key: "equals", value: function equals(that) { return this.x === that.x && this.y === that.y; } /** * precisionEquals * * @param {module:kld-affine.Point2D} that * @param {number} precision * @returns {boolean} */ }, { key: "precisionEquals", value: function precisionEquals(that, precision) { return Math.abs(this.x - that.x) < precision && Math.abs(this.y - that.y) < precision; } // utility methods /** * lerp * * @param {module:kld-affine.Point2D} that * @param {number} t * @returns {module:kld-affine.Point2D} */ }, { key: "lerp", value: function lerp(that, t) { var omt = 1.0 - t; return new this.constructor(this.x * omt + that.x * t, this.y * omt + that.y * t); } /** * distanceFrom * * @param {module:kld-affine.Point2D} that * @returns {number} */ }, { key: "distanceFrom", value: function distanceFrom(that) { var dx = this.x - that.x; var dy = this.y - that.y; return Math.sqrt(dx * dx + dy * dy); } /** * min * * @param {module:kld-affine.Point2D} that * @returns {number} */ }, { key: "min", value: function min(that) { return new this.constructor(Math.min(this.x, that.x), Math.min(this.y, that.y)); } /** * max * * @param {module:kld-affine.Point2D} that * @returns {number} */ }, { key: "max", value: function max(that) { return new this.constructor(Math.max(this.x, that.x), Math.max(this.y, that.y)); } /** * transform * * @param {module:kld-affine.Matrix2D} matrix * @returns {module:kld-affine.Point2D} */ }, { key: "transform", value: function transform(matrix) { return new this.constructor(matrix.a * this.x + matrix.c * this.y + matrix.e, matrix.b * this.x + matrix.d * this.y + matrix.f); } /** * toString * * @returns {string} */ }, { key: "toString", value: function toString() { return "point(".concat(this.x, ",").concat(this.y, ")"); } }]); return Point2D; }(); /** * Vector2D.js * @module Vector2D * @copyright 2001-2019 Kevin Lindsey */ /** * Vector2D * * @memberof module:kld-affine */ var Vector2D = /*#__PURE__*/ function () { /** * Vector2D * * @param {number} x * @param {number} y * @returns {module:kld-affine.Vector2D} */ function Vector2D() { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; _classCallCheck(this, Vector2D); this.x = x; this.y = y; } /** * fromPoints * * @param {module:kld-affine.Point2D} p1 * @param {module:kld-affine.Point2D} p2 * @returns {module:kld-affine.Vector2D} */ _createClass(Vector2D, [{ key: "length", /** * length * * @returns {number} */ value: function length() { return Math.sqrt(this.x * this.x + this.y * this.y); } /** * magnitude * * @returns {number} */ }, { key: "magnitude", value: function magnitude() { return this.x * this.x + this.y * this.y; } /** * dot * * @param {module:kld-affine.Vector2D} that * @returns {number} */ }, { key: "dot", value: function dot(that) { return this.x * that.x + this.y * that.y; } /** * cross * * @param {module:kld-affine.Vector2D} that * @returns {number} */ }, { key: "cross", value: function cross(that) { return this.x * that.y - this.y * that.x; } /** * determinant * * @param {module:kld-affine.Vector2D} that * @returns {number} */ }, { key: "determinant", value: function determinant(that) { return this.x * that.y - this.y * that.x; } /** * unit * * @returns {module:kld-affine.Vector2D} */ }, { key: "unit", value: function unit() { return this.divide(this.length()); } /** * add * * @param {module:kld-affine.Vector2D} that * @returns {module:kld-affine.Vector2D} */ }, { key: "add", value: function add(that) { return new this.constructor(this.x + that.x, this.y + that.y); } /** * subtract * * @param {module:kld-affine.Vector2D} that * @returns {module:kld-affine.Vector2D} */ }, { key: "subtract", value: function subtract(that) { return new this.constructor(this.x - that.x, this.y - that.y); } /** * multiply * * @param {number} scalar * @returns {module:kld-affine.Vector2D} */ }, { key: "multiply", value: function multiply(scalar) { return new this.constructor(this.x * scalar, this.y * scalar); } /** * divide * * @param {number} scalar * @returns {module:kld-affine.Vector2D} */ }, { key: "divide", value: function divide(scalar) { return new this.constructor(this.x / scalar, this.y / scalar); } /** * angleBetween * * @param {module:kld-affine.Vector2D} that * @returns {number} */ }, { key: "angleBetween", value: function angleBetween(that) { var cos = this.dot(that) / (this.length() * that.length()); cos = Math.max(-1, Math.min(cos, 1)); var radians = Math.acos(cos); return this.cross(that) < 0.0 ? -radians : radians; } /** * Find a vector is that is perpendicular to this vector * * @returns {module:kld-affine.Vector2D} */ }, { key: "perp", value: function perp() { return new this.constructor(-this.y, this.x); } /** * Find the component of the specified vector that is perpendicular to * this vector * * @param {module:kld-affine.Vector2D} that * @returns {module:kld-affine.Vector2D} */ }, { key: "perpendicular", value: function perpendicular(that) { return this.subtract(this.project(that)); } /** * project * * @param {module:kld-affine.Vector2D} that * @returns {module:kld-affine.Vector2D} */ }, { key: "project", value: function project(that) { var percent = this.dot(that) / that.dot(that); return that.multiply(percent); } /** * transform * * @param {module:kld-affine.Matrix2D} matrix * @returns {module:kld-affine.Vector2D} */ }, { key: "transform", value: function transform(matrix) { return new this.constructor(matrix.a * this.x + matrix.c * this.y, matrix.b * this.x + matrix.d * this.y); } /** * equals * * @param {module:kld-affine.Vector2D} that * @returns {boolean} */ }, { key: "equals", value: function equals(that) { return this.x === that.x && this.y === that.y; } /** * precisionEquals * * @param {module:kld-affine.Vector2D} that * @param {number} precision * @returns {boolean} */ }, { key: "precisionEquals", value: function precisionEquals(that, precision) { return Math.abs(this.x - that.x) < precision && Math.abs(this.y - that.y) < precision; } /** * toString * * @returns {string} */ }, { key: "toString", value: function toString() { return "vector(".concat(this.x, ",").concat(this.y, ")"); } }], [{ key: "fromPoints", value: function fromPoints(p1, p2) { return new Vector2D(p2.x - p1.x, p2.y - p1.y); } }]); return Vector2D; }(); /** * Matrix2D.js * @module Matrix2D * @copyright 2001-2019 Kevin Lindsey */ /** * Matrix2D * * @memberof module:kld-affine */ var Matrix2D = /*#__PURE__*/ function () { /** * A 2D Matrix of the form:<br> * [a c e]<br> * [b d f]<br> * [0 0 1]<br> * * @param {number} a * @param {number} b * @param {number} c * @param {number} d * @param {number} e * @param {number} f * @returns {module:kld-affine.Matrix2D} */ function Matrix2D() { var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var c = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var d = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1; var e = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var f = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; _classCallCheck(this, Matrix2D); this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; } /** * translation * * @param {number} tx * @param {number} ty * @returns {module:kld-affine.Matrix2D} */ _createClass(Matrix2D, [{ key: "multiply", /** * multiply * * @param {module:kld-affine.Matrix2D} that * @returns {module:kld-affine.Matrix2D} */ value: function multiply(that) { if (this.isIdentity()) { return that; } if (that.isIdentity()) { return this; } return new this.constructor(this.a * that.a + this.c * that.b, this.b * that.a + this.d * that.b, this.a * that.c + this.c * that.d, this.b * that.c + this.d * that.d, this.a * that.e + this.c * that.f + this.e, this.b * that.e + this.d * that.f + this.f); } /** * inverse * * @returns {module:kld-affine.Matrix2D} */ }, { key: "inverse", value: function inverse() { if (this.isIdentity()) { return this; } var det1 = this.a * this.d - this.b * this.c; if (det1 === 0.0) { throw new Error("Matrix is not invertible"); } var idet = 1.0 / det1; var det2 = this.f * this.c - this.e * this.d; var det3 = this.e * this.b - this.f * this.a; return new this.constructor(this.d * idet, -this.b * idet, -this.c * idet, this.a * idet, det2 * idet, det3 * idet); } /** * translate * * @param {number} tx * @param {number} ty * @returns {module:kld-affine.Matrix2D} */ }, { key: "translate", value: function translate(tx, ty) { return new this.constructor(this.a, this.b, this.c, this.d, this.a * tx + this.c * ty + this.e, this.b * tx + this.d * ty + this.f); } /** * scale * * @param {number} scale * @returns {module:kld-affine.Matrix2D} */ }, { key: "scale", value: function scale(_scale) { return new this.constructor(this.a * _scale, this.b * _scale, this.c * _scale, this.d * _scale, this.e, this.f); } /** * scaleAt * * @param {number} scale * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "scaleAt", value: function scaleAt(scale, center) { var dx = center.x - scale * center.x; var dy = center.y - scale * center.y; return new this.constructor(this.a * scale, this.b * scale, this.c * scale, this.d * scale, this.a * dx + this.c * dy + this.e, this.b * dx + this.d * dy + this.f); } /** * scaleNonUniform * * @param {number} scaleX * @param {number} scaleY * @returns {module:kld-affine.Matrix2D} */ }, { key: "scaleNonUniform", value: function scaleNonUniform(scaleX, scaleY) { return new this.constructor(this.a * scaleX, this.b * scaleX, this.c * scaleY, this.d * scaleY, this.e, this.f); } /** * scaleNonUniformAt * * @param {number} scaleX * @param {number} scaleY * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "scaleNonUniformAt", value: function scaleNonUniformAt(scaleX, scaleY, center) { var dx = center.x - scaleX * center.x; var dy = center.y - scaleY * center.y; return new this.constructor(this.a * scaleX, this.b * scaleX, this.c * scaleY, this.d * scaleY, this.a * dx + this.c * dy + this.e, this.b * dx + this.d * dy + this.f); } /** * rotate * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotate", value: function rotate(radians) { var c = Math.cos(radians); var s = Math.sin(radians); return new this.constructor(this.a * c + this.c * s, this.b * c + this.d * s, this.a * -s + this.c * c, this.b * -s + this.d * c, this.e, this.f); } /** * rotateAt * * @param {number} radians * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotateAt", value: function rotateAt(radians, center) { var cos = Math.cos(radians); var sin = Math.sin(radians); var cx = center.x; var cy = center.y; var a = this.a * cos + this.c * sin; var b = this.b * cos + this.d * sin; var c = this.c * cos - this.a * sin; var d = this.d * cos - this.b * sin; return new this.constructor(a, b, c, d, (this.a - a) * cx + (this.c - c) * cy + this.e, (this.b - b) * cx + (this.d - d) * cy + this.f); } /** * rotateFromVector * * @param {module:kld-affine.Vector2D} vector * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotateFromVector", value: function rotateFromVector(vector) { var unit = vector.unit(); var c = unit.x; // cos var s = unit.y; // sin return new this.constructor(this.a * c + this.c * s, this.b * c + this.d * s, this.a * -s + this.c * c, this.b * -s + this.d * c, this.e, this.f); } /** * flipX * * @returns {module:kld-affine.Matrix2D} */ }, { key: "flipX", value: function flipX() { return new this.constructor(-this.a, -this.b, this.c, this.d, this.e, this.f); } /** * flipY * * @returns {module:kld-affine.Matrix2D} */ }, { key: "flipY", value: function flipY() { return new this.constructor(this.a, this.b, -this.c, -this.d, this.e, this.f); } /** * skewX * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "skewX", value: function skewX(radians) { var t = Math.tan(radians); return new this.constructor(this.a, this.b, this.c + this.a * t, this.d + this.b * t, this.e, this.f); } // TODO: skewXAt /** * skewY * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "skewY", value: function skewY(radians) { var t = Math.tan(radians); return new this.constructor(this.a + this.c * t, this.b + this.d * t, this.c, this.d, this.e, this.f); } // TODO: skewYAt /** * isIdentity * * @returns {boolean} */ }, { key: "isIdentity", value: function isIdentity() { return this.a === 1.0 && this.b === 0.0 && this.c === 0.0 && this.d === 1.0 && this.e === 0.0 && this.f === 0.0; } /** * isInvertible * * @returns {boolean} */ }, { key: "isInvertible", value: function isInvertible() { return this.a * this.d - this.b * this.c !== 0.0; } /** * getScale * * @returns {{ scaleX: number, scaleY: number }} */ }, { key: "getScale", value: function getScale() { return { scaleX: Math.sqrt(this.a * this.a + this.c * this.c), scaleY: Math.sqrt(this.b * this.b + this.d * this.d) }; } /** * Calculates matrix Singular Value Decomposition * * The resulting matrices — translation, rotation, scale, and rotation0 — return * this matrix when they are multiplied together in the listed order * * @see Jim Blinn's article {@link http://dx.doi.org/10.1109/38.486688} * @see {@link http://math.stackexchange.com/questions/861674/decompose-a-2d-arbitrary-transform-into-only-scaling-and-rotation} * * @returns {{ * translation: module:kld-affine.Matrix2D, * rotation: module:kld-affine.Matrix2D, * scale: module:kld-affine.Matrix2D, * rotation0: module:kld-affine.Matrix2D * }} */ }, { key: "getDecomposition", value: function getDecomposition() { var E = (this.a + this.d) * 0.5; var F = (this.a - this.d) * 0.5; var G = (this.b + this.c) * 0.5; var H = (this.b - this.c) * 0.5; var Q = Math.sqrt(E * E + H * H); var R = Math.sqrt(F * F + G * G); var scaleX = Q + R; var scaleY = Q - R; var a1 = Math.atan2(G, F); var a2 = Math.atan2(H, E); var theta = (a2 - a1) * 0.5; var phi = (a2 + a1) * 0.5; return { translation: this.constructor.translation(this.e, this.f), rotation: this.constructor.rotation(phi), scale: this.constructor.nonUniformScaling(scaleX, scaleY), rotation0: this.constructor.rotation(theta) }; } /** * equals * * @param {module:kld-affine.Matrix2D} that * @returns {boolean} */ }, { key: "equals", value: function equals(that) { return this.a === that.a && this.b === that.b && this.c === that.c && this.d === that.d && this.e === that.e && this.f === that.f; } /** * precisionEquals * * @param {module:kld-affine.Matrix2D} that * @param {number} precision * @returns {boolean} */ }, { key: "precisionEquals", value: function precisionEquals(that, precision) { return Math.abs(this.a - that.a) < precision && Math.abs(this.b - that.b) < precision && Math.abs(this.c - that.c) < precision && Math.abs(this.d - that.d) < precision && Math.abs(this.e - that.e) < precision && Math.abs(this.f - that.f) < precision; } /** * toString * * @returns {string} */ }, { key: "toString", value: function toString() { return "matrix(".concat(this.a, ",").concat(this.b, ",").concat(this.c, ",").concat(this.d, ",").concat(this.e, ",").concat(this.f, ")"); } }], [{ key: "translation", value: function translation(tx, ty) { return new Matrix2D(1, 0, 0, 1, tx, ty); } /** * scaling * * @param {number} scale * @returns {module:kld-affine.Matrix2D} */ }, { key: "scaling", value: function scaling(scale) { return new Matrix2D(scale, 0, 0, scale, 0, 0); } /** * scalingAt * * @param {number} scale * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "scalingAt", value: function scalingAt(scale, center) { return new Matrix2D(scale, 0, 0, scale, center.x - center.x * scale, center.y - center.y * scale); } /** * nonUniformScaling * * @param {number} scaleX * @param {number} scaleY * @returns {module:kld-affine.Matrix2D} */ }, { key: "nonUniformScaling", value: function nonUniformScaling(scaleX, scaleY) { return new Matrix2D(scaleX, 0, 0, scaleY, 0, 0); } /** * nonUniformScalingAt * * @param {number} scaleX * @param {number} scaleY * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "nonUniformScalingAt", value: function nonUniformScalingAt(scaleX, scaleY, center) { return new Matrix2D(scaleX, 0, 0, scaleY, center.x - center.x * scaleX, center.y - center.y * scaleY); } /** * rotation * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotation", value: function rotation(radians) { var c = Math.cos(radians); var s = Math.sin(radians); return new Matrix2D(c, s, -s, c, 0, 0); } /** * rotationAt * * @param {number} radians * @param {module:kld-affine.Point2D} center * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotationAt", value: function rotationAt(radians, center) { var c = Math.cos(radians); var s = Math.sin(radians); return new Matrix2D(c, s, -s, c, center.x - center.x * c + center.y * s, center.y - center.y * c - center.x * s); } /** * rotationFromVector * * @param {module:kld-affine.Vector2D} vector * @returns {module:kld-affine.Matrix2D} */ }, { key: "rotationFromVector", value: function rotationFromVector(vector) { var unit = vector.unit(); var c = unit.x; // cos var s = unit.y; // sin return new Matrix2D(c, s, -s, c, 0, 0); } /** * xFlip * * @returns {module:kld-affine.Matrix2D} */ }, { key: "xFlip", value: function xFlip() { return new Matrix2D(-1, 0, 0, 1, 0, 0); } /** * yFlip * * @returns {module:kld-affine.Matrix2D} */ }, { key: "yFlip", value: function yFlip() { return new Matrix2D(1, 0, 0, -1, 0, 0); } /** * xSkew * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "xSkew", value: function xSkew(radians) { var t = Math.tan(radians); return new Matrix2D(1, 0, t, 1, 0, 0); } /** * ySkew * * @param {number} radians * @returns {module:kld-affine.Matrix2D} */ }, { key: "ySkew", value: function ySkew(radians) { var t = Math.tan(radians); return new Matrix2D(1, t, 0, 1, 0, 0); } }]); return Matrix2D; }(); /** * Identity matrix * * @returns {module:kld-affine.Matrix2D} */ Matrix2D.IDENTITY = new Matrix2D(); Matrix2D.IDENTITY.isIdentity = function () { return true; }; /** * @module kld-affine */ export { Matrix2D, Point2D, Vector2D };