diff --git a/src/core/main.js b/src/core/main.js index 4ce9d91c55..969c3a2ba1 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -50,7 +50,7 @@ class p5 { constructor(sketch, node) { // Apply addon defined decorations if(p5.decorations.size > 0){ - decorateClass(p5, p5.decorations); + decorateClass(p5, p5.decorations, 'p5'); p5.decorations.clear(); } diff --git a/src/math/index.js b/src/math/index.js index 2acb397b21..8e08dd88bd 100644 --- a/src/math/index.js +++ b/src/math/index.js @@ -4,6 +4,7 @@ import random from './random.js'; import trigonometry from './trigonometry.js'; import math from './math.js'; import vector from './p5.Vector.js'; +import vectorValidation from './patch-vector.js'; export default function(p5){ p5.registerAddon(calculation); @@ -12,4 +13,5 @@ export default function(p5){ p5.registerAddon(trigonometry); p5.registerAddon(math); p5.registerAddon(vector); + p5.registerAddon(vectorValidation); } diff --git a/src/math/math.js b/src/math/math.js index 9e513a7c36..0cfbb593bb 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -38,7 +38,7 @@ function math(p5, fn) { * p5.Vector class. * * @method createVector - * @param {...Number} x Zero or more numbers, representing each component of the vector. + * @param {...Number} x List of numbers representing each component of the vector. * @return {p5.Vector} new p5.Vector object. * * @example @@ -92,21 +92,8 @@ function math(p5, fn) { * point(pos); * } */ - fn.createVector = function (x, y, z) { - if (arguments.length === 0) { - p5._friendlyError( - 'In 1.x, createVector() was a shortcut for createVector(0, 0, 0). In 2.x, p5.js has vectors of any dimension, so you must provide your desired number of zeros. Use createVector(0, 0) for a 2D vector and createVector(0, 0, 0) for a 3D vector.' - ); - } - if (this instanceof p5) { - return new p5.Vector( - this._fromRadians.bind(this), - this._toRadians.bind(this), - ...arguments - ); - } else { - return new p5.Vector(x, y, z); - } + fn.createVector = function (...args) { + return new p5.Vector(...args); }; /** diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index 1f3df37f8a..32cf84a98b 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -5,85 +5,81 @@ import * as constants from '../core/constants'; -/// HELPERS FOR REMAINDER METHOD -const calculateRemainder2D = function (xComponent, yComponent) { - if (xComponent !== 0) { - this.x = this.x % xComponent; - } - if (yComponent !== 0) { - this.y = this.y % yComponent; - } - return this; +/** + * This function is used by binary vector operations to prioritize shorter vectors, + * and to emit a warning when lengths do not match. + */ +const prioritizeSmallerDimension = function(currentVectorDimension, args) { + return Math.min(currentVectorDimension, args.length); + + //if (args.length !== currentVectorDimension && args.length !== 1) { + // TODO how to suppress for valid solo arguments? + // p5._friendlyError( + // `Operating on two vectors of different sizes, the smaller dimension is used. In this operation, both vector will be treated as ${minDimension}D vectors, and any additional values of the longer vector will be ignored.`, 'p5.Vector' + //); + //} + //return minDimension; }; -const calculateRemainder3D = function (xComponent, yComponent, zComponent) { - if (xComponent !== 0) { - this.x = this.x % xComponent; - } - if (yComponent !== 0) { - this.y = this.y % yComponent; - } - if (zComponent !== 0) { - this.z = this.z % zComponent; - } - return this; -}; class Vector { + /** + * The values of an N-dimensional vector. + * + * This array of numbers that represents the vector. + * Each number in the array corresponds to a different component of the vector, + * like its position in different directions (e.g., x, y, z). + * + * You can update the values of the entire vector to a new set of values. + * You need to provide an array of numbers, where each number represents a component + * of the vector (e.g., x, y, z). The length of the array will become the number of + * dimensions of the vector. + * + * You can add (`add()`), multiply (`mult()`), divide (`div()`), and subtract (`sub()`) + * vectors from each other, and calculate remainder (`rem()`). Only use these functions + * on vectors when they are the same size: for example, both 2D, or both 3D. + * When an operation uses two vectors of different sizes, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * + * You can multiply, divide, or calculate remainder of a vector with a single number. Then, + * the same operation will be done on each element of the vector. + * + * @type {Array} The array of values representing the vector. + * @throws Will throw an error if provided no arguments, or if the arguments + * are not all finity numbers + */ + values = []; + // This is how it comes in with createVector() // This check if the first argument is a function constructor(...args) { - let values = args; // .map(arg => arg || 0); + + if (args.length === 0) { + p5._friendlyError( + 'Requires valid arguments.', 'p5.Vector' + ); + } + if (typeof args[0] === 'function') { this.isPInst = true; this._fromRadians = args[0]; this._toRadians = args[1]; - values = args.slice(2); // .map(arg => arg || 0); + args = args.slice(2); } - let dimensions = values.length; // TODO: make default 3 if no arguments - if (dimensions === 0) { - this.dimensions = 2; - this._values = [0, 0, 0]; + + this.values = []; + if(Array.isArray(args) && !args.every(v => typeof v === 'number' && Number.isFinite(v))){ + p5._friendlyError( + 'Arguments contain non-finite numbers', + target.name + ); } else { - this.dimensions = dimensions; - this._values = values; + this.values = args; } } - /** - * Gets the values of the N-dimensional vector. - * - * This method returns an array of numbers that represent the vector. - * Each number in the array corresponds to a different component of the vector, - * like its position in different directions (e.g., x, y, z). - * - * @returns {Array} The array of values representing the vector. - */ - get values() { - return this._values; - } - - /** - * Sets the values of the vector. - * - * This method allows you to update the entire vector with a new set of values. - * You need to provide an array of numbers, where each number represents a component - * of the vector (e.g., x, y, z). The length of the array should match the number of - * dimensions of the vector. If the array is shorter, the missing components will be - * set to 0. If the array is longer, the extra values will be ignored. - * - * @param {Array} newValues - An array of numbers representing the new values for the vector. - * - */ - set values(newValues) { - let dimensions = newValues.length; - if (dimensions === 0) { - this.dimensions = 2; - this._values = [0, 0, 0]; - } else { - this.dimensions = dimensions; - this._values = newValues.slice(); - } + get dimensions(){ + return this.values.length; } /** @@ -96,7 +92,7 @@ class Vector { * @returns {Number} The x component of the vector. Returns 0 if the value is not defined. */ get x() { - return this._values[0] || 0; + return this.values[0] || 0; } /** @@ -117,8 +113,8 @@ class Vector { * get a value from a position that doesn't exist in the vector. */ getValue(index) { - if (index < this._values.length) { - return this._values[index]; + if (index < this.values.length) { + return this.values[index]; } else { p5._friendlyError( 'The index parameter is trying to set a value outside the bounds of the vector', @@ -142,8 +138,8 @@ class Vector { * @throws Will throw an error if the index is outside the bounds of the vector, meaning if you try to set a value at a position that doesn't exist in the vector. */ setValue(index, value) { - if (index < this._values.length) { - this._values[index] = value; + if (index < this.values.length) { + this.values[index] = value; } else { p5._friendlyError( 'The index parameter is trying to set a value outside the bounds of the vector', @@ -162,7 +158,7 @@ class Vector { * @returns {Number} The y component of the vector. Returns 0 if the value is not defined. */ get y() { - return this._values[1] || 0; + return this.values[1] || 0; } /** @@ -175,7 +171,7 @@ class Vector { * @returns {Number} The z component of the vector. Returns 0 if the value is not defined. */ get z() { - return this._values[2] || 0; + return this.values[2] || 0; } /** @@ -188,7 +184,7 @@ class Vector { * @returns {Number} The w component of the vector. Returns 0 if the value is not defined. */ get w() { - return this._values[3] || 0; + return this.values[3] || 0; } /** @@ -201,8 +197,8 @@ class Vector { * @param {Number} xVal - The new value for the x component. */ set x(xVal) { - if (this._values.length > 1) { - this._values[0] = xVal; + if (this.values.length > 1) { + this.values[0] = xVal; } } @@ -216,8 +212,8 @@ class Vector { * @param {Number} yVal - The new value for the y component. */ set y(yVal) { - if (this._values.length > 1) { - this._values[1] = yVal; + if (this.values.length > 1) { + this.values[1] = yVal; } } @@ -231,8 +227,8 @@ class Vector { * @param {Number} zVal - The new value for the z component. */ set z(zVal) { - if (this._values.length > 2) { - this._values[2] = zVal; + if (this.values.length > 2) { + this.values[2] = zVal; } } @@ -246,8 +242,8 @@ class Vector { * @param {Number} wVal - The new value for the w component. */ set w(wVal) { - if (this._values.length > 3) { - this._values[3] = wVal; + if (this.values.length > 3) { + this.values[3] = wVal; } } @@ -269,7 +265,7 @@ class Vector { * } */ toString() { - return `vector[${this._values.join(', ')}]`; + return `vector[${this.values.join(', ')}]`; } /** @@ -327,13 +323,12 @@ class Vector { */ set(...args) { if (args[0] instanceof Vector) { - this._values = args[0].values.slice(); + this.values = args[0].values.slice(); } else if (Array.isArray(args[0])) { - this._values = args[0].map(arg => arg || 0); + this.values = args[0].map(arg => arg || 0); } else { - this._values = args.map(arg => arg || 0); + this.values = args.map(arg => arg || 0); } - this.dimensions = this._values.length; return this; } @@ -363,12 +358,14 @@ class Vector { */ copy() { if (this.isPInst) { - return new Vector(this._fromRadians, this._toRadians, ...this._values); + return new Vector(this._fromRadians, this._toRadians, ...this.values); } else { - return new Vector(...this._values); + return new Vector(...this.values); } } + + /** * Adds to a vector's components. * @@ -376,8 +373,11 @@ class Vector { * another p5.Vector object, as in `v.add(v2)`, or * an array of numbers, as in `v.add([1, 2, 3])`. * - * If a value isn't provided for a component, it won't change. For - * example, `v.add(4, 5)` adds 4 to `v.x`, 5 to `v.y`, and 0 to `v.z`. + * Add vectors only when they are the same size: both 2D, or both 3D. + * When two vectors of different sizes are added, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * For example, adding `[1, 2, 3]` and `[4, 5]` will result in `[5, 7]`. + * * Calling `add()` with no arguments, as in `v.add()`, has no effect. * * This method supports N-dimensional vectors. @@ -494,17 +494,18 @@ class Vector { * @chainable */ add(...args) { - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } - args.forEach((value, index) => { - this._values[index] = (this._values[index] || 0) + (value || 0); - }); + const minDimension = prioritizeSmallerDimension(this.dimensions, args); + + this.values = this.values.reduce((acc, v, i) => { + if(i < minDimension) acc[i] = this.values[i] + Number(args[i]); + return acc; + }, new Array(minDimension)); + return this; } + + /** * Performs modulo (remainder) division with a vector's `x`, `y`, and `z` * components. @@ -514,10 +515,14 @@ class Vector { * an array of numbers, as in `v.rem([1, 2, 3])`. * * If only one value is provided, as in `v.rem(2)`, then all the components - * will be set to their values modulo 2. If two values are provided, as in - * `v.rem(2, 3)`, then `v.z` won't change. Calling `rem()` with no + * will be set to their values modulo 2. Calling `rem()` with no * arguments, as in `v.rem()`, has no effect. * + * Modulo vectors only when they are the same size: both 2D, or both 3D. + * When two vectors of different sizes are used, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * For example, taking `[3, 6, 9]` modulo `[2, 4]` will result in `[1, 2]`. + * * The static version of `rem()`, as in `p5.Vector.rem(v2, v1)`, returns a * new p5.Vector object and doesn't change the * originals. @@ -612,59 +617,14 @@ class Vector { * @param {p5.Vector | Number[]} value divisor vector. * @chainable */ - rem(x, y, z) { - if (x instanceof Vector) { - if ([x.x, x.y, x.z].every(Number.isFinite)) { - const xComponent = parseFloat(x.x); - const yComponent = parseFloat(x.y); - const zComponent = parseFloat(x.z); - return calculateRemainder3D.call( - this, - xComponent, - yComponent, - zComponent - ); - } - } else if (Array.isArray(x)) { - if (x.every(element => Number.isFinite(element))) { - if (x.length === 2) { - return calculateRemainder2D.call(this, x[0], x[1]); - } - if (x.length === 3) { - return calculateRemainder3D.call(this, x[0], x[1], x[2]); - } - } - } else if (arguments.length === 1) { - if (Number.isFinite(arguments[0]) && arguments[0] !== 0) { - this.x = this.x % arguments[0]; - this.y = this.y % arguments[0]; - this.z = this.z % arguments[0]; - return this; - } - } else if (arguments.length === 2) { - const vectorComponents = [...arguments]; - if (vectorComponents.every(element => Number.isFinite(element))) { - if (vectorComponents.length === 2) { - return calculateRemainder2D.call( - this, - vectorComponents[0], - vectorComponents[1] - ); - } - } - } else if (arguments.length === 3) { - const vectorComponents = [...arguments]; - if (vectorComponents.every(element => Number.isFinite(element))) { - if (vectorComponents.length === 3) { - return calculateRemainder3D.call( - this, - vectorComponents[0], - vectorComponents[1], - vectorComponents[2] - ); - } - } - } + rem(...args) { + const minDimension = prioritizeSmallerDimension(this.dimensions, args); + + this.values = Array.from({ length: minDimension }, (_, i) => { + return (args[i] > 0) ? this.values[i] % args[i] : this.values[i]; + }); + + return this; } /** @@ -674,10 +634,13 @@ class Vector { * p5.Vector object, as in `v.sub(v2)`, or an array * of numbers, as in `v.sub([1, 2, 3])`. * - * If a value isn't provided for a component, it won't change. For - * example, `v.sub(4, 5)` subtracts 4 from `v.x`, 5 from `v.y`, and 0 from `v.z`. * Calling `sub()` with no arguments, as in `v.sub()`, has no effect. * + * Subtract vectors only when they are the same size: both 2D, or both 3D. + * When two vectors of different sizes are used, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * For example, subtracting `[1, 2]` from `[3, 5, 7]` will result in `[2, 3]`. + * * The static version of `sub()`, as in `p5.Vector.sub(v2, v1)`, returns a new * p5.Vector object and doesn't change the * originals. @@ -787,19 +750,13 @@ class Vector { * @chainable */ sub(...args) { - if (args[0] instanceof Vector) { - args[0].values.forEach((value, index) => { - this._values[index] -= value || 0; - }); - } else if (Array.isArray(args[0])) { - args[0].forEach((value, index) => { - this._values[index] -= value || 0; - }); - } else { - args.forEach((value, index) => { - this._values[index] -= value || 0; - }); - } + const minDimension = prioritizeSmallerDimension(this.dimensions, args); + + this.values = this.values.reduce((acc, v, i) => { + if(i < minDimension) acc[i] = this.values[i] - args[i]; + return acc; + }, new Array(minDimension)); + return this; } @@ -811,11 +768,14 @@ class Vector { * of numbers, as in `v.mult([1, 2, 3])`. * * If only one value is provided, as in `v.mult(2)`, then all the components - * will be multiplied by 2. If a value isn't provided for a component, it - * won't change. For example, `v.mult(4, 5)` multiplies `v.x` by, `v.y` by 5, - * and `v.z` by 1. Calling `mult()` with no arguments, as in `v.mult()`, has + * will be multiplied by 2. Calling `mult()` with no arguments, as in `v.mult()`, has * no effect. * + * Multiply vectors only when they are the same size: both 2D, or both 3D. + * When two vectors of different sizes are multiplied, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * For example, multiplying `[1, 2, 3]` by `[4, 5]` will result in `[4, 10]`. + * * The static version of `mult()`, as in `p5.Vector.mult(v, 2)`, returns a new * p5.Vector object and doesn't change the * originals. @@ -978,43 +938,51 @@ class Vector { * @chainable */ mult(...args) { - if (args.length === 1 && args[0] instanceof Vector) { - const v = args[0]; - const maxLen = Math.min(this._values.length, v.values.length); - for (let i = 0; i < maxLen; i++) { - if (Number.isFinite(v.values[i]) && typeof v.values[i] === 'number') { - this._values[i] *= v.values[i]; - } else { - console.warn( - 'p5.Vector.prototype.mult:', - 'v contains components that are either undefined or not finite numbers' - ); - return this; - } - } - } else if (args.length === 1 && Array.isArray(args[0])) { - const arr = args[0]; - const maxLen = Math.min(this._values.length, arr.length); - for (let i = 0; i < maxLen; i++) { - if (Number.isFinite(arr[i]) && typeof arr[i] === 'number') { - this._values[i] *= arr[i]; - } else { - console.warn( - 'p5.Vector.prototype.mult:', - 'arr contains elements that are either undefined or not finite numbers' - ); - return this; - } - } - } else if ( - args.length === 1 && - typeof args[0] === 'number' && - Number.isFinite(args[0]) - ) { - for (let i = 0; i < this._values.length; i++) { - this._values[i] *= args[0]; - } - } + const minDimension = prioritizeSmallerDimension(this.dimensions, args); + + this.values = this.values.reduce((acc, v, i) => { + if(i < minDimension) acc[i] = this.values[i] * args[i]; + return acc; + }, new Array(minDimension)); + + // if (args.length === 1 && args[0] instanceof Vector) { + // const v = args[0]; + // const maxLen = Math.min(this.values.length, v.values.length); + // for (let i = 0; i < maxLen; i++) { + // if (Number.isFinite(v.values[i]) && typeof v.values[i] === 'number') { + // if(!this.values[i]) this.values[i] = 0; + // this.values[i] *= v.values[i]; + // } else { + // console.warn( + // 'p5.Vector.prototype.mult:', + // 'v contains components that are either undefined or not finite numbers' + // ); + // return this; + // } + // } + // } else if (args.length === 1 && Array.isArray(args[0])) { + // const arr = args[0]; + // const maxLen = Math.min(this.values.length, arr.length); + // for (let i = 0; i < maxLen; i++) { + // if (Number.isFinite(arr[i]) && typeof arr[i] === 'number') { + // this.values[i] *= arr[i]; + // } else { + // console.warn( + // 'p5.Vector.prototype.mult:', + // 'arr contains elements that are either undefined or not finite numbers' + // ); + // return this; + // } + // } + // } else if ( + // args.length === 1 && + // typeof args[0] === 'number' && + // Number.isFinite(args[0]) + // ) { + // for (let i = 0; i < this.values.length; i++) { + // this.values[i] *= args[0]; + // } + // } return this; } @@ -1026,11 +994,14 @@ class Vector { * of numbers, as in `v.div([1, 2, 3])`. * * If only one value is provided, as in `v.div(2)`, then all the components - * will be divided by 2. If a value isn't provided for a component, it - * won't change. For example, `v.div(4, 5)` divides `v.x` by, `v.y` by 5, - * and `v.z` by 1. Calling `div()` with no arguments, as in `v.div()`, has + * will be divided by 2. Calling `div()` with no arguments, as in `v.div()`, has * no effect. * + * Divide vectors only when they are the same size: both 2D, or both 3D. + * When two vectors of different sizes are divided, the smaller dimension will be + * used, any additional values of the longer vector will be ignored. + * For example, dividing `[8, 12, 21]` by `[2, 3]` will result in `[4, 4]`. + * * The static version of `div()`, as in `p5.Vector.div(v, 2)`, returns a new * p5.Vector object and doesn't change the * originals. @@ -1194,57 +1165,20 @@ class Vector { * @chainable */ div(...args) { - if (args.length === 0) return this; - if (args.length === 1 && args[0] instanceof Vector) { - const v = args[0]; - if ( - v._values.every( - val => Number.isFinite(val) && typeof val === 'number' - ) - ) { - if (v._values.some(val => val === 0)) { - console.warn('p5.Vector.prototype.div:', 'divide by 0'); - return this; - } - this._values = this._values.map((val, i) => val / v._values[i]); - } else { - console.warn( - 'p5.Vector.prototype.div:', - 'vector contains components that are either undefined or not finite numbers' - ); - } - return this; - } + const minDimension = prioritizeSmallerDimension(this.dimensions, args); - if (args.length === 1 && Array.isArray(args[0])) { - const arr = args[0]; - if (arr.every(val => Number.isFinite(val) && typeof val === 'number')) { - if (arr.some(val => val === 0)) { - console.warn('p5.Vector.prototype.div:', 'divide by 0'); - return this; - } - this._values = this._values.map((val, i) => val / arr[i]); - } else { - console.warn( - 'p5.Vector.prototype.div:', - 'array contains components that are either undefined or not finite numbers' - ); - } - return this; - } - - if (args.every(val => Number.isFinite(val) && typeof val === 'number')) { - if (args.some(val => val === 0)) { - console.warn('p5.Vector.prototype.div:', 'divide by 0'); - return this; - } - this._values = this._values.map((val, i) => val / args[0]); - } else { + if(!args.every(v => typeof v === 'number' && v !== 0)){ console.warn( - 'p5.Vector.prototype.div:', - 'arguments contain components that are either undefined or not finite numbers' + 'p5.Vector.prototype.div', + 'Arguments contain components that are 0' ); - } + return this; + }; + + this.values = this.values.reduce((acc, v, i) => { + if(i < minDimension) acc[i] = this.values[i] / args[i]; + return acc; + }, new Array(minDimension)); return this; } @@ -1297,7 +1231,8 @@ class Vector { * // Create a p5.Vector object. * let p = createVector(30, 40); * - * // Draw a line from the origin. + * // Draw a line from th + * e origin. * line(0, 0, p.x, p.y); * * // Style the text. @@ -1312,7 +1247,7 @@ class Vector { * } */ magSq() { - return this._values.reduce( + return this.values.reduce( (sum, component) => sum + component * component, 0 ); @@ -1416,9 +1351,9 @@ class Vector { */ dot(...args) { if (args[0] instanceof Vector) { - return this.dot(...args[0]._values); + return this.dot(...args[0].values); } - return this._values.reduce((sum, component, index) => { + return this.values.reduce((sum, component, index) => { return sum + component * (args[index] || 0); }, 0); } @@ -2823,15 +2758,15 @@ class Vector { equals(...args) { let values; if (args[0] instanceof Vector) { - values = args[0]._values; + values = args[0].values; } else if (Array.isArray(args[0])) { values = args[0]; } else { values = args; } - for (let i = 0; i < this._values.length; i++) { - if (this._values[i] !== (values[i] || 0)) { + for (let i = 0; i < this.values.length; i++) { + if (this.values[i] !== (values[i] || 0)) { return false; } } @@ -2851,8 +2786,8 @@ class Vector { * @chainable */ clampToZero() { - for (let i = 0; i < this._values.length; i++) { - this._values[i] = this._clampToZero(this._values[i]); + for (let i = 0; i < this.values.length; i++) { + this.values[i] = this._clampToZero(this.values[i]); } return this; } diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js new file mode 100644 index 0000000000..8216c822e0 --- /dev/null +++ b/src/math/patch-vector.js @@ -0,0 +1,73 @@ +import { Vector } from './p5.Vector.js'; + +/** + * @private + * @internal + */ +export function _defaultEmptyVector(target){ + return function(...args){ + if(args.length === 0){ + p5._friendlyError( + 'In 1.x, createVector() was a shortcut for createVector(0, 0, 0). In 2.x, p5.js has vectors of any dimension, so you must provide your desired number of zeros. Use createVector(0, 0) for a 2D vector and createVector(0, 0, 0) for a 3D vector.', + 'p5.createVector' + ); + return target.call(this, 0, 0, 0); + }else{ + if (Array.isArray(args[0])) { + args = args[0]; + } + return target.call(this, ...args); + } + }; +} + + +/** + * @private + * @internal + */ +export function _validatedVectorOperation(expectsSoloNumberArgument){ + return function(target){ + return function(...args){ + if (args.length === 0) { + // No arguments? No action + return this; + } else if (args[0] instanceof Vector) { + // First argument is a vector? Make it an array + args = args[0].values; + } else if (Array.isArray(args[0])) { + // First argument is an array? Great, keep it! + args = args[0]; + } else if (expectsSoloNumberArgument && args.length === 1){ + // Special case for a solo numeric arguments only applies sometimes + args = new Array(3).fill(args[0]); + } + + if(Array.isArray(args) && !args.every(v => typeof v === 'number' && Number.isFinite(v))){ + p5._friendlyError( + 'Arguments contain non-finite numbers', + target.name + ); + return this; + }; + + return target.call(this, ...args); + }; + }; +} + +/** + * Each of the following decorators validates the data on vector operations. + * These ensure that the arguments are consistently formatted, and that + * pre-conditions are met. + */ +export default function vectorValidation(p5, fn, lifecycles){ + + p5.registerDecorator('p5.prototype.createVector', _defaultEmptyVector); + p5.registerDecorator('p5.Vector.prototype.mult', _validatedVectorOperation(true)); + p5.registerDecorator('p5.Vector.prototype.rem', _validatedVectorOperation(true)); + p5.registerDecorator('p5.Vector.prototype.div', _validatedVectorOperation(true)); + p5.registerDecorator('p5.Vector.prototype.add', _validatedVectorOperation(false)); + p5.registerDecorator('p5.Vector.prototype.sub', _validatedVectorOperation(false)); + +} diff --git a/src/webgl/interaction.js b/src/webgl/interaction.js index ef5beb665a..dd09abed1e 100644 --- a/src/webgl/interaction.js +++ b/src/webgl/interaction.js @@ -370,8 +370,11 @@ function interaction(p5, fn){ // accelerate rotate velocity this._renderer.rotateVelocity.add( deltaTheta * rotateAccelerationFactor, - deltaPhi * rotateAccelerationFactor + deltaPhi * rotateAccelerationFactor, + 0 ); + + //console.log("added"); } if (this._renderer.rotateVelocity.magSq() > 0.000001) { // if freeRotation is true, the camera always rotates freely in the direction the pointer moves @@ -390,8 +393,10 @@ function interaction(p5, fn){ } // damping this._renderer.rotateVelocity.mult(damping); + //console.log("multiplied", damping, this._renderer.rotateVelocity); + } else { - this._renderer.rotateVelocity.set(0, 0); + this._renderer.rotateVelocity.set(0, 0, 0); } // move process diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 23fe61b123..9ddf9101c9 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -615,7 +615,7 @@ function loading(p5, fn){ model.uvs.push(loadedVerts.vt.at(vertParts[1]) ? loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]); model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ? - loadedVerts.vn.at(vertParts[2]).copy() : new Vector()); + loadedVerts.vn.at(vertParts[2]).copy() : new Vector(0, 0, 0)); usedVerts[vertString][currentMaterial] = vertIndex; face.push(vertIndex); diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js index 5dcca97a20..b4dbece928 100644 --- a/src/webgl/p5.Camera.js +++ b/src/webgl/p5.Camera.js @@ -1601,10 +1601,10 @@ class Camera { const up1 = rotMat1.row(1); // prepare new vectors. - const newFront = new Vector(); - const newUp = new Vector(); - const newEye = new Vector(); - const newCenter = new Vector(); + const newFront = new Vector(0, 0, 0); + const newUp = new Vector(0, 0, 0); + const newEye = new Vector(0, 0, 0); + const newCenter = new Vector(0, 0, 0); // Create the inverse matrix of mat0 by transposing mat0, // and multiply it to mat1 from the right. diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index dad4c860f4..5da0ddbb10 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1205,7 +1205,7 @@ class Geometry { // initialize the vertexNormals array with empty vectors vertexNormals.length = 0; for (iv = 0; iv < vertices.length; ++iv) { - vertexNormals.push(new Vector()); + vertexNormals.push(new Vector(0, 0, 0)); } // loop through all the faces adding its normal to the normal diff --git a/test/bench/vectors.bench.js b/test/bench/vectors.bench.js new file mode 100644 index 0000000000..952c428509 --- /dev/null +++ b/test/bench/vectors.bench.js @@ -0,0 +1,120 @@ +import { Vector } from '../../src/math/p5.Vector.js'; + +import { bench, describe } from "vitest"; + + +describe("vector operations", () => { + + bench( + "mult 5", + () => { + const nLimited = 5; + // TODO try just operating on det. values based on i and j + // without re-creation + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].mult(arr[j]); + } + } + } + ); + + bench( + "mult 10", + () => { + const nLimited = 10; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].mult(arr[j]); + } + } + } + ); + + bench( + "mult 20", + () => { + const nLimited = 20; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].mult(arr[j]); + } + } + } + ); + bench( + "mult 100", + () => { + const nLimited = 100; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].mult(arr[j]); + } + } + } + ); + + + + bench( + "add 5", + () => { + const nLimited = 5; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].add(arr[j]); + } + } + } + ); + + bench( + "add 10", + () => { + const nLimited = 10; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].add(arr[j]); + } + } + }); + + bench( + "add 20", + () => { + const nLimited = 20; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].add(arr[j]); + } + } + } + ); + bench( + "add 100", + () => { + const nLimited = 100; + const arr = setupVectors(); + + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].add(arr[j]); + } + } + }, + ); + +}); diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index 4028ee2dba..d0fe08bce5 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -1,24 +1,47 @@ -import vector from '../../../src/math/p5.Vector.js'; +import { Vector } from '../../../src/math/p5.Vector.js'; +import { default as math } from '../../../src/math/math.js'; +import { _defaultEmptyVector, _validatedVectorOperation } from '../../../src/math/patch-vector.js'; import { vi } from 'vitest'; + suite('p5.Vector', function () { var v; - const mockP5 = { - _validateParameters: vi.fn() - }; + const mockP5 = {}; const mockP5Prototype = {}; - beforeEach(async function () { - vector(mockP5, mockP5Prototype); + beforeAll(async function () { + // Makes createVector available + mockP5.Vector = Vector; + math(mockP5, mockP5Prototype); + + // Ensures all decorators are used by unit tests + mockP5Prototype.createVector = _defaultEmptyVector( + mockP5Prototype.createVector + ); + + // The following mocks simulate the validation decorator + Vector.prototype.add = _validatedVectorOperation(false)(Vector.prototype.add); + Vector.prototype.sub = _validatedVectorOperation(false)(Vector.prototype.sub); + Vector.prototype.mult = _validatedVectorOperation(true)(Vector.prototype.mult); + Vector.prototype.rem = _validatedVectorOperation(true)(Vector.prototype.rem); + Vector.prototype.div = _validatedVectorOperation(true)(Vector.prototype.div); + + globalThis.FESCalled = false; + globalThis.p5 = { + _friendlyError: function(msg, func) { + globalThis.FESCalled = true; + console.warn(msg); + } + }; }); afterEach(function () {}); suite.todo('p5.prototype.setHeading() RADIANS', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.RADIANS); - v = mockP5Prototype.createVector(1, 1); + mockP5Prototype.angleMode(RADIANS); + v = new Vector(1, 1); v.setHeading(1); }); test('should have heading() value of 1 (RADIANS)', function () { @@ -28,7 +51,7 @@ suite('p5.Vector', function () { suite.todo('p5.prototype.setHeading() DEGREES', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.DEGREES); + mockP5Prototype.angleMode(DEGREES); v = mockP5Prototype.createVector(1, 1); v.setHeading(1); }); @@ -44,7 +67,7 @@ suite('p5.Vector', function () { v = mockP5Prototype.createVector(); }); test('should create instance of p5.Vector', function () { - assert.instanceOf(v, mockP5.Vector); + assert.instanceOf(v, Vector); }); test('should have x, y, z be initialized to 0', function () { @@ -69,6 +92,32 @@ suite('p5.Vector', function () { assert.equal(v.z, 3); }); + test('should have values be initialized to 1,2,3', function () { + assert.deepEqual(v.values, [1, 2, 3]); + }); + + test('should have dimensions initialized to 3', function () { + assert.equal(v.dimensions, 3); + }); + }); + + + suite.todo('p5.prototype.createVector()', function () { + beforeEach(function () { + v = mockP5Prototype.createVector(); + }); + + test('should have x, y, z be initialized to 0,0,0', function () { + console.log(typeof mockP5Prototype.createVector); + assert.equal(v.x, 0); + assert.equal(v.y, 0); + assert.equal(v.z, 0); + }); + + test('should have values be initialized to 0,0,0', function () { + assert.deepEqual(v.values, [0,0,0]); + }); + test('should have dimensions initialized to 3', function () { assert.equal(v.dimensions, 3); }); @@ -76,10 +125,10 @@ suite('p5.Vector', function () { suite('new p5.Vector()', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new Vector(); }); test('should set constant to DEGREES', function () { - assert.instanceOf(v, mockP5.Vector); + assert.instanceOf(v, Vector); }); test('should have x, y, z be initialized to 0', function () { @@ -91,7 +140,7 @@ suite('p5.Vector', function () { suite('new p5.Vector(1, 2, 3)', function () { beforeEach(function () { - v = new mockP5.Vector(1, 2, 3); + v = new Vector(1, 2, 3); }); test('should have x, y, z be initialized to 1,2,3', function () { @@ -101,9 +150,9 @@ suite('p5.Vector', function () { }); }); - suite('new p5.Vector(1,2,undefined)', function () { + suite('new p5.Vector(1,2)', function () { beforeEach(function () { - v = new mockP5.Vector(1, 2, undefined); + v = new Vector(1, 2); }); test('should have x, y, z be initialized to 1,2,0', function () { @@ -116,13 +165,13 @@ suite('p5.Vector', function () { suite('rotate', function () { suite('p5.Vector.prototype.rotate() [INSTANCE]', function () { test('should return the same object', function () { - v = new mockP5.Vector(0, 1); + v = new Vector(0, 1); expect(v.rotate(Math.PI)).to.eql(v); }); suite.todo('radians', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.RADIANS); + mockP5Prototype.angleMode(RADIANS); }); test('should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]', function () { @@ -152,7 +201,7 @@ suite('p5.Vector', function () { suite.todo('degrees', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.DEGREES); + mockP5Prototype.angleMode(DEGREES); }); test('should rotate the vector [0, 1, 0] by 180 degrees to [0, -1, 0]', function () { @@ -175,12 +224,12 @@ suite('p5.Vector', function () { suite.todo('p5.Vector.rotate() [CLASS]', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.RADIANS); + mockP5Prototype.angleMode(RADIANS); }); test('should not change the original object', function () { v = mockP5Prototype.createVector(1, 0, 0); - mockP5.Vector.rotate(v, Math.PI / 2); + Vector.rotate(v, Math.PI / 2); expect(v.x).to.equal(1); expect(v.y).to.equal(0); expect(v.z).to.equal(0); @@ -188,7 +237,7 @@ suite('p5.Vector', function () { test('should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]', function () { v = mockP5Prototype.createVector(0, 1, 0); - const v1 = mockP5.Vector.rotate(v, Math.PI); + const v1 = Vector.rotate(v, Math.PI); expect(v1.x).to.be.closeTo(0, 0.01); expect(v1.y).to.be.closeTo(-1, 0.01); expect(v1.z).to.be.closeTo(0, 0.01); @@ -196,7 +245,7 @@ suite('p5.Vector', function () { test('should rotate the vector [1, 0, 0] by -pi/2 radians to [0, -1, 0]', function () { v = mockP5Prototype.createVector(1, 0, 0); - const v1 = mockP5.Vector.rotate(v, -Math.PI / 2); + const v1 = Vector.rotate(v, -Math.PI / 2); expect(v1.x).to.be.closeTo(0, 0.01); expect(v1.y).to.be.closeTo(-1, 0.01); expect(v1.z).to.be.closeTo(0, 0.01); @@ -207,8 +256,8 @@ suite('p5.Vector', function () { suite('angleBetween', function () { let v1, v2; beforeEach(function () { - v1 = new mockP5.Vector(1, 0, 0); - v2 = new mockP5.Vector(2, 2, 0); + v1 = new Vector(1, 0, 0); + v2 = new Vector(2, 2, 0); }); suite('p5.Vector.prototype.angleBetween() [INSTANCE]', function () { @@ -218,60 +267,60 @@ suite('p5.Vector', function () { }); test('should not trip on rounding issues in 2D space', function () { - v1 = new mockP5.Vector(-11, -20); - v2 = new mockP5.Vector(-5.5, -10); - const v3 = new mockP5.Vector(5.5, 10); + v1 = new Vector(-11, -20); + v2 = new Vector(-5.5, -10); + const v3 = new Vector(5.5, 10); expect(v1.angleBetween(v2)).to.be.closeTo(0, 0.00001); expect(v1.angleBetween(v3)).to.be.closeTo(Math.PI, 0.00001); }); test('should not trip on rounding issues in 3D space', function () { - v1 = new mockP5.Vector(1, 1.1, 1.2); - v2 = new mockP5.Vector(2, 2.2, 2.4); + v1 = new Vector(1, 1.1, 1.2); + v2 = new Vector(2, 2.2, 2.4); expect(v1.angleBetween(v2)).to.be.closeTo(0, 0.00001); }); test('should return NaN for zero vector', function () { - v1 = new mockP5.Vector(0, 0, 0); - v2 = new mockP5.Vector(2, 3, 4); + v1 = new Vector(0, 0, 0); + v2 = new Vector(2, 3, 4); expect(v1.angleBetween(v2)).to.be.NaN; expect(v2.angleBetween(v1)).to.be.NaN; }); test.todo('between [1,0,0] and [1,0,0] should be 0 degrees', function () { - mockP5Prototype.angleMode(mockP5.DEGREES); - v1 = new mockP5.Vector(1, 0, 0); - v2 = new mockP5.Vector(1, 0, 0); + mockP5Prototype.angleMode(DEGREES); + v1 = new Vector(1, 0, 0); + v2 = new Vector(1, 0, 0); expect(v1.angleBetween(v2)).to.equal(0); }); test.todo( 'between [0,3,0] and [0,-3,0] should be 180 degrees', function () { - mockP5Prototype.angleMode(mockP5.DEGREES); - v1 = new mockP5.Vector(0, 3, 0); - v2 = new mockP5.Vector(0, -3, 0); + mockP5Prototype.angleMode(DEGREES); + v1 = new Vector(0, 3, 0); + v2 = new Vector(0, -3, 0); expect(v1.angleBetween(v2)).to.be.closeTo(180, 0.01); } ); test('between [1,0,0] and [2,2,0] should be 1/4 PI radians', function () { - v1 = new mockP5.Vector(1, 0, 0); - v2 = new mockP5.Vector(2, 2, 0); + v1 = new Vector(1, 0, 0); + v2 = new Vector(2, 2, 0); expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI / 4, 0.01); expect(v2.angleBetween(v1)).to.be.closeTo((-1 * Math.PI) / 4, 0.01); }); test('between [2,0,0] and [-2,0,0] should be PI radians', function () { - v1 = new mockP5.Vector(2, 0, 0); - v2 = new mockP5.Vector(-2, 0, 0); + v1 = new Vector(2, 0, 0); + v2 = new Vector(-2, 0, 0); expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI, 0.01); }); test('between [2,0,0] and [-2,-2,0] should be -3/4 PI radians ', function () { - v1 = new mockP5.Vector(2, 0, 0); - v2 = new mockP5.Vector(-2, -2, 0); + v1 = new Vector(2, 0, 0); + v2 = new Vector(-2, -2, 0); expect(v1.angleBetween(v2)).to.be.closeTo( -1 * (Math.PI / 2 + Math.PI / 4), 0.01 @@ -279,8 +328,8 @@ suite('p5.Vector', function () { }); test('between [-2,-2,0] and [2,0,0] should be 3/4 PI radians', function () { - v1 = new mockP5.Vector(-2, -2, 0); - v2 = new mockP5.Vector(2, 0, 0); + v1 = new Vector(-2, -2, 0); + v2 = new Vector(2, 0, 0); expect(v1.angleBetween(v2)).to.be.closeTo( Math.PI / 2 + Math.PI / 4, 0.01 @@ -288,40 +337,40 @@ suite('p5.Vector', function () { }); test('For the same vectors, the angle between them should always be 0.', function () { - v1 = new mockP5.Vector(288, 814); - v2 = new mockP5.Vector(288, 814); + v1 = new Vector(288, 814); + v2 = new Vector(288, 814); expect(v1.angleBetween(v2)).to.equal(0); }); test('The angle between vectors pointing in opposite is always PI.', function () { - v1 = new mockP5.Vector(219, 560); - v2 = new mockP5.Vector(-219, -560); + v1 = new Vector(219, 560); + v2 = new Vector(-219, -560); expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI, 0.0000001); }); }); suite('p5.Vector.angleBetween() [CLASS]', function () { test('should return NaN for zero vector', function () { - v1 = new mockP5.Vector(0, 0, 0); - v2 = new mockP5.Vector(2, 3, 4); - expect(mockP5.Vector.angleBetween(v1, v2)).to.be.NaN; - expect(mockP5.Vector.angleBetween(v2, v1)).to.be.NaN; + v1 = new Vector(0, 0, 0); + v2 = new Vector(2, 3, 4); + expect(Vector.angleBetween(v1, v2)).to.be.NaN; + expect(Vector.angleBetween(v2, v1)).to.be.NaN; }); test.todo( 'between [1,0,0] and [0,-1,0] should be -90 degrees', function () { - mockP5Prototype.angleMode(mockP5.DEGREES); - v1 = new mockP5.Vector(1, 0, 0); - v2 = new mockP5.Vector(0, -1, 0); - expect(mockP5.Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01); + mockP5Prototype.angleMode(DEGREES); + v1 = new Vector(1, 0, 0); + v2 = new Vector(0, -1, 0); + expect(Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01); } ); test('between [0,3,0] and [0,-3,0] should be PI radians', function () { - v1 = new mockP5.Vector(0, 3, 0); - v2 = new mockP5.Vector(0, -3, 0); - expect(mockP5.Vector.angleBetween(v1, v2)).to.be.closeTo(Math.PI, 0.01); + v1 = new Vector(0, 3, 0); + v2 = new Vector(0, -3, 0); + expect(Vector.angleBetween(v1, v2)).to.be.closeTo(Math.PI, 0.01); }); }); }); @@ -329,7 +378,7 @@ suite('p5.Vector', function () { suite('set()', function () { suite('with p5.Vector', function () { test("should have x, y, z be initialized to the vector's x, y, z", function () { - v.set(new mockP5.Vector(2, 5, 6)); + v.set(new Vector(2, 5, 6)); expect(v.x).to.eql(2); expect(v.y).to.eql(5); expect(v.z).to.eql(6); @@ -364,7 +413,7 @@ suite('p5.Vector', function () { suite('copy', function () { beforeEach(function () { - v = new mockP5.Vector(2, 3, 4); + v = new Vector(2, 3, 4); }); suite('p5.Vector.prototype.copy() [INSTANCE]', function () { @@ -383,12 +432,12 @@ suite('p5.Vector', function () { suite('p5.Vector.copy() [CLASS]', function () { test('should not return the same instance', function () { - var newObject = mockP5.Vector.copy(v); + var newObject = Vector.copy(v); expect(newObject).to.not.equal(v); }); test("should return the passed object's x, y, z", function () { - var newObject = mockP5.Vector.copy(v); + var newObject = Vector.copy(v); expect(newObject.x).to.eql(2); expect(newObject.y).to.eql(3); expect(newObject.z).to.eql(4); @@ -398,12 +447,12 @@ suite('p5.Vector', function () { suite('add()', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new Vector(0, 0, 0); }); suite('with p5.Vector', function () { test('should add x, y, z from the vector argument', function () { - v.add(new mockP5.Vector(1, 5, 6)); + v.add(new Vector(1, 5, 6)); expect(v.x).to.eql(1); expect(v.y).to.eql(5); expect(v.z).to.eql(6); @@ -457,9 +506,9 @@ suite('p5.Vector', function () { suite('p5.Vector.add(v1, v2)', function () { var v1, v2, res; beforeEach(function () { - v1 = new mockP5.Vector(2, 0, 3); - v2 = new mockP5.Vector(0, 1, 3); - res = mockP5.Vector.add(v1, v2); + v1 = new Vector(2, 0, 3); + v2 = new Vector(0, 1, 3); + res = Vector.add(v1, v2); }); test('should return neither v1 nor v2', function () { @@ -477,7 +526,7 @@ suite('p5.Vector', function () { suite('rem()', function () { beforeEach(function () { - v = new mockP5.Vector(3, 4, 5); + v = new Vector(3, 4, 5); }); test('should give same vector if nothing passed as parameter', function () { @@ -495,10 +544,12 @@ suite('p5.Vector', function () { }); test('should give correct output if passed two numeric value', function () { + expect(v.dimensions).to.eql(3); v.rem(2, 3); expect(v.x).to.eql(1); expect(v.y).to.eql(1); - expect(v.z).to.eql(5); + expect(v.z).to.eql(0); + expect(v.dimensions).to.eql(2); }); test('should give correct output if passed three numeric value', function () { @@ -510,28 +561,28 @@ suite('p5.Vector', function () { suite('with p5.Vector', function () { test('should return correct output if only one component is non-zero', function () { - v.rem(new mockP5.Vector(0, 0, 4)); + v.rem(new Vector(0, 0, 4)); expect(v.x).to.eql(3); expect(v.y).to.eql(4); expect(v.z).to.eql(1); }); test('should return correct output if x component is zero', () => { - v.rem(new mockP5.Vector(0, 3, 4)); + v.rem(new Vector(0, 3, 4)); expect(v.x).to.eql(3); expect(v.y).to.eql(1); expect(v.z).to.eql(1); }); test('should return correct output if all components are non-zero', () => { - v.rem(new mockP5.Vector(2, 3, 4)); + v.rem(new Vector(2, 3, 4)); expect(v.x).to.eql(1); expect(v.y).to.eql(1); expect(v.z).to.eql(1); }); test('should return same vector if all components are zero', () => { - v.rem(new mockP5.Vector(0, 0, 0)); + v.rem(new Vector(0, 0, 0)); expect(v.x).to.eql(3); expect(v.y).to.eql(4); expect(v.z).to.eql(5); @@ -541,10 +592,10 @@ suite('p5.Vector', function () { suite('with negative vectors', function () { let v; beforeEach(function () { - v = new mockP5.Vector(-15, -5, -2); + v = new Vector(-15, -5, -2); }); test('should return correct output', () => { - v.rem(new mockP5.Vector(2, 3, 3)); + v.rem(new Vector(2, 3, 3)); expect(v.x).to.eql(-1); expect(v.y).to.eql(-2); expect(v.z).to.eql(-2); @@ -562,14 +613,16 @@ suite('p5.Vector', function () { v.rem([2, 3]); expect(v.x).to.eql(1); expect(v.y).to.eql(1); - expect(v.z).to.eql(5); + expect(v.z).to.eql(0); + expect(v.dimensions).to.eql(2); }); test('should return correct output if x,y components are zero for 2D vector', () => { v.rem([0, 0]); expect(v.x).to.eql(3); expect(v.y).to.eql(4); - expect(v.z).to.eql(5); + expect(v.z).to.eql(0); + expect(v.dimensions).to.eql(2); }); test('should return same vector if any vector component is non-finite number', () => { @@ -583,9 +636,9 @@ suite('p5.Vector', function () { suite('p5.Vector.rem(v1,v2)', function () { let v1, v2, res; beforeEach(function () { - v1 = new mockP5.Vector(2, 3, 4); - v2 = new mockP5.Vector(1, 2, 3); - res = mockP5.Vector.rem(v1, v2); + v1 = new Vector(2, 3, 4); + v2 = new Vector(1, 2, 3); + res = Vector.rem(v1, v2); }); test('should return neither v1 nor v2', function () { @@ -605,14 +658,11 @@ suite('p5.Vector', function () { beforeEach(function () { v.x = 0; v.y = 0; - v.z = 0; }); suite('with p5.Vector', function () { test('should sub x, y, z from the vector argument', function () { - v.sub(new mockP5.Vector(2, 5, 6)); - expect(v.x).to.eql(-2); - expect(v.y).to.eql(-5); - expect(v.z).to.eql(-6); + v.sub(new Vector(2, 5)); + assert.deepEqual(v.values, [-2, -5]); }); }); @@ -626,11 +676,9 @@ suite('p5.Vector', function () { }); }); - test("should subtract from the array's 0,1,2 index", function () { + test("should subtract from the array's 0, 1, 2 index", function () { v.sub([2, 5, 6]); - expect(v.x).to.eql(-2); - expect(v.y).to.eql(-5); - expect(v.z).to.eql(-6); + expect(v.values).to.eql([-2, -5]); }); }); @@ -645,19 +693,19 @@ suite('p5.Vector', function () { suite('sub(2,3,4)', function () { test('should subtract the x, y, z components', function () { - v.sub(5, 5, 5); + v.sub(5, 5); expect(v.x).to.eql(-5); expect(v.y).to.eql(-5); - expect(v.z).to.eql(-5); + //expect(v.z).to.eql(-5); }); }); suite('p5.Vector.sub(v1, v2)', function () { var v1, v2, res; beforeEach(function () { - v1 = new mockP5.Vector(2, 0, 3); - v2 = new mockP5.Vector(0, 1, 3); - res = mockP5.Vector.sub(v1, v2); + v1 = new Vector(2, 0, 3); + v2 = new Vector(0, 1, 3); + res = Vector.sub(v1, v2); }); test('should return neither v1 nor v2', function () { @@ -675,7 +723,7 @@ suite('p5.Vector', function () { suite('mult()', function () { beforeEach(function () { - v = new mockP5.Vector(1, 1, 1); + v = new Vector(1, 1, 1); }); test('should return the same object', function () { @@ -705,11 +753,20 @@ suite('p5.Vector', function () { }); }); + suite('with arglist', function () { + test('multiply the x, y, z with the scalar', function () { + v.mult(2, 3, 4); + expect(v.x).to.eql(2); + expect(v.y).to.eql(3); + expect(v.z).to.eql(4); + }); + }); + suite('v0.mult(v1)', function () { var v0, v1; beforeEach(function () { - v0 = new mockP5.Vector(1, 2, 3); - v1 = new mockP5.Vector(2, 3, 4); + v0 = new Vector(1, 2, 3); + v1 = new Vector(2, 3, 4); v0.mult(v1); }); @@ -723,7 +780,7 @@ suite('p5.Vector', function () { suite('v0.mult(arr)', function () { var v0, arr; beforeEach(function () { - v0 = new mockP5.Vector(1, 2, 3); + v0 = new Vector(1, 2, 3); arr = [2, 3, 4]; v0.mult(arr); }); @@ -738,8 +795,8 @@ suite('p5.Vector', function () { suite('p5.Vector.mult(v, n)', function () { var v, res; beforeEach(function () { - v = new mockP5.Vector(1, 2, 3); - res = mockP5.Vector.mult(v, 4); + v = new Vector(1, 2, 3); + res = Vector.mult(v, 4); }); test('should return a new p5.Vector', function () { @@ -756,9 +813,9 @@ suite('p5.Vector', function () { suite('p5.Vector.mult(v, v', function () { var v0, v1, res; beforeEach(function () { - v0 = new mockP5.Vector(1, 2, 3); - v1 = new mockP5.Vector(2, 3, 4); - res = mockP5.Vector.mult(v0, v1); + v0 = new Vector(1, 2, 3); + v1 = new Vector(2, 3, 4); + res = Vector.mult(v0, v1); }); test('should return new vector from component wise multiplication', function () { @@ -768,12 +825,12 @@ suite('p5.Vector', function () { }); }); - suite('p5.Vector.mult(v, arr', function () { + suite('p5.Vector.mult(v, arr)', function () { var v0, arr, res; beforeEach(function () { - v0 = new mockP5.Vector(1, 2, 3); + v0 = new Vector(1, 2, 3); arr = [2, 3, 4]; - res = mockP5.Vector.mult(v0, arr); + res = Vector.mult(v0, arr); }); test('should return new vector from component wise multiplication with an array', function () { @@ -786,7 +843,7 @@ suite('p5.Vector', function () { suite('div()', function () { beforeEach(function () { - v = new mockP5.Vector(1, 1, 1); + v = new Vector(1, 1, 1); }); test('should return the same object', function () { @@ -823,11 +880,20 @@ suite('p5.Vector', function () { }); }); + suite('with arglist', function () { + test('divide the x, y, z with the scalar', function () { + v.div(2, 3, 4); + expect(v.x).to.be.closeTo(0.5, 0.01); + expect(v.y).to.be.closeTo(0.333, 0.01); + expect(v.z).to.be.closeTo(0.25, 0.01); + }); + }); + suite('p5.Vector.div(v, n)', function () { var v, res; beforeEach(function () { - v = new mockP5.Vector(1, 1, 1); - res = mockP5.Vector.div(v, 4); + v = new Vector(1, 1, 1); + res = Vector.div(v, 4); }); test('should not be undefined', function () { @@ -848,10 +914,10 @@ suite('p5.Vector', function () { suite('v0.div(v1)', function () { var v0, v1, v2, v3; beforeEach(function () { - v0 = new mockP5.Vector(2, 6, 9); - v1 = new mockP5.Vector(2, 2, 3); - v2 = new mockP5.Vector(1, 1, 1); - v3 = new mockP5.Vector(0, 0, 0); + v0 = new Vector(2, 6, 9); + v1 = new Vector(2, 2, 3); + v2 = new Vector(1, 1, 1); + v3 = new Vector(0, 0, 0); v0.div(v1); }); @@ -870,8 +936,8 @@ suite('p5.Vector', function () { }); test('should work on 2D vectors', function () { - const v = new mockP5.Vector(1, 1); - const divisor = new mockP5.Vector(2, 2); + const v = new Vector(1, 1); + const divisor = new Vector(2, 2); v.div(divisor); expect(v.x).to.eql(0.5); expect(v.y).to.eql(0.5); @@ -879,8 +945,8 @@ suite('p5.Vector', function () { }); test('should work when the dividend has 0', function () { - const v = new mockP5.Vector(1, 0); - const divisor = new mockP5.Vector(2, 2); + const v = new Vector(1, 0); + const divisor = new Vector(2, 2); v.div(divisor); expect(v.x).to.eql(0.5); expect(v.y).to.eql(0); @@ -888,8 +954,8 @@ suite('p5.Vector', function () { }); test('should do nothing when the divisor has 0', function () { - const v = new mockP5.Vector(1, 1); - const divisor = new mockP5.Vector(0, 2); + const v = new Vector(1, 1); + const divisor = new Vector(0, 2); v.div(divisor); expect(v.x).to.eql(1); expect(v.y).to.eql(1); @@ -900,8 +966,8 @@ suite('p5.Vector', function () { suite('v0.div(arr)', function () { var v0, v1, arr; beforeEach(function () { - v0 = new mockP5.Vector(2, 6, 9); - v1 = new mockP5.Vector(1, 1, 1); + v0 = new Vector(2, 6, 9); + v1 = new Vector(1, 1, 1); arr = [2, 2, 3]; v0.div(arr); }); @@ -923,9 +989,9 @@ suite('p5.Vector', function () { suite('p5.Vector.div(v, v', function () { var v0, v1, res; beforeEach(function () { - v0 = new mockP5.Vector(2, 6, 9); - v1 = new mockP5.Vector(2, 2, 3); - res = mockP5.Vector.div(v0, v1); + v0 = new Vector(2, 6, 9); + v1 = new Vector(2, 2, 3); + res = Vector.div(v0, v1); }); test('should return new vector from component wise division', function () { @@ -938,9 +1004,9 @@ suite('p5.Vector', function () { suite('p5.Vector.div(v, arr', function () { var v0, arr, res; beforeEach(function () { - v0 = new mockP5.Vector(2, 6, 9); + v0 = new Vector(2, 6, 9); arr = [2, 2, 3]; - res = mockP5.Vector.div(v0, arr); + res = Vector.div(v0, arr); }); test('should return new vector from component wise division with an array', function () { @@ -951,6 +1017,56 @@ suite('p5.Vector', function () { }); }); + + suite('smaller dimension', function () { + let v1, v2, v3; + beforeEach(function () { + v1 = new Vector(1); + v2 = new Vector(2, 3); + v3 = new Vector(4, 5, 6); + }); + + test('should be prioritized in add()', function () { + assert.deepEqual(v1.add(v2).values, [3]); + expect(v1.add(v2).dimensions).to.eql(1); + + assert.deepEqual(v3.add(v2).values, [6, 8]); + expect(v3.add(v2).dimensions).to.eql(2); + }); + + test('should be prioritized in sub()', function () { + assert.deepEqual(v1.sub(v2).values, [-1]); + expect(v1.sub(v2).dimensions).to.eql(1); + + assert.deepEqual(v3.sub(v2).values, [2, 2]); + expect(v3.sub(v2).dimensions).to.eql(2); + }); + + test('should be prioritized in mult()', function () { + assert.deepEqual(v1.mult(v2).values, [2]); + expect(v1.mult(v2).dimensions).to.eql(1); + + assert.deepEqual(v3.mult(v2).values, [8, 15]); + expect(v3.mult(v2).dimensions).to.eql(2); + }); + + test('should be prioritized in div()', function () { + assert.deepEqual(v1.div(v2).values, [1/2]); + expect(v1.div(v2).dimensions).to.eql(1); + + assert.deepEqual(v3.div(v2).values, [2, 5/3]); + expect(v3.div(v2).dimensions).to.eql(2); + }); + + test('should be prioritized in rem()', function () { + assert.deepEqual(v1.rem(v2).values, [1]); + expect(v1.rem(v2).dimensions).to.eql(1); + + assert.deepEqual(v3.rem(v2).values, [0, 2]); + expect(v3.rem(v2).dimensions).to.eql(2); + }); + }); + suite('dot', function () { beforeEach(function () { v.x = 1; @@ -959,12 +1075,12 @@ suite('p5.Vector', function () { }); test('should return a number', function () { - expect(typeof v.dot(new mockP5.Vector()) === 'number').to.eql(true); + expect(typeof v.dot(new Vector()) === 'number').to.eql(true); }); suite('with p5.Vector', function () { test('should be the dot product of the vector', function () { - expect(v.dot(new mockP5.Vector(2, 2))).to.eql(4); + expect(v.dot(new Vector(2, 2))).to.eql(4); }); }); @@ -981,9 +1097,9 @@ suite('p5.Vector', function () { suite('p5.Vector.dot(v, n)', function () { var v1, v2, res; beforeEach(function () { - v1 = new mockP5.Vector(1, 1, 1); - v2 = new mockP5.Vector(2, 3, 4); - res = mockP5.Vector.dot(v1, v2); + v1 = new Vector(1, 1, 1); + v2 = new Vector(2, 3, 4); + res = Vector.dot(v1, v2); }); test('should return a number', function () { @@ -1005,12 +1121,12 @@ suite('p5.Vector', function () { }); test('should return a new product', function () { - expect(v.cross(new mockP5.Vector())).to.not.eql(v); + expect(v.cross(new Vector())).to.not.eql(v); }); suite('with p5.Vector', function () { test('should cross x, y, z from the vector argument', function () { - res = v.cross(new mockP5.Vector(2, 5, 6)); + res = v.cross(new Vector(2, 5, 6)); expect(res.x).to.eql(1); //this.y * v.z - this.z * v.y expect(res.y).to.eql(-4); //this.z * v.x - this.x * v.z expect(res.z).to.eql(3); //this.x * v.y - this.y * v.x @@ -1020,9 +1136,9 @@ suite('p5.Vector', function () { suite('p5.Vector.cross(v1, v2)', function () { var v1, v2, res; beforeEach(function () { - v1 = new mockP5.Vector(3, 6, 9); - v2 = new mockP5.Vector(1, 1, 1); - res = mockP5.Vector.cross(v1, v2); + v1 = new Vector(3, 6, 9); + v2 = new Vector(1, 1, 1); + res = Vector.cross(v1, v2); }); test('should not be undefined', function () { @@ -1045,9 +1161,9 @@ suite('p5.Vector', function () { suite('dist', function () { var b, c; beforeEach(function () { - v = new mockP5.Vector(0, 0, 1); - b = new mockP5.Vector(0, 0, 5); - c = new mockP5.Vector(3, 4, 1); + v = new Vector(0, 0, 1); + b = new Vector(0, 0, 5); + c = new Vector(3, 4, 1); }); test('should return a number', function () { @@ -1070,23 +1186,23 @@ suite('p5.Vector', function () { suite('p5.Vector.dist(v1, v2)', function () { var v1, v2; beforeEach(function () { - v1 = new mockP5.Vector(0, 0, 0); - v2 = new mockP5.Vector(0, 3, 4); + v1 = new Vector(0, 0, 0); + v2 = new Vector(0, 3, 4); }); test('should return a number', function () { - expect(typeof mockP5.Vector.dist(v1, v2) === 'number').to.eql(true); + expect(typeof Vector.dist(v1, v2) === 'number').to.eql(true); }); test('should be commutative', function () { - expect(mockP5.Vector.dist(v1, v2)).to.eql(mockP5.Vector.dist(v2, v1)); + expect(Vector.dist(v1, v2)).to.eql(Vector.dist(v2, v1)); }); }); suite('normalize', function () { suite('p5.Vector.prototype.normalize() [INSTANCE]', function () { beforeEach(function () { - v = new mockP5.Vector(1, 1, 1); + v = new Vector(1, 1, 1); }); test('should return the same object', function () { @@ -1117,8 +1233,8 @@ suite('p5.Vector', function () { suite('p5.Vector.normalize(v) [CLASS]', function () { var res; beforeEach(function () { - v = new mockP5.Vector(1, 0, 0); - res = mockP5.Vector.normalize(v); + v = new Vector(1, 0, 0); + res = Vector.normalize(v); }); test('should not be undefined', function () { @@ -1139,7 +1255,7 @@ suite('p5.Vector', function () { v.x = 2; v.y = 2; v.z = 1; - res = mockP5.Vector.normalize(v); + res = Vector.normalize(v); expect(res.x).to.be.closeTo(0.6666, 0.01); expect(res.y).to.be.closeTo(0.6666, 0.01); expect(res.z).to.be.closeTo(0.3333, 0.01); @@ -1151,7 +1267,7 @@ suite('p5.Vector', function () { let v; beforeEach(function () { - v = new mockP5.Vector(5, 5, 5); + v = new Vector(5, 5, 5); }); suite('p5.Vector.prototype.limit() [INSTANCE]', function () { @@ -1180,12 +1296,12 @@ suite('p5.Vector', function () { suite('p5.Vector.limit() [CLASS]', function () { test('should not return the same object', function () { - expect(mockP5.Vector.limit(v)).to.not.equal(v); + expect(Vector.limit(v)).to.not.equal(v); }); suite('with a vector larger than the limit', function () { test('should limit the vector', function () { - const res = mockP5.Vector.limit(v, 1); + const res = Vector.limit(v, 1); expect(res.x).to.be.closeTo(0.5773, 0.01); expect(res.y).to.be.closeTo(0.5773, 0.01); expect(res.z).to.be.closeTo(0.5773, 0.01); @@ -1194,7 +1310,7 @@ suite('p5.Vector', function () { suite('with a vector smaller than the limit', function () { test('should not limit the vector', function () { - const res = mockP5.Vector.limit(v, 8.67); + const res = Vector.limit(v, 8.67); expect(res.x).to.eql(5); expect(res.y).to.eql(5); expect(res.z).to.eql(5); @@ -1203,8 +1319,8 @@ suite('p5.Vector', function () { suite('when given a target vector', function () { test('should store limited vector in the target', function () { - const target = new mockP5.Vector(0, 0, 0); - mockP5.Vector.limit(v, 1, target); + const target = new Vector(0, 0, 0); + Vector.limit(v, 1, target); expect(target.x).to.be.closeTo(0.5773, 0.01); expect(target.y).to.be.closeTo(0.5773, 0.01); expect(target.z).to.be.closeTo(0.5773, 0.01); @@ -1217,7 +1333,7 @@ suite('p5.Vector', function () { let v; beforeEach(function () { - v = new mockP5.Vector(1, 0, 0); + v = new Vector(1, 0, 0); }); suite('p5.Vector.setMag() [INSTANCE]', function () { @@ -1245,11 +1361,11 @@ suite('p5.Vector', function () { suite('p5.Vector.prototype.setMag() [CLASS]', function () { test('should not return the same object', function () { - expect(mockP5.Vector.setMag(v, 2)).to.not.equal(v); + expect(Vector.setMag(v, 2)).to.not.equal(v); }); test('should set the magnitude of the vector', function () { - const res = mockP5.Vector.setMag(v, 4); + const res = Vector.setMag(v, 4); expect(res.x).to.eql(4); expect(res.y).to.eql(0); expect(res.z).to.eql(0); @@ -1259,7 +1375,7 @@ suite('p5.Vector', function () { v.x = 2; v.y = 3; v.z = -6; - const res = mockP5.Vector.setMag(v, 14); + const res = Vector.setMag(v, 14); expect(res.x).to.eql(4); expect(res.y).to.eql(6); expect(res.z).to.eql(-12); @@ -1267,8 +1383,8 @@ suite('p5.Vector', function () { suite('when given a target vector', function () { test('should set the magnitude on the target', function () { - const target = new mockP5.Vector(0, 1, 0); - const res = mockP5.Vector.setMag(v, 4, target); + const target = new Vector(0, 1, 0); + const res = Vector.setMag(v, 4, target); expect(target).to.equal(res); expect(target.x).to.eql(4); expect(target.y).to.eql(0); @@ -1280,7 +1396,7 @@ suite('p5.Vector', function () { suite('heading', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new Vector(0,0,0); }); suite('p5.Vector.prototype.heading() [INSTANCE]', function () { @@ -1311,7 +1427,7 @@ suite('p5.Vector', function () { suite.todo('with `angleMode(DEGREES)`', function () { beforeEach(function () { - mockP5Prototype.angleMode(mockP5.DEGREES); + mockP5Prototype.angleMode(DEGREES); }); test('heading for vector pointing right is 0', function () { @@ -1339,28 +1455,28 @@ suite('p5.Vector', function () { suite('p5.Vector.heading() [CLASS]', function () { test('should return a number', function () { - expect(typeof mockP5.Vector.heading(v) === 'number').to.eql(true); + expect(typeof Vector.heading(v) === 'number').to.eql(true); }); test('heading for vector pointing right is 0', function () { v.x = 1; v.y = 0; v.z = 0; - expect(mockP5.Vector.heading(v)).to.be.closeTo(0, 0.01); + expect(Vector.heading(v)).to.be.closeTo(0, 0.01); }); test('heading for vector pointing down is PI/2', function () { v.x = 0; v.y = 1; v.z = 0; - expect(mockP5.Vector.heading(v)).to.be.closeTo(Math.PI / 2, 0.01); + expect(Vector.heading(v)).to.be.closeTo(Math.PI / 2, 0.01); }); test('heading for vector pointing left is PI', function () { v.x = -1; v.y = 0; v.z = 0; - expect(mockP5.Vector.heading(v)).to.be.closeTo(Math.PI, 0.01); + expect(Vector.heading(v)).to.be.closeTo(Math.PI, 0.01); }); }); }); @@ -1370,14 +1486,13 @@ suite('p5.Vector', function () { expect(v.lerp()).to.eql(v); }); - // PEND: ADD BACK IN - // suite('with p5.Vector', function() { - // test('should call lerp with 4 arguments', function() { - // spyOn(v, 'lerp').andCallThrough(); - // v.lerp(new p5.Vector(1,2,3), 1); - // expect(v.lerp).toHaveBeenCalledWith(1, 2, 3, 1); - // }); - // }); + suite('with p5.Vector', function() { + test('should call lerp with 4 arguments', function() { + vi.spyOn(v, 'lerp'); + v.lerp(new Vector(1,2,3), 1); + expect(v.lerp).toHaveBeenCalledWith(1, 2, 3, 1); + }); + }); suite('with x, y, z, amt', function () { beforeEach(function () { @@ -1416,9 +1531,9 @@ suite('p5.Vector', function () { suite('p5.Vector.lerp(v1, v2, amt)', function () { var res, v1, v2; beforeEach(function () { - v1 = new mockP5.Vector(0, 0, 0); - v2 = new mockP5.Vector(2, 2, 2); - res = mockP5.Vector.lerp(v1, v2, 0.5); + v1 = new Vector(0, 0, 0); + v2 = new Vector(2, 2, 2); + res = Vector.lerp(v1, v2, 0.5); }); test('should not be undefined', function () { @@ -1426,7 +1541,7 @@ suite('p5.Vector', function () { }); test('should be a p5.Vector', function () { - expect(res).to.be.an.instanceof(mockP5.Vector); + expect(res).to.be.an.instanceof(Vector); }); test('should return neither v1 nor v2', function () { @@ -1445,7 +1560,7 @@ suite('p5.Vector', function () { var w; beforeEach(function () { v.set(1, 2, 3); - w = new mockP5.Vector(4, 6, 8); + w = new Vector(4, 6, 8); }); test('if amt is 0, returns original vector', function () { @@ -1496,9 +1611,9 @@ suite('p5.Vector', function () { suite('p5.Vector.slerp(v1, v2, amt)', function () { var res, v1, v2; beforeEach(function () { - v1 = new mockP5.Vector(1, 0, 0); - v2 = new mockP5.Vector(0, 0, 1); - res = mockP5.Vector.slerp(v1, v2, 1 / 3); + v1 = new Vector(1, 0, 0); + v2 = new Vector(0, 0, 1); + res = Vector.slerp(v1, v2, 1 / 3); }); test('should not be undefined', function () { @@ -1506,7 +1621,7 @@ suite('p5.Vector', function () { }); test('should be a p5.Vector', function () { - expect(res).to.be.an.instanceof(mockP5.Vector); + expect(res).to.be.an.instanceof(Vector); }); test('should return neither v1 nor v2', function () { @@ -1521,14 +1636,14 @@ suite('p5.Vector', function () { }); test('Make sure the interpolation in -1/3 is correct', function () { - mockP5.Vector.slerp(v1, v2, -1 / 3, res); + Vector.slerp(v1, v2, -1 / 3, res); expect(res.x).to.be.closeTo(Math.cos(-Math.PI / 6), 0.00001); expect(res.y).to.be.closeTo(0, 0.00001); expect(res.z).to.be.closeTo(Math.sin(-Math.PI / 6), 0.00001); }); test('Make sure the interpolation in 5/3 is correct', function () { - mockP5.Vector.slerp(v1, v2, 5 / 3, res); + Vector.slerp(v1, v2, 5 / 3, res); expect(res.x).to.be.closeTo(Math.cos((5 * Math.PI) / 6), 0.00001); expect(res.y).to.be.closeTo(0, 0.00001); expect(res.z).to.be.closeTo(Math.sin((5 * Math.PI) / 6), 0.00001); @@ -1539,7 +1654,7 @@ suite('p5.Vector', function () { var res, angle; beforeEach(function () { angle = Math.PI / 2; - res = mockP5.Vector.fromAngle(angle); + res = Vector.fromAngle(angle); }); test('should be a p5.Vector with values (0,1)', function () { @@ -1551,7 +1666,7 @@ suite('p5.Vector', function () { suite('p5.Vector.random2D()', function () { var res; beforeEach(function () { - res = mockP5.Vector.random2D(); + res = Vector.random2D(); }); test('should be a unit p5.Vector', function () { @@ -1562,7 +1677,7 @@ suite('p5.Vector', function () { suite('p5.Vector.random3D()', function () { var res; beforeEach(function () { - res = mockP5.Vector.random3D(); + res = Vector.random3D(); }); test('should be a unit p5.Vector', function () { expect(res.mag()).to.be.closeTo(1, 0.01); @@ -1571,7 +1686,7 @@ suite('p5.Vector', function () { suite('array', function () { beforeEach(function () { - v = new mockP5.Vector(1, 23, 4); + v = new Vector(1, 23, 4); }); suite('p5.Vector.prototype.array() [INSTANCE]', function () { @@ -1586,11 +1701,11 @@ suite('p5.Vector', function () { suite('p5.Vector.array() [CLASS]', function () { test('should return an array', function () { - expect(mockP5.Vector.array(v)).to.be.instanceof(Array); + expect(Vector.array(v)).to.be.instanceof(Array); }); test('should return an with the x y and z components', function () { - expect(mockP5.Vector.array(v)).to.eql([1, 23, 4]); + expect(Vector.array(v)).to.eql([1, 23, 4]); }); }); }); @@ -1609,31 +1724,31 @@ suite('p5.Vector', function () { incoming_x = 1; incoming_y = 1; incoming_z = 1; - original_incoming = new mockP5.Vector( + original_incoming = new Vector( incoming_x, incoming_y, incoming_z ); - x_normal = new mockP5.Vector(3, 0, 0); - y_normal = new mockP5.Vector(0, 3, 0); - z_normal = new mockP5.Vector(0, 0, 3); + x_normal = new Vector(3, 0, 0); + y_normal = new Vector(0, 3, 0); + z_normal = new Vector(0, 0, 3); - x_bounce_incoming = new mockP5.Vector( + x_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z ); x_bounce_outgoing = x_bounce_incoming.reflect(x_normal); - y_bounce_incoming = new mockP5.Vector( + y_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z ); y_bounce_outgoing = y_bounce_incoming.reflect(y_normal); - z_bounce_incoming = new mockP5.Vector( + z_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z @@ -1642,9 +1757,9 @@ suite('p5.Vector', function () { }); test('should return a p5.Vector', function () { - expect(x_bounce_incoming).to.be.an.instanceof(mockP5.Vector); - expect(y_bounce_incoming).to.be.an.instanceof(mockP5.Vector); - expect(z_bounce_incoming).to.be.an.instanceof(mockP5.Vector); + expect(x_bounce_incoming).to.be.an.instanceof(Vector); + expect(y_bounce_incoming).to.be.an.instanceof(Vector); + expect(z_bounce_incoming).to.be.an.instanceof(Vector); }); test('should update this', function () { @@ -1720,47 +1835,47 @@ suite('p5.Vector', function () { incoming_x = 1; incoming_y = 1; incoming_z = 1; - original_incoming = new mockP5.Vector( + original_incoming = new Vector( incoming_x, incoming_y, incoming_z ); - x_target = new mockP5.Vector(); - y_target = new mockP5.Vector(); - z_target = new mockP5.Vector(); + x_target = new Vector(0, 0, 0); + y_target = new Vector(0, 0, 0); + z_target = new Vector(0, 0, 0); - x_normal = new mockP5.Vector(3, 0, 0); - y_normal = new mockP5.Vector(0, 3, 0); - z_normal = new mockP5.Vector(0, 0, 3); + x_normal = new Vector(3, 0, 0); + y_normal = new Vector(0, 3, 0); + z_normal = new Vector(0, 0, 3); - x_bounce_incoming = new mockP5.Vector( + x_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z ); - x_bounce_outgoing = mockP5.Vector.reflect( + x_bounce_outgoing = Vector.reflect( x_bounce_incoming, x_normal, x_target ); - y_bounce_incoming = new mockP5.Vector( + y_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z ); - y_bounce_outgoing = mockP5.Vector.reflect( + y_bounce_outgoing = Vector.reflect( y_bounce_incoming, y_normal, y_target ); - z_bounce_incoming = new mockP5.Vector( + z_bounce_incoming = new Vector( incoming_x, incoming_y, incoming_z ); - z_bounce_outgoing = mockP5.Vector.reflect( + z_bounce_outgoing = Vector.reflect( z_bounce_incoming, z_normal, z_target @@ -1768,9 +1883,9 @@ suite('p5.Vector', function () { }); test('should return a p5.Vector', function () { - expect(x_bounce_incoming).to.be.an.instanceof(mockP5.Vector); - expect(y_bounce_incoming).to.be.an.instanceof(mockP5.Vector); - expect(z_bounce_incoming).to.be.an.instanceof(mockP5.Vector); + expect(x_bounce_incoming).to.be.an.instanceof(Vector); + expect(y_bounce_incoming).to.be.an.instanceof(Vector); + expect(z_bounce_incoming).to.be.an.instanceof(Vector); }); test('should not update this', function () { @@ -1846,8 +1961,8 @@ suite('p5.Vector', function () { let v1; beforeEach(function () { - v0 = new mockP5.Vector(0, 0, 0); - v1 = new mockP5.Vector(1, 2, 3); + v0 = new Vector(0, 0, 0); + v1 = new Vector(1, 2, 3); }); suite('p5.Vector.prototype.mag() [INSTANCE]', function () { @@ -1859,8 +1974,8 @@ suite('p5.Vector', function () { suite('p5.Vector.mag() [CLASS]', function () { test('should return the magnitude of the vector', function () { - expect(mockP5.Vector.mag(v0)).to.eql(0); - expect(mockP5.Vector.mag(v1)).to.eql(MAG); + expect(Vector.mag(v0)).to.eql(0); + expect(Vector.mag(v1)).to.eql(MAG); }); }); }); @@ -1872,8 +1987,8 @@ suite('p5.Vector', function () { let v1; beforeEach(function () { - v0 = new mockP5.Vector(0, 0, 0); - v1 = new mockP5.Vector(1, 2, 3); + v0 = new Vector(0, 0, 0); + v1 = new Vector(1, 2, 3); }); suite('p5.Vector.prototype.magSq() [INSTANCE]', function () { @@ -1885,8 +2000,8 @@ suite('p5.Vector', function () { suite('p5.Vector.magSq() [CLASS]', function () { test('should return the magnitude of the vector', function () { - expect(mockP5.Vector.magSq(v0)).to.eql(0); - expect(mockP5.Vector.magSq(v1)).to.eql(MAG); + expect(Vector.magSq(v0)).to.eql(0); + expect(Vector.magSq(v1)).to.eql(MAG); }); }); }); @@ -1894,8 +2009,8 @@ suite('p5.Vector', function () { suite('equals', function () { suite('p5.Vector.prototype.equals() [INSTANCE]', function () { test('should return false for parameters inequal to the vector', function () { - const v1 = new mockP5.Vector(0, -1, 1); - const v2 = new mockP5.Vector(1, 2, 3); + const v1 = new Vector(0, -1, 1); + const v2 = new Vector(1, 2, 3); const a2 = [1, 2, 3]; expect(v1.equals(v2)).to.be.false; expect(v1.equals(a2)).to.be.false; @@ -1903,88 +2018,89 @@ suite('p5.Vector', function () { }); test('should return true for equal vectors', function () { - const v1 = new mockP5.Vector(0, -1, 1); - const v2 = new mockP5.Vector(0, -1, 1); + const v1 = new Vector(0, -1, 1); + const v2 = new Vector(0, -1, 1); expect(v1.equals(v2)).to.be.true; }); test('should return true for arrays equal to the vector', function () { - const v1 = new mockP5.Vector(0, -1, 1); + const v1 = new Vector(0, -1, 1); const a1 = [0, -1, 1]; expect(v1.equals(a1)).to.be.true; }); test('should return true for arguments equal to the vector', function () { - const v1 = new mockP5.Vector(0, -1, 1); + const v1 = new Vector(0, -1, 1); expect(v1.equals(0, -1, 1)).to.be.true; }); }); suite('p5.Vector.equals() [CLASS]', function () { test('should return false for inequal parameters', function () { - const v1 = new mockP5.Vector(0, -1, 1); - const v2 = new mockP5.Vector(1, 2, 3); + const v1 = new Vector(0, -1, 1); + const v2 = new Vector(1, 2, 3); const a2 = [1, 2, 3]; - expect(mockP5.Vector.equals(v1, v2)).to.be.false; - expect(mockP5.Vector.equals(v1, a2)).to.be.false; - expect(mockP5.Vector.equals(a2, v1)).to.be.false; + expect(Vector.equals(v1, v2)).to.be.false; + expect(Vector.equals(v1, a2)).to.be.false; + expect(Vector.equals(a2, v1)).to.be.false; }); test('should return true for equal vectors', function () { - const v1 = new mockP5.Vector(0, -1, 1); - const v2 = new mockP5.Vector(0, -1, 1); - expect(mockP5.Vector.equals(v1, v2)).to.be.true; + const v1 = new Vector(0, -1, 1); + const v2 = new Vector(0, -1, 1); + expect(Vector.equals(v1, v2)).to.be.true; }); test('should return true for equal vectors and arrays', function () { - const v1 = new mockP5.Vector(0, -1, 1); + const v1 = new Vector(0, -1, 1); const a1 = [0, -1, 1]; - expect(mockP5.Vector.equals(v1, a1)).to.be.true; - expect(mockP5.Vector.equals(a1, v1)).to.be.true; + expect(Vector.equals(v1, a1)).to.be.true; + expect(Vector.equals(a1, v1)).to.be.true; }); test('should return true for equal arrays', function () { const a1 = [0, -1, 1]; const a2 = [0, -1, 1]; - expect(mockP5.Vector.equals(a1, a2)).to.be.true; + expect(Vector.equals(a1, a2)).to.be.true; }); }); }); suite('set values', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new Vector(); }); - test('should set values to [0,0,0] if values array is empty', function () { + test('should NOT set values to [0,0,0] if values array is empty', function () { v.values = []; assert.equal(v.x, 0); assert.equal(v.y, 0); assert.equal(v.z, 0); - assert.equal(v.dimensions, 2); + assert.equal(v.dimensions, 0); }); }); suite('get value', function () { test('should return element in range of a non empty vector', function () { - let vect = new mockP5.Vector(1, 2, 3, 4); + let vect = new Vector(1, 2, 3, 4); assert.equal(vect.getValue(0), 1); assert.equal(vect.getValue(1), 2); assert.equal(vect.getValue(2), 3); assert.equal(vect.getValue(3), 4); }); - test.fails( - 'should throw friendly error if attempting to get element outside lenght', + test('should throw friendly error if attempting to get element outside length', function () { - let vect = new mockP5.Vector(1, 2, 3, 4); - assert.equal(vect.getValue(5), 1); + let vect = new Vector(1, 2, 3, 4); + globalThis.FESCalled = false; + assert.equal(vect.getValue(5), undefined); + assert.equal(globalThis.FESCalled, true); } ); }); suite('set value', function () { test('should set value of element in range', function () { - let vect = new mockP5.Vector(1, 2, 3, 4); + let vect = new Vector(1, 2, 3, 4); vect.setValue(0, 7); assert.equal(vect.getValue(0), 7); assert.equal(vect.getValue(1), 2); @@ -1992,30 +2108,31 @@ suite('p5.Vector', function () { assert.equal(vect.getValue(3), 4); }); - test.fails( - 'should throw friendly error if attempting to set element outside lenght', + test('should throw friendly error if attempting to set element outside lenght', function () { - let vect = new mockP5.Vector(1, 2, 3, 4); + let vect = new Vector(1, 2, 3, 4); + globalThis.FESCalled = false; vect.setValue(100, 7); + assert.equal(globalThis.FESCalled, true); } ); }); describe('get w', () => { it('should return the w component of the vector', () => { - v = new mockP5.Vector(1, 2, 3, 4); + v = new Vector(1, 2, 3, 4); expect(v.w).toBe(4); }); it('should return 0 if w component is not set', () => { - v = new mockP5.Vector(1, 2, 3); + v = new Vector(1, 2, 3); expect(v.w).toBe(0); }); }); describe('set w', () => { it('should set 4th dimension of vector to w value if it exists', () => { - v = new mockP5.Vector(1, 2, 3, 4); + v = new Vector(1, 2, 3, 4); v.w = 7; expect(v.x).toBe(1); expect(v.y).toBe(2); @@ -2024,24 +2141,22 @@ suite('p5.Vector', function () { }); it('should throw error if trying to set w if vector dimensions is less than 4', () => { - v = new mockP5.Vector(1, 2); + v = new Vector(1, 2); v.w = 5; - console.log(v); - console.log(v.w); expect(v.w).toBe(0); //TODO: Check this, maybe this should fail }); }); describe('vector to string', () => { it('should return the string version of a vector', () => { - v = new mockP5.Vector(1, 2, 3, 4); + v = new Vector(1, 2, 3, 4); expect(v.toString()).toBe('vector[1, 2, 3, 4]'); }); }); describe('set heading', () => { it('should rotate a 2D vector by specified angle without changing magnitude', () => { - v = new mockP5.Vector(0, 2); + v = new Vector(0, 2); const mag = v.mag(); expect(v.setHeading(2 * Math.PI).mag()).toBe(mag); expect(v.x).toBe(2); @@ -2051,23 +2166,23 @@ suite('p5.Vector', function () { describe('clamp to zero', () => { it('should clamp values cloze to zero to zero, with Number.epsilon value', () => { - v = new mockP5.Vector(0, 1, 0.5, 0.1, 0.0000000000000001); + v = new Vector(0, 1, 0.5, 0.1, 0.0000000000000001); expect(v.clampToZero().values).toEqual([0, 1, 0.5, 0.1, 0]); }); }); suite('p5.Vector.fromAngles()', function () { - it('should create a v3ctor froma pair of ISO spherical angles', () => { - let vect = mockP5.Vector.fromAngles(0, 0); + it('should create a vector froma pair of ISO spherical angles', () => { + let vect = Vector.fromAngles(0, 0); expect(vect.values).toEqual([0, -1, 0]); }); }); suite('p5.Vector.rotate()', function () { it('should rotate the vector (only 2D vectors) by the given angle; magnitude remains the same.', () => { - v = new mockP5.Vector(0, 1, 2); - let target = new mockP5.Vector(); - mockP5.Vector.rotate(v, 1 * Math.PI, target); + v = new Vector(0, 1, 2); + let target = new Vector(); + Vector.rotate(v, 1 * Math.PI, target); expect(target.values).toEqual([ -4.10759023698152e-16, -2.23606797749979, 2 ]); diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index bdfb0e5476..6ad679d39b 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -2242,7 +2242,9 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca }, { myp5 }); } catch (e) { /* expected */ } - assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called'); + + + assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called, btw: '+globalThis.FESCalled); const errMsg = mockUserError.mock.calls[0][1]; assert.include(errMsg, 'float3'); assert.include(errMsg, 'float4');