UNPKG

js-2dmath

Version:

Fast 2d geometry math: Vector2, Rectangle, Circle, Matrix2x3 (2D transformation), Circle, BoundingBox, Line2, Segment2, Intersections, Distances, Transitions (animation/tween), Random numbers, Noise

826 lines (714 loc) 19.1 kB
/** * Stability: 1 (Only additions & fixes) * * 2x3 Transformation matrix used in 2D (column-major) represented as a 8 coordinates array * [m11:Number, m12:Number, m13:Number, m21:Number, m22:Number, m23:Number, **cache**:Array(5), dirty:Boolean] * cache = [xScale:Number, yScale:Number, xSkew:Number, yScale:Number, rotation:Number] * * why cache? Speed improvements in exchange of memory to avoid tan/atan2/sqrt. * * why dirty? Matrix.transform could be expensive with large polygons, keep track of this variable to transform only when necessary. * @TODO dSetSkewX / dSetSkewY */ // cache variables var DEG_TO_RAD = Math.DEG_TO_RAD, PI = Math.PI, cos = Math.cos, sin = Math.sin, tan = Math.tan, atan2 = Math.atan2, __x, __y, aux_vec = [0, 0], c = 0, s = 0, angle = 0, m11 = 0, m12 = 0, m21 = 0, m22 = 0, dx = 0, dy = 0; /** * Creates a new identity 2x3 matrix * @return {Matrix23} */ function create() { return [1, 0, 0, 1, 0, 0, [1, 1, 0, 0, 0], false]; } /** * Creates a new matrix given 4 points(a Rectangle) * * @todo * @see http://jsfiddle.net/dFrHS/1/ * @return {Matrix23} a new 2x3 matrix */ function fromPoints() { } /** * Creates a new matrix given 4 points(a Rectangle) * * @todo * @see http://jsfiddle.net/dFrHS/1/ * @return {Matrix23} a new 2x3 matrix */ function fromAngle() { return [1, 0, 0, 1, 0, 0, [1, 1, 0, 0, 0], false]; } /** * Copy m2d into out * * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @return {Matrix23} out 2x3 matrix */ function copy(out, m2d) { out[0] = m2d[0]; out[1] = m2d[1]; out[2] = m2d[2]; out[3] = m2d[3]; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = m2d[7]; return out; } /** * Copy m2d into out * * @param {Matrix23} out destiny matrix * @return {Matrix23} out 2x3 matrix */ function identity(out) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 1; out[4] = 0; out[5] = 0; out[6][0] = 1; out[6][1] = 1; out[6][2] = 0; out[6][3] = 0; out[6][4] = 0; out[7] = false; return out; } /** * Rotates a Matrix23 by the given angle in degrees(increment rotation) * @note increment rotation * * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} degrees Degrees * @return {Matrix23} out 2x3 matrix */ function dRotate(out, m2d, degrees) { return rotate(out, m2d, degrees * DEG_TO_RAD); } /** * Rotates a Matrix23 by the given angle in radians(increment rotation) * @note increment rotation * * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} radians Radians * @return {Matrix23} out 2x3 matrix */ function rotate(out, m2d, radians) { c = cos(radians); s = sin(radians); m11 = m2d[0] * c + m2d[2] * s; m12 = m2d[1] * c + m2d[3] * s; m21 = m2d[0] * -s + m2d[2] * c; m22 = m2d[1] * -s + m2d[3] * c; out[0] = m11; out[1] = m12; out[2] = m21; out[3] = m22; // copy out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4] + radians; out[7] = true; return out; } /** * Set rotation of a Matrix23 by the given angle in degrees(set rotation) * @note set rotation * * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} degrees Degrees * @return {Matrix23} out 2x3 matrix */ function dRotation(out, m2d, degrees) { return rotation(out, m2d, degrees * DEG_TO_RAD); } /** * Set rotation of a Matrix23 by the given angle in radians(set rotation) * @note set rotation * * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} radians Radians * @return {Matrix23} out 2x3 matrix */ function rotation(out, m2d, radians) { c = radians - out[6][4]; rotate(out, m2d, c); out[6][4] = radians; out[7] = true; return out; } /** * Translates given Matrix23 by the dimensions in the given vec2 * @note This translation is affected by rotation/skew * @note increment position * @see gTranslate * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2 amount to be translated * @return {Matrix23} out 2x3 matrix */ function translate(out, m2d, vec2) { out[0] = m2d[0]; out[1] = m2d[1]; out[2] = m2d[2]; out[3] = m2d[3]; out[4] = m2d[4] + m2d[0] * vec2[0] + m2d[2] * vec2[1]; out[5] = m2d[5] + m2d[1] * vec2[0] + m2d[3] * vec2[1]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Translates given Matrix23 by the dimensions in the given vec2 * @note This translation is NOT affected by rotation/skew * @note increment position * @see translate * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2 amount to be translated * @return {Matrix23} out 2x3 matrix */ function gTranslate(out, m2d, vec2) { out[0] = m2d[0]; out[1] = m2d[1]; out[2] = m2d[2]; out[3] = m2d[3]; out[4] = m2d[4] + vec2[0]; out[5] = m2d[5] + vec2[1]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Set Matrix23 position * @note This translation is NOT affected by rotation/skew * @note set position * @see gTranslate * @see translate * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2 destiny position * @return {Matrix23} out 2x3 matrix */ function position(out, m2d, vec2) { out[0] = m2d[0]; out[1] = m2d[1]; out[2] = m2d[2]; out[3] = m2d[3]; out[4] = vec2[0]; out[5] = vec2[1]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Scales the Matrix23 by the dimensions in the given vec2 * @note incremental scale * @note do not affect position * @see scalation * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2 destiny position * @return {Matrix23} out 2x3 matrix */ function scale(out, m2d, vec2) { __x = vec2[0]; __y = vec2[1]; out[0] = m2d[0] * __x; out[1] = m2d[1] * __x; out[2] = m2d[2] * __y; out[3] = m2d[3] * __y; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0] * __x; out[6][1] = m2d[6][1] * __y; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Set the Matrix23 scale by the dimensions in the given vec2 * @note set scale * @note do not affect position * @see scale * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2 destiny position * @return {Matrix23} out 2x3 matrix */ function scalation(out, m2d, vec2) { return scale(out, m2d, [vec2[0] / m2d[6][0], vec2[1] / m2d[6][1]]); } /** * Increment the Matrix23 x-skew by given degrees * * @note increment skewX * @see skewX * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} degrees Degrees to skew * @return {Matrix23} out 2x3 matrix */ function dSkewX(out, m2d, degrees) { return skewX(out, m2d, degrees * DEG_TO_RAD); } /** * Increment the Matrix23 x-skew by given radians * * @note increment skewX * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} radians Radians to skew * @return {Matrix23} out 2x3 matrix */ function skewX(out, m2d, radians) { angle = tan(radians); out[0] = m2d[0]; out[1] = m2d[1]; out[2] = m2d[2] + m2d[0] * angle; out[3] = m2d[3] + m2d[1] * angle; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2] + radians; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Increment the Matrix23 y-skew by given degrees * * @note increment skewY * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} degrees Degrees to skew * @return {Matrix23} out 2x3 matrix */ function dSkewY(out, m2d, degrees) { return skewY(out, m2d, degrees * DEG_TO_RAD); } /** * Increment the Matrix23 y-skew by given radians * * @note increment skewY * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Number} radians Radians to skew * @return {Matrix23} out 2x3 matrix */ function skewY(out, m2d, radians) { angle = tan(radians); out[0] = m2d[0] + m2d[2] * angle; out[1] = m2d[1] + m2d[3] * angle; out[2] = m2d[2]; out[3] = m2d[3]; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3] + angle; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Increment the Matrix23 skew y by given degrees in vec2_degrees * * @note increment skew * @see dSetSkew * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2_degrees Degrees to skew * @return {Matrix23} out 2x3 matrix */ function dSkew(out, m2d, vec2_degrees) { aux_vec[0] = vec2_degrees[0] * DEG_TO_RAD; aux_vec[1] = vec2_degrees[1] * DEG_TO_RAD; return skew(out, m2d, aux_vec); } /** * Increment the Matrix23 skew y by given radians in vec2 * * @note increment skew * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2_radians Radians to skew * @return {Matrix23} out 2x3 matrix */ function skew(out, m2d, vec2_radians) { c = tan(vec2_radians[0]); s = tan(vec2_radians[1]); out[0] = m2d[0] + m2d[2] * s; out[1] = m2d[1] + m2d[3] * s; out[2] = m2d[2] + m2d[0] * c; out[3] = m2d[3] + m2d[1] * c; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2] + vec2_radians[0]; out[6][3] = m2d[6][3] + vec2_radians[1]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Set the Matrix23 skew y by given degrees in vec2_degrees * * @note set skew * @see setSkew * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2_degrees Degrees to skew * @return {Matrix23} out 2x3 matrix */ function dSetSkew(out, m2d, vec2_degrees) { aux_vec[0] = vec2_degrees[0] * DEG_TO_RAD; aux_vec[1] = vec2_degrees[1] * DEG_TO_RAD; return setSkew(out, m2d, aux_vec); } /** * Set the Matrix23 skew y by given radians in vec2 * * @note set skew * @param {Matrix23} out destiny matrix * @param {Matrix23} m2d source matrix * @param {Vec2} vec2_radians Radians to skew * @return {Matrix23} out 2x3 matrix */ function setSkew(out, m2d, vec2_radians) { c = tan(vec2_radians[0] - m2d[6][2]); s = tan(vec2_radians[1] - m2d[6][3]); out[0] = m2d[0] + m2d[2] * s; out[1] = m2d[1] + m2d[3] * s; out[2] = m2d[2] + m2d[0] * c; out[3] = m2d[3] + m2d[1] * c; out[4] = m2d[4]; out[5] = m2d[5]; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = vec2_radians[0]; out[6][3] = vec2_radians[1]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Multiplies two Matrix23's * * @param {Matrix23} out destiny matrix(A*B) * @param {Matrix23} m2d A matrix * @param {Matrix23} m2d_2 B matrix * @return {Matrix23} out 2x3 matrix */ function multiply(out, m2d, m2d_2) { m11 = m2d[0] * m2d_2[0] + m2d[2] * m2d_2[1]; m12 = m2d[1] * m2d_2[0] + m2d[3] * m2d_2[1]; m21 = m2d[0] * m2d_2[2] + m2d[2] * m2d_2[3]; m22 = m2d[1] * m2d_2[2] + m2d[3] * m2d_2[3]; dx = m2d[0] * m2d_2[4] + m2d[2] * m2d_2[5] + m2d[4]; dy = m2d[1] * m2d_2[4] + m2d[3] * m2d_2[5] + m2d[5]; out[0] = m11; out[1] = m12; out[2] = m21; out[3] = m22; out[4] = dx; out[5] = dy; out[6][0] = m2d[6][0]; out[6][1] = m2d[6][1]; out[6][2] = m2d[6][2]; out[6][3] = m2d[6][3]; out[6][4] = m2d[6][4]; out[7] = true; return out; } /** * Multiplies a Matrix23 and a Vec2 * * @param {Vec2} out_vec2 destiny Vec2 * @param {Matrix23} m2d source Matrix23 * @param {Vec2} vec2 * @return {Vec2} out_vec2, result Vec2 */ function multiplyVec2(out_vec2, m2d, vec2) { out_vec2[0] = vec2[0] * m2d[0] + vec2[0] * m2d[2] + vec2[0] * m2d[4]; out_vec2[1] = vec2[1] * m2d[1] + vec2[1] * m2d[3] + vec2[1] * m2d[5]; return out_vec2; } /** * Retrieve current position as Vec2 * * @param {Vec2} out_vec2 destiny Vec2 * @param {Matrix23} m2d source Matrix23 * @return {Vec2} out_vec2, result Vec2 */ function getPosition(out_vec2, m2d) { out_vec2[0] = m2d[4]; out_vec2[1] = m2d[5]; return out_vec2; } /** * Retrieve current scale as Vec2 * * @param {Vec2} out_vec2 destiny Vec2 * @param {Matrix23} m2d source Matrix23 * @return {Vec2} out_vec2, result Vec2 */ function getScale(out_vec2, m2d) { out_vec2[0] = m2d[6][0]; out_vec2[1] = m2d[6][1]; return out_vec2; } /** * Retrieve current skew as Vec2 * * @param {Vec2} out_vec2 destiny Vec2 * @param {Matrix23} m2d source Matrix23 * @return {Vec2} out_vec2, result Vec2 */ function getSkew(out_vec2, m2d) { out_vec2[0] = m2d[6][2]; out_vec2[1] = m2d[6][3]; return out_vec2; } /** * Alias of rotate 180º(PI) * * @param {Matrix23} out destiny Matrix23 * @param {Matrix23} m2d source Matrix23 * @return {Matrix23} out 2x3 matrix */ function reflect(out, m2d) { return rotate(out, m2d, PI); } /** * @TODO needed ? * @param {Matrix23} out destiny Matrix23 * @param {Matrix23} m2d source Matrix23 */ function transpose(out, m2d) { } /** * @TODO review & test * @param {Matrix23} m2d source Matrix23 * @return {Number} */ function determinant(m2d) { var fCofactor00 = m2d[1][1] * m2d[2][2] - m2d[1][2] * m2d[2][1], fCofactor10 = m2d[1][2] * m2d[2][0] - m2d[1][0] * m2d[2][2], fCofactor20 = m2d[1][0] * m2d[2][1] - m2d[1][1] * m2d[2][0]; return m2d[0][0] * fCofactor00 + m2d[0][1] * fCofactor10 + m2d[0][2] * fCofactor20; } /** * Returns a 3x2 2D column-major translation matrix for x and y. * * @param {Number} x * @param {Number} y * @return {Matrix23} a new 2x3 matrix */ function translationMatrix(x, y) { return [ 1, 0, 0, 1, x, y ]; } /** * Returns a 3x2 2D column-major y-skew matrix for the given degrees. * * @param {Number} degrees * @return {Matrix23} a new 2x3 matrix */ function dSkewXMatrix(degrees) { return [ 1, 0, tan(degrees * 0.017453292519943295769236907684886), 1, 0, 0 ]; } /** * Returns a 3x2 2D column-major y-skew matrix for the given radians. * * @param {Number} radians * @return {Matrix23} a new 2x3 matrix */ function skewXMatrix(radians) { return [ 1, 0, tan(radians), 1, 0, 0 ]; } /** * Returns a 3x2 2D column-major y-skew matrix for the given degrees. * * @param {Number} degrees * @return {Matrix23} a new 2x3 matrix */ function dSkewYMatrix(degrees) { return [ 1, tan(degrees * 0.017453292519943295769236907684886), 0, 1, 0, 0 ]; } /** * Returns a 3x2 2D column-major y-skew matrix for the given radians. * * @param {Number} radians * @return {Matrix23} a new 2x3 matrix */ function skewYMatrix(radians) { return [ 1, tan(radians), 0, 1, 0, 0 ]; } /** * Returns a 3x2 2D column-major y-skew matrix for the given radians. * * @param {Number} radians * @return {Matrix23} a new 2x3 matrix */ function rotationMatrix(radians) { var c = cos(radians), s = sin(radians); return [c, -s, s, c, 0, 0 ]; } /** * Returns a 3x2 2D column-major scaling matrix for x and y. * * @param {Number} x * @param {Number} y */ function scalingMatrix(x, y) { return [ x, 0, 0, y, 0, 0 ]; } /** * Interpolate two matrixes by given factor. * Used in conjunction with Transitions and you will have nice transformations :) * * @param {Matrix23} out * @param {Matrix23} m2d first matrix * @param {Matrix23} m2d_2 second matrix * @param {Number} factor * @return {Matrix23} */ function interpolate(out, m2d, m2d_2, factor) { out[0] = m2d[0] + ((m2d_2[0] - m2d[0]) * factor); out[1] = m2d[1] + ((m2d_2[1] - m2d[1]) * factor); out[2] = m2d[2] + ((m2d_2[2] - m2d[2]) * factor); out[3] = m2d[3] + ((m2d_2[3] - m2d[3]) * factor); out[4] = m2d[4] + ((m2d_2[4] - m2d[4]) * factor); out[5] = m2d[5] + ((m2d_2[5] - m2d[5]) * factor); var o = out[6], i1 = m2d[6], i2 = m2d_2[6]; o[0] = i1[0] + ((i2[0] - i1[0]) * factor); o[1] = i1[1] + ((i2[1] - i1[1]) * factor); o[2] = i1[2] + ((i2[2] - i1[2]) * factor); o[3] = i1[3] + ((i2[3] - i1[3]) * factor); o[4] = i1[4] + ((i2[4] - i1[4]) * factor); out[7] = m2d[7]; return out; } /** * For completeness because it's not need in the current implementation just get: m2d[6][4] * * @param {Matrix23} m2d */ function toAngle(m2d) { return atan2(m2d[1], m2d[0]); } /** * Transform a vector by given matrix * * @param {Vec2} out_vec2 * @param {Matrix23} m2d * @param {Vec2} vec2 * @return {Vec2} */ function transform(out_vec2, m2d, vec2) { var x = vec2[0] * m2d[0] + vec2[1] * m2d[2] + m2d[4], y = vec2[0] * m2d[1] + vec2[1] * m2d[3] + m2d[5]; out_vec2[0] = x; out_vec2[1] = y; return out_vec2; } var Matrix23 = { create: create, fromPoints: fromPoints, copy: copy, identity: identity, dRotate: dRotate, rotate: rotate, dRotation: dRotation, rotation: rotation, translate: translate, gTranslate: gTranslate, position: position, scale: scale, scalation: scalation, dSkewX: dSkewX, skewX: skewX, dSkewY: dSkewY, skewY: skewY, dSkew: dSkew, skew: skew, dSetSkew: dSetSkew, setSkew: setSkew, multiply: multiply, multiplyVec2: multiplyVec2, getPosition: getPosition, getScale: getScale, getSkew: getSkew, reflect: reflect, transpose: transpose, determinant: determinant, translationMatrix: translationMatrix, dSkewXMatrix: dSkewXMatrix, skewXMatrix: skewXMatrix, dSkewYMatrix: dSkewYMatrix, skewYMatrix: skewYMatrix, scalingMatrix: scalingMatrix, rotationMatrix: rotationMatrix, interpolate: interpolate, transform: transform, toAngle: toAngle, // alias dSetRotation: dRotation, setRotation: rotation, setPosition: position, setScale: scalation, }; module.exports = Matrix23;