/*
This file was derived from the p5.js source code at
https://github.com/processing/p5.js
Copyright (c) the p5.js contributors and Andre Seidelt <superilu@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// https://academo.org/demos/rotation-about-point/
// https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
// http://www.calcul.com/show/calculator/matrix-multiplication_;3;3;3;1?matrix1=[[%2211%22,%2212%22,%2213%22],[%2221%22,%2222%22,%2223%22],[%2231%22,%2232%22,%2233%22]]&matrix2=[[%22x%22],[%22y%22],[%221%22]]&operator=*
// https://stackoverflow.com/questions/27205018/multiply-2-matrices-in-javascript
/**
* @module p5compat
*/
/**
* make sure we have a 2x2 identity matrix.
*/
exports._ensureMatrix = function () {
if (!_currentEnv._matrix) {
_currentEnv._matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
}
exports._MaMul = function (a, b) {
var aNumRows = a.length, aNumCols = a[0].length,
bNumRows = b.length, bNumCols = b[0].length,
m = new Array(aNumRows); // initialize array of rows
for (var r = 0; r < aNumRows; ++r) {
m[r] = new Array(bNumCols); // initialize the current row
for (var c = 0; c < bNumCols; ++c) {
m[r][c] = 0; // initialize the current cell
for (var i = 0; i < aNumCols; ++i) {
m[r][c] += a[r][i] * b[i][c];
}
}
}
return m;
};
/**
* translate a point with the current matrix (if any).
*
* @param {number} x point
* @param {number} y point
* @returns {number} the translated x coordinate.
*/
exports._transX = function (x, y) {
if (_currentEnv._matrix) {
return x * _currentEnv._matrix[0][0] + y * _currentEnv._matrix[0][1] + _currentEnv._matrix[0][2];
} else {
return x;
}
}
/**
* translate a point with the current matrix (if any).
*
* @param {number} x point
* @param {number} y point
* @returns {number} the translated y coordinate.
*/
exports._transY = function (x, y) {
if (_currentEnv._matrix) {
return x * _currentEnv._matrix[1][0] + y * _currentEnv._matrix[1][1] + _currentEnv._matrix[1][2];
} else {
return y;
}
}
/**
* Multiplies the current matrix by the one specified through the parameters.
* This is a powerful operation that can perform the equivalent of translate,
* scale, shear and rotate all at once. You can learn more about transformation
* matrices on <a href="https://en.wikipedia.org/wiki/Transformation_matrix">
* Wikipedia.
*
* The naming of the arguments here follows the naming of the <a href=
* "https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-transform">
* WHATWG specification and corresponds to a
* transformation matrix of the
* form:
*
* > <img style="max-width: 150px" src="assets/transformation-matrix.png"
* alt="The transformation matrix used when applyMatrix is called"/>
*
* @method applyMatrix
* @param {Number} a numbers which define the 2x3 matrix to be multiplied
* @param {Number} b numbers which define the 2x3 matrix to be multiplied
* @param {Number} c numbers which define the 2x3 matrix to be multiplied
* @param {Number} d numbers which define the 2x3 matrix to be multiplied
* @param {Number} e numbers which define the 2x3 matrix to be multiplied
* @param {Number} f numbers which define the 2x3 matrix to be multiplied
* @example
* function setup() {
* frameRate(10);
* rectMode(CENTER);
* }
*
* function draw() {
* var step = frameCount % 20;
* background(200);
* // Equivalent to translate(x, y);
* applyMatrix(1, 0, 0, 1, 40 + step, 50);
* rect(0, 0, 50, 50);
* }
*
* function setup() {
* frameRate(10);
* rectMode(CENTER);
* }
*
* function draw() {
* var step = frameCount % 20;
* background(200);
* translate(50, 50);
* // Equivalent to scale(x, y);
* applyMatrix(1 / step, 0, 0, 1 / step, 0, 0);
* rect(0, 0, 50, 50);
* }
*
* function setup() {
* frameRate(10);
* rectMode(CENTER);
* }
*
* function draw() {
* var step = frameCount % 20;
* var angle = map(step, 0, 20, 0, TWO_PI);
* var cos_a = cos(angle);
* var sin_a = sin(angle);
* background(200);
* translate(50, 50);
* // Equivalent to rotate(angle);
* applyMatrix(cos_a, sin_a, -sin_a, cos_a, 0, 0);
* rect(0, 0, 50, 50);
* }
*
* function setup() {
* frameRate(10);
* rectMode(CENTER);
* }
*
* function draw() {
* var step = frameCount % 20;
* var angle = map(step, 0, 20, -PI / 4, PI / 4);
* background(200);
* translate(50, 50);
* // equivalent to shearX(angle);
* var shear_factor = 1 / tan(PI / 2 - angle);
* applyMatrix(1, 0, shear_factor, 1, 0, 0);
* rect(0, 0, 50, 50);
* }
*/
exports.applyMatrix = function (a, b, c, d, e, f) {
_ensureMatrix();
var pm = function (m) {
for (var r = 0; r < m.length; ++r) {
Println(' ' + m[r].join(' '));
}
}
var m1 = [
[a, c, e],
[b, d, f],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
/**
* Replaces the current matrix with the identity matrix.
*
* @method resetMatrix
* @example
* translate(50, 50);
* applyMatrix(0.5, 0.5, -0.5, 0.5, 0, 0);
* rect(0, 0, 20, 20);
* // Note that the translate is also reset.
* resetMatrix();
* rect(0, 0, 20, 20);
*/
exports.resetMatrix = function () {
_currentEnv._matrix = null;
};
/**
* Rotates a shape the amount specified by the angle parameter. This
* function accounts for angleMode, so angles can be entered in either
* RADIANS or DEGREES.
* <br><br>
* Objects are always rotated around their relative position to the
* origin and positive numbers rotate objects in a clockwise direction.
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI).
* All tranformations are reset when draw() begins again.
* <br><br>
* Technically, rotate() multiplies the current transformation matrix
* by a rotation matrix. This function can be further controlled by
* the push() and pop().
*
* @method rotate
* @param {Number} angle the angle of rotation, specified in radians
* or degrees, depending on current angleMode
* @example
* translate(width / 2, height / 2);
* rotate(PI / 3.0);
* rect(-26, -26, 52, 52);
*/
exports.rotate = function (angle) {
_ensureMatrix();
var cA = cos(angle);
var sA = sin(angle);
var m1 = [
[cA, -sA, 0],
[sA, cA, 0],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
/**
* Shears a shape around the x-axis the amount specified by the angle
* parameter. Angles should be specified in the current angleMode.
* Objects are always sheared around their relative position to the origin
* and positive numbers shear objects in a clockwise direction.
* <br><br>
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI).
* If shearX() is called within the draw(), the transformation is reset when
* the loop begins again.
* <br><br>
* Technically, shearX() multiplies the current transformation matrix by a
* rotation matrix. This function can be further controlled by the
* push() and pop() functions.
*
* @method shearX
* @param {Number} angle angle of shear specified in radians or degrees,
* depending on current angleMode
* @example
* translate(width / 4, height / 4);
* shearX(PI / 4.0);
* rect(0, 0, 30, 30);
*/
exports.shearX = function (angle) {
_ensureMatrix();
var m1 = [
[1, tan(angle), 0],
[0, 1, 0],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
/**
* Shears a shape around the y-axis the amount specified by the angle
* parameter. Angles should be specified in the current angleMode. Objects
* are always sheared around their relative position to the origin and
* positive numbers shear objects in a clockwise direction.
* <br><br>
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If
* shearY() is called within the draw(), the transformation is reset when
* the loop begins again.
* <br><br>
* Technically, shearY() multiplies the current transformation matrix by a
* rotation matrix. This function can be further controlled by the
* push() and pop() functions.
*
* @method shearY
* @param {Number} angle angle of shear specified in radians or degrees,
* depending on current angleMode
* @example
* translate(width / 4, height / 4);
* shearY(PI / 4.0);
* rect(0, 0, 30, 30);
*/
exports.shearY = function (angle) {
_ensureMatrix();
var m1 = [
[1, 0, 0],
[tan(angle), 1, 0],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
/**
* Specifies an amount to displace objects within the display window.
* The x parameter specifies left/right translation, the y parameter
* specifies up/down translation.
* <br><br>
* Transformations are cumulative and apply to everything that happens after
* and subsequent calls to the function accumulates the effect. For example,
* calling translate(50, 0) and then translate(20, 0) is the same as
* translate(70, 0). If translate() is called within draw(), the
* transformation is reset when the loop begins again. This function can be
* further controlled by using push() and pop().
*
* @method translate
* @param {Number} x left/right translation
* @param {Number} y up/down translation
* @param {Number} [z] forward/backward translation (webgl only)
* @example
* translate(30, 20);
* rect(0, 0, 55, 55);
*
* rect(0, 0, 55, 55); // Draw rect at original 0,0
* translate(30, 20);
* rect(0, 0, 55, 55); // Draw rect at new 0,0
* translate(14, 14);
* rect(0, 0, 55, 55); // Draw rect at new 0,0
*
* function draw() {
* background(200);
* rectMode(CENTER);
* translate(width / 2, height / 2);
* translate(p5.Vector.fromAngle(millis() / 1000, 40));
* rect(0, 0, 20, 20);
* }
*/
/**
* @method translate
* @param {p5.Vector} vector the vector to translate by
*/
exports.translate = function (x, y, z) {
_ensureMatrix();
if (x instanceof PVector) {
y = x.y;
x = x.x;
}
var m1 = [
[1, 0, x],
[0, 1, y],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
/**
* Increases or decreases the size of a shape by expanding and contracting
* vertices. Objects always scale from their relative origin to the
* coordinate system. Scale values are specified as decimal percentages.
* For example, the function call scale(2.0) increases the dimension of a
* shape by 200%.
* <br><br>
* Transformations apply to everything that happens after and subsequent
* calls to the function multiply the effect. For example, calling scale(2.0)
* and then scale(1.5) is the same as scale(3.0). If scale() is called
* within draw(), the transformation is reset when the loop begins again.
* <br><br>
* Using this function with the z parameter is only available in WEBGL mode.
* This function can be further controlled with push() and pop().
*
* @method scale
* @param {Number|p5.Vector|Number[]} s
* percent to scale the object, or percentage to
* scale the object in the x-axis if multiple arguments
* are given
* @param {Number} [y] percent to scale the object in the y-axis
* @example
* rect(30, 20, 50, 50);
* scale(0.5);
* rect(30, 20, 50, 50);
*
* rect(30, 20, 50, 50);
* scale(0.5, 1.3);
* rect(30, 20, 50, 50);
*/
/**
* @method scale
* @param {p5.Vector|Number[]} scales per-axis percents to scale the object
*/
exports.scale = function (x, y, z) {
_ensureMatrix();
// Only check for Vector argument type if Vector is available
if (x instanceof PVector) {
var v = x;
x = v.x;
y = v.y;
z = v.z;
} else if (x instanceof Array) {
var rg = x;
x = rg[0];
y = rg[1];
z = rg[2] || 1;
}
if (isNaN(y)) {
y = z = x;
} else if (isNaN(z)) {
z = 1;
}
var m1 = [
[x, 0, 0],
[0, y, 0],
[0, 0, 1]
];
_currentEnv._matrix = _MaMul(_currentEnv._matrix, m1);
};
Source