From e9c9d96c3166409a9c4193f3d36d1a4487a5268c Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Fri, 14 Nov 2025 20:03:03 +0000 Subject: [PATCH 01/14] Implement pad zero proof of concept --- src/math/index.js | 2 ++ src/math/p5.Vector.js | 22 +++++++++++++++++----- src/math/patch-vector.js | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 src/math/patch-vector.js diff --git a/src/math/index.js b/src/math/index.js index 2acb397b21..40d8d75924 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 patchVector 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(patchVector); } diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index a264fb811e..b0b29b9519 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -30,6 +30,8 @@ const calculateRemainder3D = function (xComponent, yComponent, zComponent) { }; class Vector { + _values = []; + // This is how it comes in with createVector() // This check if the first argument is a function constructor(...args) { @@ -521,6 +523,7 @@ class Vector { args = args[0]; } args.forEach((value, index) => { + if(!this._values[index]) this._values[index] = 0; this._values[index] = (this._values[index] || 0) + (value || 0); }); return this; @@ -833,6 +836,7 @@ class Vector { sub(...args) { if (args[0] instanceof Vector) { args[0].values.forEach((value, index) => { + if(!this._values[index]) this._values[index] = 0; this._values[index] -= value || 0; }); } else if (Array.isArray(args[0])) { @@ -1046,6 +1050,7 @@ class Vector { 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( @@ -1277,18 +1282,23 @@ class Vector { */ div(...args) { if (args.length === 0) return this; + // If passed a vector if (args.length === 1 && args[0] instanceof Vector) { const v = args[0]; if ( - v._values.every( + v.values.every( val => Number.isFinite(val) && typeof val === 'number' ) ) { - if (v._values.some(val => val === 0)) { + 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]); + // this._values = this._values.map((val, i) => val / v.values[i]); + for (let i = 0; i < v.values.length; i++) { + if(!this._values[i]) this._values[i] = 0; + this._values[i] /= v.values[i]; + } } else { console.warn( 'p5.Vector.prototype.div:', @@ -1298,6 +1308,7 @@ class Vector { return this; } + // If passed an array if (args.length === 1 && Array.isArray(args[0])) { const arr = args[0]; if (arr.every(val => Number.isFinite(val) && typeof val === 'number')) { @@ -1315,6 +1326,7 @@ class Vector { return this; } + // If passed individual arguments 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'); @@ -1514,7 +1526,7 @@ 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 sum + component * (args[index] || 0); @@ -3034,7 +3046,7 @@ 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 { diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js new file mode 100644 index 0000000000..ec2ed993e8 --- /dev/null +++ b/src/math/patch-vector.js @@ -0,0 +1,14 @@ +export default function patchVector(p5, fn, lifecycles){ + p5.decorateHelper('createVector', function(target){ + return function(...args){ + if(args.length === 0){ + // console.log('empty call to createVector'); + return new Proxy(new p5.Vector(0, 0, 0), { + + }); + }else{ + return target.call(this, ...args); + } + }; + }); +} From 124a60e5fa233197a72191574c64bdb3cc1ed914 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Mon, 1 Dec 2025 11:24:15 +0000 Subject: [PATCH 02/14] Vector use values property directly and calculate dimension --- src/math/p5.Vector.js | 181 ++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 93 deletions(-) diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index b0b29b9519..8f7f23710a 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -30,62 +30,58 @@ const calculateRemainder3D = function (xComponent, yComponent, zComponent) { }; class Vector { - _values = []; + 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 (typeof args[0] === 'function') { this.isPInst = true; this._fromRadians = args[0]; this._toRadians = args[1]; - values = args.slice(2); // .map(arg => arg || 0); - } - let dimensions = values.length; // TODO: make default 3 if no arguments - if (dimensions === 0) { - this.dimensions = 2; - this._values = [0, 0, 0]; - } else { - this.dimensions = dimensions; - this._values = values; - } - } - - /** - * 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(); + args = args.slice(2); } + 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; } /** @@ -98,7 +94,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; } /** @@ -119,8 +115,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', @@ -144,8 +140,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', @@ -164,7 +160,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; } /** @@ -177,7 +173,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; } /** @@ -190,7 +186,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; } /** @@ -203,8 +199,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; } } @@ -218,8 +214,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; } } @@ -233,8 +229,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; } } @@ -248,8 +244,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; } } @@ -274,7 +270,7 @@ class Vector { * */ toString() { - return `vector[${this._values.join(', ')}]`; + return `vector[${this.values.join(', ')}]`; } /** @@ -336,13 +332,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; } @@ -376,9 +371,9 @@ 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); } } @@ -523,8 +518,8 @@ class Vector { args = args[0]; } args.forEach((value, index) => { - if(!this._values[index]) this._values[index] = 0; - this._values[index] = (this._values[index] || 0) + (value || 0); + if(!this.values[index]) this.values[index] = 0; + this.values[index] = (this.values[index] || 0) + (value || 0); }); return this; } @@ -836,16 +831,16 @@ class Vector { sub(...args) { if (args[0] instanceof Vector) { args[0].values.forEach((value, index) => { - if(!this._values[index]) this._values[index] = 0; - this._values[index] -= value || 0; + if(!this.values[index]) this.values[index] = 0; + this.values[index] -= value || 0; }); } else if (Array.isArray(args[0])) { args[0].forEach((value, index) => { - this._values[index] -= value || 0; + this.values[index] -= value || 0; }); } else { args.forEach((value, index) => { - this._values[index] -= value || 0; + this.values[index] -= value || 0; }); } return this; @@ -1047,11 +1042,11 @@ class Vector { mult(...args) { if (args.length === 1 && args[0] instanceof Vector) { const v = args[0]; - const maxLen = Math.min(this._values.length, v.values.length); + 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]; + if(!this.values[i]) this.values[i] = 0; + this.values[i] *= v.values[i]; } else { console.warn( 'p5.Vector.prototype.mult:', @@ -1062,10 +1057,10 @@ class Vector { } } else if (args.length === 1 && Array.isArray(args[0])) { const arr = args[0]; - const maxLen = Math.min(this._values.length, arr.length); + 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]; + this.values[i] *= arr[i]; } else { console.warn( 'p5.Vector.prototype.mult:', @@ -1079,8 +1074,8 @@ class Vector { typeof args[0] === 'number' && Number.isFinite(args[0]) ) { - for (let i = 0; i < this._values.length; i++) { - this._values[i] *= args[0]; + for (let i = 0; i < this.values.length; i++) { + this.values[i] *= args[0]; } } return this; @@ -1296,8 +1291,8 @@ class Vector { } // this._values = this._values.map((val, i) => val / v.values[i]); for (let i = 0; i < v.values.length; i++) { - if(!this._values[i]) this._values[i] = 0; - this._values[i] /= v.values[i]; + if(!this.values[i]) this.values[i] = 0; + this.values[i] /= v.values[i]; } } else { console.warn( @@ -1316,7 +1311,7 @@ class Vector { console.warn('p5.Vector.prototype.div:', 'divide by 0'); return this; } - this._values = this._values.map((val, i) => val / arr[i]); + this.values = this.values.map((val, i) => val / arr[i]); } else { console.warn( 'p5.Vector.prototype.div:', @@ -1332,7 +1327,7 @@ class Vector { console.warn('p5.Vector.prototype.div:', 'divide by 0'); return this; } - this._values = this._values.map((val, i) => val / args[0]); + this.values = this.values.map((val, i) => val / args[0]); } else { console.warn( 'p5.Vector.prototype.div:', @@ -1414,7 +1409,7 @@ class Vector { * */ magSq() { - return this._values.reduce( + return this.values.reduce( (sum, component) => sum + component * component, 0 ); @@ -1528,7 +1523,7 @@ class Vector { if (args[0] instanceof Vector) { 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); } @@ -3053,8 +3048,8 @@ class Vector { 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; } } @@ -3074,8 +3069,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; } From 04a2f240f9380861cf2be60b866319760bfab25a Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Mon, 1 Dec 2025 14:21:29 +0000 Subject: [PATCH 03/14] Implement vector smaller size priority --- src/math/p5.Vector.js | 364 +++++++++++++++++++++++------------------- 1 file changed, 204 insertions(+), 160 deletions(-) diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index 8f7f23710a..49e56cc401 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -517,10 +517,13 @@ class Vector { } else if (Array.isArray(args[0])) { args = args[0]; } - args.forEach((value, index) => { - if(!this.values[index]) this.values[index] = 0; - this.values[index] = (this.values[index] || 0) + (value || 0); - }); + + const resultDimension = Math.min(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { + if(i < resultDimension) acc[i] = this.values[i] + args[i]; + return acc; + }, new Array(resultDimension)); + return this; } @@ -644,59 +647,74 @@ 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) { + if (args[0] instanceof Vector) { + args = args[0].values; + } else if (Array.isArray(args[0])) { + args = args[0]; } + + if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + + const resultDimension = Math.min(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { + if(i < resultDimension) acc[i] = this.values[i] % args[i]; + return acc; + }, new Array(resultDimension)); + + return this; + // 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] + // ); + // } + // } + // } } /** @@ -830,19 +848,17 @@ class Vector { */ sub(...args) { if (args[0] instanceof Vector) { - args[0].values.forEach((value, index) => { - if(!this.values[index]) this.values[index] = 0; - this.values[index] -= value || 0; - }); + args = args[0].values; } 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; - }); + args = args[0]; } + + const resultDimension = Math.min(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { + if(i < resultDimension) acc[i] = this.values[i] - args[i]; + return acc; + }, new Array(resultDimension)); + return this; } @@ -1040,44 +1056,58 @@ 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') { - 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]; - } + if (args[0] instanceof Vector) { + args = args[0].values; + } else if (Array.isArray(args[0])) { + args = args[0]; } + + if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + + const resultDimension = Math.min(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { + if(i < resultDimension) acc[i] = this.values[i] * args[i]; + return acc; + }, new Array(resultDimension)); + + // 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; } @@ -1276,64 +1306,78 @@ class Vector { * @chainable */ div(...args) { - if (args.length === 0) return this; - // If passed a vector - 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]); - for (let i = 0; i < v.values.length; i++) { - if(!this.values[i]) this.values[i] = 0; - this.values[i] /= v.values[i]; - } - } else { - console.warn( - 'p5.Vector.prototype.div:', - 'vector contains components that are either undefined or not finite numbers' - ); - } - return this; - } - - // If passed an array - 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[0] instanceof Vector) { + args = args[0].values; + } else if (Array.isArray(args[0])) { + args = args[0]; } - // If passed individual arguments - 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 { - console.warn( - 'p5.Vector.prototype.div:', - 'arguments contain components that are either undefined or not finite numbers' - ); - } + if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + + const resultDimension = Math.min(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { + if(i < resultDimension) acc[i] = this.values[i] / args[i]; + return acc; + }, new Array(resultDimension)); + + // if (args.length === 0) return this; + // // If passed a vector + // 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]); + // for (let i = 0; i < v.values.length; i++) { + // if(!this.values[i]) this.values[i] = 0; + // this.values[i] /= v.values[i]; + // } + // } else { + // console.warn( + // 'p5.Vector.prototype.div:', + // 'vector contains components that are either undefined or not finite numbers' + // ); + // } + // return this; + // } + + // // If passed an array + // 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 passed individual arguments + // 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 { + // console.warn( + // 'p5.Vector.prototype.div:', + // 'arguments contain components that are either undefined or not finite numbers' + // ); + // } return this; } From 492b9e9fbe39cd5a3025b0c8d7a281900a900cc0 Mon Sep 17 00:00:00 2001 From: ksen0 Date: Fri, 6 Feb 2026 15:16:52 +0100 Subject: [PATCH 04/14] Vector decorator does not use Proxy --- src/math/patch-vector.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index ec2ed993e8..40fc2d6f80 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -2,10 +2,9 @@ export default function patchVector(p5, fn, lifecycles){ p5.decorateHelper('createVector', function(target){ return function(...args){ if(args.length === 0){ - // console.log('empty call to createVector'); - return new Proxy(new p5.Vector(0, 0, 0), { + return new p5.Vector(0, 0, 0), { - }); + }; }else{ return target.call(this, ...args); } From 93920ba50a9e85677955154ea685409cdeb802af Mon Sep 17 00:00:00 2001 From: ksen0 Date: Fri, 6 Feb 2026 17:05:51 +0100 Subject: [PATCH 05/14] Abstract out vector format and dimension checks for binary vector ops --- src/math/math.js | 5 - src/math/p5.Vector.js | 282 +++++++++++++----------------------- src/math/patch-vector.js | 11 +- test/unit/math/p5.Vector.js | 8 +- 4 files changed, 115 insertions(+), 191 deletions(-) diff --git a/src/math/math.js b/src/math/math.js index 9071aa233b..9f2e110861 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -100,11 +100,6 @@ function math(p5, fn) { * */ 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), diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index e3ade52617..bfd4b517ff 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -5,31 +5,34 @@ 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; -}; - -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; +/// HELPER FOR SMALLER DIMENSION PRIORITY LOGIC. +/// Pending implementation as decorator. +const smallerDimensionPriorityHelper = function(dimOther, dimSelf) { + const resultDimension = Math.min(dimOther, dimSelf); + if (dimOther != dimSelf) { + + console.warn( + 'When working with two vectors of different sizes, the smaller dimension is used. In this operation, both vector will be treated as ' + resultDimension + 'D vectors, and any additional values of the linger vector will be ignored.', + ); } - return this; -}; + return resultDimension +} class Vector { + /** + * The values of the 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. + * + * @type {Array} The array of values representing the vector. + */ values = []; // This is how it comes in with createVector() @@ -41,45 +44,19 @@ class Vector { this._toRadians = args[1]; args = args.slice(2); } + + // TODO Implement using decorator API to reduce duplication. + // Should use the same check as patchVector on 'createVector' + if(args.length === 0){ + console.warn( + '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.' + ); + args = [0, 0, 0] + } + 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; } @@ -377,6 +354,8 @@ class Vector { } } + + /** * Adds to a vector's components. * @@ -512,13 +491,17 @@ class Vector { * @chainable */ add(...args) { + + // TODO Implement using decorator API to reduce duplication. if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; + } else if (args.length === 0) { + return this; } - const resultDimension = Math.min(args.length, this.dimensions); + const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { if(i < resultDimension) acc[i] = this.values[i] + args[i]; return acc; @@ -527,6 +510,8 @@ class Vector { return this; } + + /** * Performs modulo (remainder) division with a vector's `x`, `y`, and `z` * components. @@ -648,73 +633,37 @@ class Vector { * @chainable */ rem(...args) { + + // TODO Implement using decorator API to reduce duplication. + if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; + } else if (args.length === 1) { + args = new Array(this.dimensions).fill(args[0]); + } else if (args.length === 0) { + return this; } - if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + if(!args.every(v => Number.isFinite(v))){ + console.warn( + 'p5.Vector.prototype.rem', + 'Arguments contain non-finite numbers' + ); + return this; + }; + + const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); - const resultDimension = Math.min(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { - if(i < resultDimension) acc[i] = this.values[i] % args[i]; + // Extra check for non empty operand + if(i < resultDimension && args[i] > 0) acc[i] = this.values[i] % args[i]; + else acc[i] = this.values[i] return acc; }, new Array(resultDimension)); return this; - // 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] - // ); - // } - // } - // } } /** @@ -847,13 +796,18 @@ class Vector { * @chainable */ sub(...args) { + + // TODO Implement using decorator API to reduce duplication. if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; + } else if (args.length === 0) { + return this; } - const resultDimension = Math.min(args.length, this.dimensions); + const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { if(i < resultDimension) acc[i] = this.values[i] - args[i]; return acc; @@ -1056,15 +1010,28 @@ class Vector { * @chainable */ mult(...args) { + // TODO Implement using decorator API to reduce duplication. + if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; + } else if (args.length === 1) { + args = new Array(this.dimensions).fill(args[0]); + } else if (args.length === 0) { + return this; } - if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + if(!args.every(v => Number.isFinite(v))){ + console.warn( + 'p5.Vector.prototype.mult', + 'Arguments contain non-finite numbers' + ); + return this; + }; + + const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); - const resultDimension = Math.min(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { if(i < resultDimension) acc[i] = this.values[i] * args[i]; return acc; @@ -1306,79 +1273,35 @@ class Vector { * @chainable */ div(...args) { + + // TODO Implement using decorator API to reduce duplication. + if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; + } else if (args.length === 1) { + args = new Array(this.dimensions).fill(args[0]); + + } else if (args.length === 0) { + return this; } - if(!args.every(v => v !== 0 && Number.isFinite(v))) return this; + if(!args.every(v => typeof v === 'number' && v !== 0 && Number.isFinite(v))){ + console.warn( + 'p5.Vector.prototype.div:', + 'arguments contain components that are either 0 or not finite numbers' + ); + return this; + }; + + const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); - const resultDimension = Math.min(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { if(i < resultDimension) acc[i] = this.values[i] / args[i]; return acc; }, new Array(resultDimension)); - // if (args.length === 0) return this; - // // If passed a vector - // 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]); - // for (let i = 0; i < v.values.length; i++) { - // if(!this.values[i]) this.values[i] = 0; - // this.values[i] /= v.values[i]; - // } - // } else { - // console.warn( - // 'p5.Vector.prototype.div:', - // 'vector contains components that are either undefined or not finite numbers' - // ); - // } - // return this; - // } - - // // If passed an array - // 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 passed individual arguments - // 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 { - // console.warn( - // 'p5.Vector.prototype.div:', - // 'arguments contain components that are either undefined or not finite numbers' - // ); - // } - return this; } @@ -1436,7 +1359,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. diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index 40fc2d6f80..fb5717d59d 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -1,13 +1,18 @@ export default function patchVector(p5, fn, lifecycles){ + + // An empty vector defaults to a 3D vector. p5.decorateHelper('createVector', function(target){ return function(...args){ if(args.length === 0){ - return new p5.Vector(0, 0, 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.' + ); + return target.call(this, 0, 0, 0); }else{ return target.call(this, ...args); } }; }); + + } diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index 4028ee2dba..41863f9898 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -398,7 +398,7 @@ suite('p5.Vector', function () { suite('add()', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new mockP5.Vector(0, 0, 0); }); suite('with p5.Vector', function () { @@ -1370,7 +1370,7 @@ suite('p5.Vector', function () { expect(v.lerp()).to.eql(v); }); - // PEND: ADD BACK IN + // TODO PEND: ADD BACK IN // suite('with p5.Vector', function() { // test('should call lerp with 4 arguments', function() { // spyOn(v, 'lerp').andCallThrough(); @@ -1956,12 +1956,12 @@ suite('p5.Vector', function () { v = new mockP5.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 () { From f258b5e537912dbfb5a627371551d314b630f40b Mon Sep 17 00:00:00 2001 From: ksen0 Date: Wed, 18 Feb 2026 22:02:12 +0100 Subject: [PATCH 06/14] Added tests --- src/math/p5.Vector.js | 42 +-- src/math/patch-vector.js | 1 - test/unit/math/p5.Vector.js | 592 ++++++++++++++++++++---------------- 3 files changed, 358 insertions(+), 277 deletions(-) diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index bfd4b517ff..2bca04f450 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -7,21 +7,21 @@ import * as constants from '../core/constants'; /// HELPER FOR SMALLER DIMENSION PRIORITY LOGIC. /// Pending implementation as decorator. -const smallerDimensionPriorityHelper = function(dimOther, dimSelf) { - const resultDimension = Math.min(dimOther, dimSelf); - if (dimOther != dimSelf) { +const smallerDimensionPriority = function(dimOther, dimSelf) { + const minDimension = Math.min(dimOther, dimSelf); + if (dimOther !== dimSelf) { console.warn( - 'When working with two vectors of different sizes, the smaller dimension is used. In this operation, both vector will be treated as ' + resultDimension + 'D vectors, and any additional values of the linger vector will be ignored.', + 'When working with 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 linger vector will be ignored.', ); } - return resultDimension + return minDimension } class Vector { /** * The values of the 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). @@ -501,11 +501,11 @@ class Vector { return this; } - const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { - if(i < resultDimension) acc[i] = this.values[i] + args[i]; + if(i < minDimension) acc[i] = this.values[i] + Number(args[i]); return acc; - }, new Array(resultDimension)); + }, new Array(minDimension)); return this; } @@ -654,14 +654,14 @@ class Vector { return this; }; - const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { // Extra check for non empty operand - if(i < resultDimension && args[i] > 0) acc[i] = this.values[i] % args[i]; + if(i < minDimension && args[i] > 0) acc[i] = this.values[i] % args[i]; else acc[i] = this.values[i] return acc; - }, new Array(resultDimension)); + }, new Array(minDimension)); return this; } @@ -806,12 +806,12 @@ class Vector { return this; } - const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { - if(i < resultDimension) acc[i] = this.values[i] - args[i]; + if(i < minDimension) acc[i] = this.values[i] - args[i]; return acc; - }, new Array(resultDimension)); + }, new Array(minDimension)); return this; } @@ -1030,12 +1030,12 @@ class Vector { return this; }; - const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { - if(i < resultDimension) acc[i] = this.values[i] * args[i]; + if(i < minDimension) acc[i] = this.values[i] * args[i]; return acc; - }, new Array(resultDimension)); + }, new Array(minDimension)); // if (args.length === 1 && args[0] instanceof Vector) { // const v = args[0]; @@ -1295,12 +1295,12 @@ class Vector { return this; }; - const resultDimension = smallerDimensionPriorityHelper(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { - if(i < resultDimension) acc[i] = this.values[i] / args[i]; + if(i < minDimension) acc[i] = this.values[i] / args[i]; return acc; - }, new Array(resultDimension)); + }, new Array(minDimension)); return this; } diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index fb5717d59d..6929daae73 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -14,5 +14,4 @@ export default function patchVector(p5, fn, lifecycles){ }; }); - } diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index 41863f9898..a7b45039d3 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -1,24 +1,23 @@ -import vector from '../../../src/math/p5.Vector.js'; +import {default as vector, Vector} from '../../../src/math/p5.Vector.js'; import { vi } from 'vitest'; +// TODO add create Vector coverage + suite('p5.Vector', function () { var v; - const mockP5 = { - _validateParameters: vi.fn() - }; const mockP5Prototype = {}; beforeEach(async function () { - vector(mockP5, mockP5Prototype); + vector(mockP5Prototype); }); 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 +27,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 +43,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 +68,31 @@ 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('p5.prototype.createVector()', function () { + beforeEach(function () { + v = mockP5Prototype.createVector(); + }); + + test('should have x, y, z be initialized to 0,0,0', function () { + 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 +100,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 +115,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 () { @@ -103,7 +127,7 @@ suite('p5.Vector', function () { suite('new p5.Vector(1,2,undefined)', function () { beforeEach(function () { - v = new mockP5.Vector(1, 2, undefined); + v = new Vector(1, 2, undefined); }); test('should have x, y, z be initialized to 1,2,0', function () { @@ -116,13 +140,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 +176,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 +199,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 +212,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 +220,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 +231,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 +242,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 +303,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 +312,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 +353,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 +388,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 +407,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 +422,12 @@ suite('p5.Vector', function () { suite('add()', function () { beforeEach(function () { - v = new mockP5.Vector(0, 0, 0); + 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 +481,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 +501,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 () { @@ -510,28 +534,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 +565,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); @@ -583,9 +607,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 +629,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 +647,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 +664,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 +694,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 +724,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 +751,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 +766,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 +784,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 () { @@ -771,9 +799,9 @@ suite('p5.Vector', 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 +814,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 +851,20 @@ suite('p5.Vector', function () { }); }); + suite('with arglist', function () { + test('multiply 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 +885,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 +907,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 +916,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 +925,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 +937,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 +960,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 +975,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 +988,52 @@ suite('p5.Vector', function () { }); }); + + suite('smaller dimension', function () { + let v0, v1, v2, v3; + beforeEach(function () { + v0 = new Vector(); + 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, [2]); + assert.deepEqual(v1.add(v2).dimension, 1); + assert.deepEqual(v3.add(v2).values, [8,15]); + assert.deepEqual(v3.add(v2).dimension, 2); + }); + + test('should be prioritized in sub()', function () { + assert.deepEqual(v1.sub(v2).values, [-1]); + assert.deepEqual(v1.sub(v2).dimension, 1); + assert.deepEqual(v3.sub(v2).values, [2, 2]); + assert.deepEqual(v3.sub(v2).dimension, 2); + }); + + test('should be prioritized in mult()', function () { + assert.deepEqual(v1.mult(v2).values, [2]); + assert.deepEqual(v1.mult(v2).dimension, 1); + assert.deepEqual(v3.mult(v2).values, [8, 15]); + assert.deepEqual(v3.mult(v2).dimension, 2); + }); + + test('should be prioritized in div()', function () { + assert.deepEqual(v1.div(v2).values, [1/2]); + assert.deepEqual(v1.div(v2).dimension, 1); + assert.deepEqual(v3.div(v2).values, [2, 5/3]); + assert.deepEqual(v3.div(v2).dimension, 2); + }); + + test('should be prioritized in rem()', function () { + assert.deepEqual(v1.rem(v2).values, [1]); + assert.deepEqual(v1.rem(v2).dimension, 1); + assert.deepEqual(v3.rem(v2).values, [0, 2]); + assert.deepEqual(v3.rem(v2).dimension, 2); + }); + }); + suite('dot', function () { beforeEach(function () { v.x = 1; @@ -959,12 +1042,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 +1064,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 +1088,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 +1103,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 +1128,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 +1153,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 +1200,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 +1222,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 +1234,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 +1263,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 +1277,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 +1286,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 +1300,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 +1328,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 +1342,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 +1350,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 +1363,7 @@ suite('p5.Vector', function () { suite('heading', function () { beforeEach(function () { - v = new mockP5.Vector(); + v = new Vector(); }); suite('p5.Vector.prototype.heading() [INSTANCE]', function () { @@ -1311,7 +1394,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 +1422,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 +1453,13 @@ suite('p5.Vector', function () { expect(v.lerp()).to.eql(v); }); - // TODO 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 +1498,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 +1508,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 +1527,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 +1578,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 +1588,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 +1603,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 +1621,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 +1633,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 +1644,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 +1653,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 +1668,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 +1691,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 +1724,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 +1802,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(); + y_target = new Vector(); + z_target = new Vector(); - 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 +1850,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 +1928,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 +1941,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 +1954,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 +1967,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 +1976,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,57 +1985,57 @@ 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 NOT set values to [0,0,0] if values array is empty', function () { @@ -1966,7 +2048,7 @@ suite('p5.Vector', function () { }); 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); @@ -1976,7 +2058,7 @@ suite('p5.Vector', function () { test.fails( 'should throw friendly error if attempting to get element outside lenght', function () { - let vect = new mockP5.Vector(1, 2, 3, 4); + let vect = new Vector(1, 2, 3, 4); assert.equal(vect.getValue(5), 1); } ); @@ -1984,7 +2066,7 @@ suite('p5.Vector', function () { 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); @@ -1995,7 +2077,7 @@ suite('p5.Vector', function () { test.fails( '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); vect.setValue(100, 7); } ); @@ -2003,19 +2085,19 @@ suite('p5.Vector', function () { 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,7 +2106,7 @@ 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); @@ -2034,14 +2116,14 @@ suite('p5.Vector', function () { 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 +2133,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 ]); From 346a2acbacb2f5dcfc93ffc8a18b5ef2402ad0b1 Mon Sep 17 00:00:00 2001 From: ksen0 Date: Wed, 25 Feb 2026 15:34:15 +0100 Subject: [PATCH 07/14] Added benchmark tests --- test/bench/vectors.bench.js | 171 ++++++++++++++++++++++++++++++++++++ test/unit/math/p5.Vector.js | 7 +- 2 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 test/bench/vectors.bench.js diff --git a/test/bench/vectors.bench.js b/test/bench/vectors.bench.js new file mode 100644 index 0000000000..d8595be7c2 --- /dev/null +++ b/test/bench/vectors.bench.js @@ -0,0 +1,171 @@ +import {default as vector, Vector} from '../../src/math/p5.Vector.js'; + +import { bench, describe, beforeAll } from "vitest"; + +// This is not parameterizable because it's run all the time +function setupVectors() { + const n = 100; + const arr = [new Array(n)] + for (let i = 0; i < n; i++) arr[i] = new Vector(Math.random(), Math.random(), Math.random()) + return arr +} + + + +describe("vector operations", () => { + beforeAll(function () { + vector(mockP5Prototype); + }); + + + bench( + "mult 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].mult(arr[j]); + } + } + }, + { iterations: 100 } + ); + + 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]); + } + } + }, + { iterations: 100 } + ); + + 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]); + } + } + }, + { iterations: 100 } + ); + 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]); + } + } + }, + { iterations: 100 } + ); + + + + 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]); + } + } + }, + { iterations: 100 } + ); + + 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]); + } + } + }, + { iterations: 100 } + ); + + 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]); + } + } + }, + { iterations: 100 } + ); + 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]); + } + } + }, + { iterations: 100 } + ); + + +/** + * possible in vitest 3? + function benchMult(nLimited) { + bench( + `mult ${nLimited}`, + (arr) => { + console.log(">",arr) + console.log(">",arr.length) + for (let i = 0; i < nLimited; i++) { + for (let j = 0; j < nLimited; j++) { + const tmp = arr[i].mult(arr[j]); + } + } + }, + { + iterations: 20, + setup: () => setupVectors(10000), + } + ) + } + + benchMult(10) + //benchMult(1000) + //benchMult(2000) + //benchMult(3000) + //benchMult(4000) + + */ + +}); diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index a7b45039d3..e5630c2b10 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -10,6 +10,8 @@ suite('p5.Vector', function () { beforeEach(async function () { vector(mockP5Prototype); + //console.log(typeof mockP5Prototype.createVector); // Debug this + //console.log(Object.keys(mockP5Prototype)); // See what was added }); afterEach(function () {}); @@ -84,6 +86,7 @@ suite('p5.Vector', function () { }); 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); @@ -796,7 +799,7 @@ 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 Vector(1, 2, 3); @@ -852,7 +855,7 @@ suite('p5.Vector', function () { }); suite('with arglist', function () { - test('multiply the x, y, z with the scalar', 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); From e5df5f96082da9605d869c8338b2ad446828a539 Mon Sep 17 00:00:00 2001 From: ksen0 Date: Wed, 4 Mar 2026 16:19:21 +0100 Subject: [PATCH 08/14] Updating tests for vectors - unit and bench --- lib/empty-example/sketch.js | 90 ++++++++++++++++++++++++++++++++++--- src/math/math.js | 1 + src/math/p5.Vector.js | 6 +-- src/math/patch-vector.js | 23 +++++++++- test/bench/vectors.bench.js | 73 +++++------------------------- test/unit/math/p5.Vector.js | 10 ++--- 6 files changed, 126 insertions(+), 77 deletions(-) diff --git a/lib/empty-example/sketch.js b/lib/empty-example/sketch.js index 336fa0777f..a10ee85f04 100644 --- a/lib/empty-example/sketch.js +++ b/lib/empty-example/sketch.js @@ -1,7 +1,85 @@ function setup() { - // put setup code here - } - - function draw() { - // put drawing code here - } + createCanvas(200, 200); + background(0, 100, 100); + noLoop(); +} + +function draw() { + +let v0, v1, v2, v3; + v0 = new p5.Vector(); + v1 = new p5.Vector([1]); + v2 = new p5.Vector([2, 3]); + v3 = new p5.Vector([4, 5, 6]); + + print('should be prioritized in add()') + v0 = v1.add(v2) + console.log(v0.x, v0.y, v0.z) + console.log(v1.add(v2).values, [2]); + console.log(v1.add(v2).dimensions, 1); + console.log(v3.add(v2).values, [8,15]); + console.log(v3.add(v2).dimensions, 2); + + print('should be prioritized in sub()') + console.log(v1.sub(v2).values, [-1]); + console.log(v1.sub(v2).dimensions, 1); + console.log(v3.sub(v2).values, [2, 2]); + console.log(v3.sub(v2).dimensions, 2); + print('should be prioritized in mult()') + console.log(v1.mult(v2).values, [2]); + console.log(v1.mult(v2).dimensions, 1); + console.log(v3.mult(v2).values, [8, 15]); + console.log(v3.mult(v2).dimensions, 2); + + print('should be prioritized in div()'); + console.log(v1.div(v2).values, [1/2]); + console.log(v1.div(v2).dimensions, 1); + console.log(v3.div(v2).values, [2, 5/3]); + console.log(v3.div(v2).dimensions, 2); + + print('should be prioritized in rem()') + console.log(v1.rem(v2).values, [1]); + console.log(v1.rem(v2).dimensions, 1); + console.log(v3.rem(v2).values, [0, 2]); + console.log(v3.rem(v2).dimensions, 2); + + return; + + + const arr_random = []; + for (let i = 0; i < 100+2; i++) { + arr_random.push(random()); + } + + const arr = []; + + // TODO: + // for(let iters = 100; iters < 5000; i+= 100) + + console.log("creating vecotrs") + console.time(); + for (let i = 0; i < 100; i++) { + arr.push(createVector(arr_random[i], arr_random[i+1], arr_random[i+2])); + } + + console.timeEnd(); + + + console.log("pairwise multiplying them vecotrs") + console.time(); + + + for (let i = 0; i < arr.length; i++) { + for (let j = 0; j < arr.length; j++) { + const _ = arr[i].mult(arr[j].mult(2)); + } + } + + console.timeEnd(); + + //default: 1.7999999523162842ms + // default: 2ms + //default: 0.20000004768371582ms but I got a "wait or kill" dialog + + background(100, 0, 100); +} diff --git a/src/math/math.js b/src/math/math.js index f965963cfa..11c7aed170 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -93,6 +93,7 @@ function math(p5, fn) { * } */ fn.createVector = function (x, y, z) { + // TODO if (this instanceof p5) { return new p5.Vector( this._fromRadians.bind(this), diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index 51248984fd..9382549fb9 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -15,8 +15,8 @@ const smallerDimensionPriority = function(dimOther, dimSelf) { 'When working with 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 linger vector will be ignored.', ); } - return minDimension -} + return minDimension; +}; class Vector { /** @@ -51,7 +51,7 @@ class Vector { console.warn( '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.' ); - args = [0, 0, 0] + args = [0, 0, 0]; } this.values = args; diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index 6929daae73..8ac609334a 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -1,8 +1,10 @@ export default function patchVector(p5, fn, lifecycles){ // An empty vector defaults to a 3D vector. - p5.decorateHelper('createVector', function(target){ + // TODO might need p5.prototype + p5.registerDecorator('createVector', function(target){ return function(...args){ + console.log("!!!!!") 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.' @@ -14,4 +16,23 @@ export default function patchVector(p5, fn, lifecycles){ }; }); + p5.registerDecorator('p5.Vector.prototype.add', function(target){ + return function(...args){ + console.log("hi") + }; + }); + /* + add(...args) { + + // TODO Implement using decorator API to reduce duplication. + if (args[0] instanceof Vector) { + args = args[0].values; + } else if (Array.isArray(args[0])) { + args = args[0]; + } else if (args.length === 0) { + return this; + } + + */ + } diff --git a/test/bench/vectors.bench.js b/test/bench/vectors.bench.js index d8595be7c2..952c428509 100644 --- a/test/bench/vectors.bench.js +++ b/test/bench/vectors.bench.js @@ -1,36 +1,23 @@ -import {default as vector, Vector} from '../../src/math/p5.Vector.js'; - -import { bench, describe, beforeAll } from "vitest"; - -// This is not parameterizable because it's run all the time -function setupVectors() { - const n = 100; - const arr = [new Array(n)] - for (let i = 0; i < n; i++) arr[i] = new Vector(Math.random(), Math.random(), Math.random()) - return arr -} +import { Vector } from '../../src/math/p5.Vector.js'; +import { bench, describe } from "vitest"; describe("vector operations", () => { - beforeAll(function () { - vector(mockP5Prototype); - }); - bench( "mult 5", () => { const nLimited = 5; - const arr = setupVectors(); + // 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]); } } - }, - { iterations: 100 } + } ); bench( @@ -44,8 +31,7 @@ describe("vector operations", () => { const tmp = arr[i].mult(arr[j]); } } - }, - { iterations: 100 } + } ); bench( @@ -59,8 +45,7 @@ describe("vector operations", () => { const tmp = arr[i].mult(arr[j]); } } - }, - { iterations: 100 } + } ); bench( "mult 100", @@ -73,8 +58,7 @@ describe("vector operations", () => { const tmp = arr[i].mult(arr[j]); } } - }, - { iterations: 100 } + } ); @@ -90,8 +74,7 @@ describe("vector operations", () => { const tmp = arr[i].add(arr[j]); } } - }, - { iterations: 100 } + } ); bench( @@ -105,9 +88,7 @@ describe("vector operations", () => { const tmp = arr[i].add(arr[j]); } } - }, - { iterations: 100 } - ); + }); bench( "add 20", @@ -120,8 +101,7 @@ describe("vector operations", () => { const tmp = arr[i].add(arr[j]); } } - }, - { iterations: 100 } + } ); bench( "add 100", @@ -135,37 +115,6 @@ describe("vector operations", () => { } } }, - { iterations: 100 } ); - - -/** - * possible in vitest 3? - function benchMult(nLimited) { - bench( - `mult ${nLimited}`, - (arr) => { - console.log(">",arr) - console.log(">",arr.length) - for (let i = 0; i < nLimited; i++) { - for (let j = 0; j < nLimited; j++) { - const tmp = arr[i].mult(arr[j]); - } - } - }, - { - iterations: 20, - setup: () => setupVectors(10000), - } - ) - } - - benchMult(10) - //benchMult(1000) - //benchMult(2000) - //benchMult(3000) - //benchMult(4000) - - */ }); diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index e5630c2b10..63673cf02c 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -1,4 +1,5 @@ -import {default as vector, 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 { vi } from 'vitest'; // TODO add create Vector coverage @@ -6,12 +7,11 @@ import { vi } from 'vitest'; suite('p5.Vector', function () { var v; + const mockP5 = {}; const mockP5Prototype = {}; - beforeEach(async function () { - vector(mockP5Prototype); - //console.log(typeof mockP5Prototype.createVector); // Debug this - //console.log(Object.keys(mockP5Prototype)); // See what was added + beforeAll(async function () { + math(mockP5, mockP5Prototype); }); afterEach(function () {}); From 613df14708d0d8347a5ad576f0fbbbafa605ddbf Mon Sep 17 00:00:00 2001 From: ksen0 Date: Mon, 9 Mar 2026 11:11:46 +0100 Subject: [PATCH 09/14] Update vector documentation --- src/math/math.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/math/math.js b/src/math/math.js index 11c7aed170..e54a45196e 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -6,6 +6,9 @@ function math(p5, fn) { /** + * + * Testing + * * Creates a new p5.Vector object. * * A vector can be thought of in different ways. In one view, a vector is like @@ -93,16 +96,7 @@ function math(p5, fn) { * } */ fn.createVector = function (x, y, z) { - // TODO - 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); - } + return new p5.Vector(x, y, z); }; /** From ce6a0485765a6727e41bbbeacc173bd905e7c70c Mon Sep 17 00:00:00 2001 From: kit Date: Thu, 12 Mar 2026 10:14:51 +0100 Subject: [PATCH 10/14] Operation logic in helpers --- lib/empty-example/sketch.js | 90 ++--------------------------- src/math/index.js | 4 +- src/math/math.js | 4 +- src/math/p5.Vector.js | 111 +++++------------------------------- src/math/patch-vector.js | 88 ++++++++++++++++++---------- test/unit/math/p5.Vector.js | 26 ++++++++- 6 files changed, 105 insertions(+), 218 deletions(-) diff --git a/lib/empty-example/sketch.js b/lib/empty-example/sketch.js index a10ee85f04..336fa0777f 100644 --- a/lib/empty-example/sketch.js +++ b/lib/empty-example/sketch.js @@ -1,85 +1,7 @@ function setup() { - createCanvas(200, 200); - background(0, 100, 100); - noLoop(); -} - -function draw() { - -let v0, v1, v2, v3; - v0 = new p5.Vector(); - v1 = new p5.Vector([1]); - v2 = new p5.Vector([2, 3]); - v3 = new p5.Vector([4, 5, 6]); - - print('should be prioritized in add()') - v0 = v1.add(v2) - console.log(v0.x, v0.y, v0.z) - console.log(v1.add(v2).values, [2]); - console.log(v1.add(v2).dimensions, 1); - console.log(v3.add(v2).values, [8,15]); - console.log(v3.add(v2).dimensions, 2); - - print('should be prioritized in sub()') - console.log(v1.sub(v2).values, [-1]); - console.log(v1.sub(v2).dimensions, 1); - console.log(v3.sub(v2).values, [2, 2]); - console.log(v3.sub(v2).dimensions, 2); - print('should be prioritized in mult()') - console.log(v1.mult(v2).values, [2]); - console.log(v1.mult(v2).dimensions, 1); - console.log(v3.mult(v2).values, [8, 15]); - console.log(v3.mult(v2).dimensions, 2); - - print('should be prioritized in div()'); - console.log(v1.div(v2).values, [1/2]); - console.log(v1.div(v2).dimensions, 1); - console.log(v3.div(v2).values, [2, 5/3]); - console.log(v3.div(v2).dimensions, 2); - - print('should be prioritized in rem()') - console.log(v1.rem(v2).values, [1]); - console.log(v1.rem(v2).dimensions, 1); - console.log(v3.rem(v2).values, [0, 2]); - console.log(v3.rem(v2).dimensions, 2); - - return; - - - const arr_random = []; - for (let i = 0; i < 100+2; i++) { - arr_random.push(random()); - } - - const arr = []; - - // TODO: - // for(let iters = 100; iters < 5000; i+= 100) - - console.log("creating vecotrs") - console.time(); - for (let i = 0; i < 100; i++) { - arr.push(createVector(arr_random[i], arr_random[i+1], arr_random[i+2])); - } - - console.timeEnd(); - - - console.log("pairwise multiplying them vecotrs") - console.time(); - - - for (let i = 0; i < arr.length; i++) { - for (let j = 0; j < arr.length; j++) { - const _ = arr[i].mult(arr[j].mult(2)); - } - } - - console.timeEnd(); - - //default: 1.7999999523162842ms - // default: 2ms - //default: 0.20000004768371582ms but I got a "wait or kill" dialog - - background(100, 0, 100); -} + // put setup code here + } + + function draw() { + // put drawing code here + } diff --git a/src/math/index.js b/src/math/index.js index 40d8d75924..8e08dd88bd 100644 --- a/src/math/index.js +++ b/src/math/index.js @@ -4,7 +4,7 @@ import random from './random.js'; import trigonometry from './trigonometry.js'; import math from './math.js'; import vector from './p5.Vector.js'; -import patchVector from './patch-vector.js'; +import vectorValidation from './patch-vector.js'; export default function(p5){ p5.registerAddon(calculation); @@ -13,5 +13,5 @@ export default function(p5){ p5.registerAddon(trigonometry); p5.registerAddon(math); p5.registerAddon(vector); - p5.registerAddon(patchVector); + p5.registerAddon(vectorValidation); } diff --git a/src/math/math.js b/src/math/math.js index e54a45196e..89c44956a1 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -95,8 +95,8 @@ function math(p5, fn) { * point(pos); * } */ - fn.createVector = function (x, y, z) { - 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 9382549fb9..f0b9899792 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -5,14 +5,15 @@ import * as constants from '../core/constants'; -/// HELPER FOR SMALLER DIMENSION PRIORITY LOGIC. -/// Pending implementation as decorator. +/** + * This function is used by binary vector operations to prioritize shorter vectors, + * and to emit a warning when lengths do not match. + */ const smallerDimensionPriority = function(dimOther, dimSelf) { const minDimension = Math.min(dimOther, dimSelf); if (dimOther !== dimSelf) { - console.warn( - 'When working with 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 linger vector will be ignored.', + `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.` ); } return minDimension; @@ -45,15 +46,6 @@ class Vector { args = args.slice(2); } - // TODO Implement using decorator API to reduce duplication. - // Should use the same check as patchVector on 'createVector' - if(args.length === 0){ - console.warn( - '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.' - ); - args = [0, 0, 0]; - } - this.values = args; } @@ -470,17 +462,8 @@ class Vector { * @chainable */ add(...args) { - - // TODO Implement using decorator API to reduce duplication. - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 0) { - return this; - } - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] + Number(args[i]); return acc; @@ -599,35 +582,11 @@ class Vector { * @chainable */ rem(...args) { - - // TODO Implement using decorator API to reduce duplication. - - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 1) { - args = new Array(this.dimensions).fill(args[0]); - } else if (args.length === 0) { - return this; - } - - if(!args.every(v => Number.isFinite(v))){ - console.warn( - 'p5.Vector.prototype.rem', - 'Arguments contain non-finite numbers' - ); - return this; - }; - const minDimension = smallerDimensionPriority(args.length, this.dimensions); - this.values = this.values.reduce((acc, v, i) => { - // Extra check for non empty operand - if(i < minDimension && args[i] > 0) acc[i] = this.values[i] % args[i]; - else acc[i] = this.values[i] - return acc; - }, new Array(minDimension)); + this.values = Array.from({ length: minDimension }, (_, i) => { + return (args[i] > 0) ? this.values[i] % args[i] : this.values[i]; + }); return this; } @@ -752,16 +711,6 @@ class Vector { * @chainable */ sub(...args) { - - // TODO Implement using decorator API to reduce duplication. - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 0) { - return this; - } - const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { @@ -947,26 +896,6 @@ class Vector { * @chainable */ mult(...args) { - // TODO Implement using decorator API to reduce duplication. - - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 1) { - args = new Array(this.dimensions).fill(args[0]); - } else if (args.length === 0) { - return this; - } - - if(!args.every(v => Number.isFinite(v))){ - console.warn( - 'p5.Vector.prototype.mult', - 'Arguments contain non-finite numbers' - ); - return this; - }; - const minDimension = smallerDimensionPriority(args.length, this.dimensions); this.values = this.values.reduce((acc, v, i) => { @@ -1191,30 +1120,16 @@ class Vector { * @chainable */ div(...args) { + const minDimension = smallerDimensionPriority(args.length, this.dimensions); - // TODO Implement using decorator API to reduce duplication. - - if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 1) { - args = new Array(this.dimensions).fill(args[0]); - - } else if (args.length === 0) { - return this; - } - - if(!args.every(v => typeof v === 'number' && v !== 0 && Number.isFinite(v))){ + if(!args.every(v => typeof v === 'number' && v !== 0)){ console.warn( - 'p5.Vector.prototype.div:', - 'arguments contain components that are either 0 or not finite numbers' + 'p5.Vector.prototype.div', + 'Arguments contain components that are 0' ); return this; }; - const minDimension = smallerDimensionPriority(args.length, this.dimensions); - this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] / args[i]; return acc; diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index 8ac609334a..075b0aa5e5 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -1,38 +1,68 @@ -export default function patchVector(p5, fn, lifecycles){ - - // An empty vector defaults to a 3D vector. - // TODO might need p5.prototype - p5.registerDecorator('createVector', function(target){ - return function(...args){ - console.log("!!!!!") - 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.' - ); - return target.call(this, 0, 0, 0); - }else{ - return target.call(this, ...args); - } - }; - }); +import { Vector } from './p5.Vector.js'; - p5.registerDecorator('p5.Vector.prototype.add', function(target){ - return function(...args){ - console.log("hi") - }; - }); - /* - add(...args) { +/** + * @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{ + return target.call(this, ...args); + } + }; +} - // TODO Implement using decorator API to reduce duplication. - if (args[0] instanceof Vector) { + +/** + * @private + * @internal + */ +export function _validatedVectorOperation(target){ + return function(...args){ + if (args.length === 0) { + // No arguments? No action + return this; + } else if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; - } else if (args.length === 0) { - return this; + } else if (args.length === 1) { + // Solo argument? This is a special case + args = new Array(3).fill(args[0]); } - */ + if(!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.add', _validatedVectorOperation); + p5.registerDecorator('p5.Vector.prototype.sub', _validatedVectorOperation); + p5.registerDecorator('p5.Vector.prototype.mult', _validatedVectorOperation); + + p5.registerDecorator('p5.Vector.prototype.rem', _validatedVectorOperation); + p5.registerDecorator('p5.Vector.prototype.div', _validatedVectorOperation); } diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index 63673cf02c..a28717be49 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -1,8 +1,8 @@ -import {Vector} from '../../../src/math/p5.Vector.js'; -import {default as math} from '../../../src/math/math.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'; -// TODO add create Vector coverage suite('p5.Vector', function () { var v; @@ -11,7 +11,27 @@ suite('p5.Vector', function () { const 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(Vector.prototype.add); + Vector.prototype.sub = _validatedVectorOperation(Vector.prototype.sub); + Vector.prototype.mult = _validatedVectorOperation(Vector.prototype.mult); + Vector.prototype.rem = _validatedVectorOperation(Vector.prototype.rem); + Vector.prototype.div = _validatedVectorOperation(Vector.prototype.div); + + globalThis.p5 = { + _friendlyError: function(msg, func) { + console.warn(msg); + } + }; }); afterEach(function () {}); From a84505e6eefdde1de551370da81090c2f35a9858 Mon Sep 17 00:00:00 2001 From: kit Date: Wed, 18 Mar 2026 13:04:36 +0100 Subject: [PATCH 11/14] shared abstraction for vector validation --- src/math/math.js | 12 ++++++- src/math/p5.Vector.js | 20 ++++++++---- src/math/patch-vector.js | 65 ++++++++++++++++++++++--------------- src/webgl/loading.js | 2 +- src/webgl/p5.Camera.js | 8 ++--- src/webgl/p5.Geometry.js | 2 +- test/unit/math/p5.Vector.js | 16 ++++----- 7 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/math/math.js b/src/math/math.js index 89c44956a1..4371585b96 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -96,7 +96,17 @@ function math(p5, fn) { * } */ fn.createVector = function (...args) { - return new p5.Vector(...args); + + // TODO MOVE PASSING THESE FUNCTIONS TO VECTOR + if (this instanceof p5) { + return new p5.Vector( + this._fromRadians.bind(this), + this._toRadians.bind(this), + ...arguments + ); + } else { + return new p5.Vector(...args); + } }; /** diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index f0b9899792..45a8d9623f 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -9,7 +9,8 @@ import * as constants from '../core/constants'; * This function is used by binary vector operations to prioritize shorter vectors, * and to emit a warning when lengths do not match. */ -const smallerDimensionPriority = function(dimOther, dimSelf) { +const smallerDimensionPriority = function(dimOther, dimSelf, args) { + console.log("sDP", args); const minDimension = Math.min(dimOther, dimSelf); if (dimOther !== dimSelf) { console.warn( @@ -39,6 +40,11 @@ class Vector { // This is how it comes in with createVector() // This check if the first argument is a function constructor(...args) { + + + // not meant to be userfacing so requires valid input + // TODO throw error when no input args + if (typeof args[0] === 'function') { this.isPInst = true; this._fromRadians = args[0]; @@ -46,6 +52,8 @@ class Vector { args = args.slice(2); } + // todo at this point must be numbers + this.values = args; } @@ -462,7 +470,7 @@ class Vector { * @chainable */ add(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] + Number(args[i]); @@ -582,7 +590,7 @@ class Vector { * @chainable */ rem(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); this.values = Array.from({ length: minDimension }, (_, i) => { return (args[i] > 0) ? this.values[i] % args[i] : this.values[i]; @@ -711,7 +719,7 @@ class Vector { * @chainable */ sub(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] - args[i]; @@ -896,7 +904,7 @@ class Vector { * @chainable */ mult(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] * args[i]; @@ -1120,7 +1128,7 @@ class Vector { * @chainable */ div(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions); + const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); if(!args.every(v => typeof v === 'number' && v !== 0)){ console.warn( diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index 075b0aa5e5..cd5b8968e9 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -13,6 +13,9 @@ export function _defaultEmptyVector(target){ ); return target.call(this, 0, 0, 0); }else{ + if (Array.isArray(args[0])) { + args = args[0]; + } return target.call(this, ...args); } }; @@ -23,29 +26,37 @@ export function _defaultEmptyVector(target){ * @private * @internal */ -export function _validatedVectorOperation(target){ - return function(...args){ - if (args.length === 0) { - // No arguments? No action - return this; - } else if (args[0] instanceof Vector) { - args = args[0].values; - } else if (Array.isArray(args[0])) { - args = args[0]; - } else if (args.length === 1) { - // Solo argument? This is a special case - args = new Array(3).fill(args[0]); - } +export function _validatedVectorOperation(expectsSoloNumberArgument){ + return function(target){ + return function(...args){ + console.log("vVO", target.name, args); + if (args.length === 0) { + // No arguments? No action + return this; + } else if (args[0] instanceof Vector) { + args = args[0].values; + } else if (Array.isArray(args[0])) { + args = args[0]; + } else if (args.length === 1) { + console.log("A") + if (expectsSoloNumberArgument){ + console.log("b") + // && typeof args[0] === 'number' && Number.isFinite(args[0]) + // Special case handling for a solo numeric argument + args = new Array(3).fill(args[0]); + } + } // (1,2,3) ...args is 1,2,3 - if(!args.every(v => typeof v === 'number' && Number.isFinite(v))){ - p5._friendlyError( - 'Arguments contain non-finite numbers', - target.name - ); - return this; - }; + 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); + return target.call(this, ...args); + }; }; } @@ -57,12 +68,14 @@ export function _validatedVectorOperation(target){ 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.add', _validatedVectorOperation); - p5.registerDecorator('p5.Vector.prototype.sub', _validatedVectorOperation); - p5.registerDecorator('p5.Vector.prototype.mult', _validatedVectorOperation); + p5.registerDecorator(function(path){ + return ['p5.Vector.prototype.add', 'p5.Vector.prototype.sub'].includes(path); + }, _validatedVectorOperation(false)); - p5.registerDecorator('p5.Vector.prototype.rem', _validatedVectorOperation); - p5.registerDecorator('p5.Vector.prototype.div', _validatedVectorOperation); + p5.registerDecorator(function(path){ + return ['p5.Vector.prototype.rem', 'p5.Vector.prototype.div'].includes(path); + }, _validatedVectorOperation(true)); } diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 1e804a4c4a..d607fb4092 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -609,7 +609,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/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index a28717be49..537ff1a110 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -23,9 +23,9 @@ suite('p5.Vector', function () { // The following mocks simulate the validation decorator Vector.prototype.add = _validatedVectorOperation(Vector.prototype.add); Vector.prototype.sub = _validatedVectorOperation(Vector.prototype.sub); - Vector.prototype.mult = _validatedVectorOperation(Vector.prototype.mult); - Vector.prototype.rem = _validatedVectorOperation(Vector.prototype.rem); - Vector.prototype.div = _validatedVectorOperation(Vector.prototype.div); + Vector.prototype.mult = _validatedVectorOperationtk(Vector.prototype.mult); + Vector.prototype.rem = _validatedVectorOperationtk(Vector.prototype.rem); + Vector.prototype.div = _validatedVectorOperationjt(Vector.prototype.div); globalThis.p5 = { _friendlyError: function(msg, func) { @@ -1386,7 +1386,7 @@ suite('p5.Vector', function () { suite('heading', function () { beforeEach(function () { - v = new Vector(); + v = new Vector(0,0,0); }); suite('p5.Vector.prototype.heading() [INSTANCE]', function () { @@ -1830,9 +1830,9 @@ suite('p5.Vector', function () { incoming_y, incoming_z ); - x_target = new Vector(); - y_target = new Vector(); - z_target = new 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 Vector(3, 0, 0); y_normal = new Vector(0, 3, 0); @@ -2131,8 +2131,6 @@ suite('p5.Vector', function () { it('should throw error if trying to set w if vector dimensions is less than 4', () => { 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 }); }); From b2c51850dd7e33ab5008fbac201cc61d5570cab0 Mon Sep 17 00:00:00 2001 From: kit Date: Wed, 25 Mar 2026 13:11:00 +0100 Subject: [PATCH 12/14] Vector tests pass --- src/math/math.js | 12 +--- src/math/p5.Vector.js | 105 +++++++++++++++++++++++------------ src/math/patch-vector.js | 28 ++++------ src/webgl/interaction.js | 9 ++- test/unit/math/p5.Vector.js | 78 +++++++++++++++----------- test/unit/webgl/p5.Shader.js | 4 +- 6 files changed, 137 insertions(+), 99 deletions(-) diff --git a/src/math/math.js b/src/math/math.js index 4371585b96..89c44956a1 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -96,17 +96,7 @@ function math(p5, fn) { * } */ fn.createVector = function (...args) { - - // TODO MOVE PASSING THESE FUNCTIONS TO VECTOR - if (this instanceof p5) { - return new p5.Vector( - this._fromRadians.bind(this), - this._toRadians.bind(this), - ...arguments - ); - } else { - return new p5.Vector(...args); - } + return new p5.Vector(...args); }; /** diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index 45a8d9623f..32cf84a98b 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -9,31 +9,44 @@ import * as constants from '../core/constants'; * This function is used by binary vector operations to prioritize shorter vectors, * and to emit a warning when lengths do not match. */ -const smallerDimensionPriority = function(dimOther, dimSelf, args) { - console.log("sDP", args); - const minDimension = Math.min(dimOther, dimSelf); - if (dimOther !== dimSelf) { - console.warn( - `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.` - ); - } - return minDimension; +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; }; + class Vector { /** - * The values of the N-dimensional 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 = []; @@ -41,9 +54,11 @@ class Vector { // This check if the first argument is a function constructor(...args) { - - // not meant to be userfacing so requires valid input - // TODO throw error when no input args + if (args.length === 0) { + p5._friendlyError( + 'Requires valid arguments.', 'p5.Vector' + ); + } if (typeof args[0] === 'function') { this.isPInst = true; @@ -52,9 +67,15 @@ class Vector { args = args.slice(2); } - // todo at this point must be numbers - - this.values = args; + 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.values = args; + } } get dimensions(){ @@ -352,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. @@ -470,7 +494,7 @@ class Vector { * @chainable */ add(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); + 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]); @@ -491,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. @@ -590,7 +618,7 @@ class Vector { * @chainable */ rem(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions, 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]; @@ -606,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. @@ -719,7 +750,7 @@ class Vector { * @chainable */ sub(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); + const minDimension = prioritizeSmallerDimension(this.dimensions, args); this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] - args[i]; @@ -737,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. @@ -904,7 +938,7 @@ class Vector { * @chainable */ mult(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); + const minDimension = prioritizeSmallerDimension(this.dimensions, args); this.values = this.values.reduce((acc, v, i) => { if(i < minDimension) acc[i] = this.values[i] * args[i]; @@ -960,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. @@ -1128,7 +1165,7 @@ class Vector { * @chainable */ div(...args) { - const minDimension = smallerDimensionPriority(args.length, this.dimensions, args); + const minDimension = prioritizeSmallerDimension(this.dimensions, args); if(!args.every(v => typeof v === 'number' && v !== 0)){ console.warn( diff --git a/src/math/patch-vector.js b/src/math/patch-vector.js index cd5b8968e9..8216c822e0 100644 --- a/src/math/patch-vector.js +++ b/src/math/patch-vector.js @@ -29,23 +29,19 @@ export function _defaultEmptyVector(target){ export function _validatedVectorOperation(expectsSoloNumberArgument){ return function(target){ return function(...args){ - console.log("vVO", target.name, 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 (args.length === 1) { - console.log("A") - if (expectsSoloNumberArgument){ - console.log("b") - // && typeof args[0] === 'number' && Number.isFinite(args[0]) - // Special case handling for a solo numeric argument - args = new Array(3).fill(args[0]); - } - } // (1,2,3) ...args is 1,2,3 + } 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( @@ -69,13 +65,9 @@ export default function vectorValidation(p5, fn, lifecycles){ p5.registerDecorator('p5.prototype.createVector', _defaultEmptyVector); p5.registerDecorator('p5.Vector.prototype.mult', _validatedVectorOperation(true)); - - p5.registerDecorator(function(path){ - return ['p5.Vector.prototype.add', 'p5.Vector.prototype.sub'].includes(path); - }, _validatedVectorOperation(false)); - - p5.registerDecorator(function(path){ - return ['p5.Vector.prototype.rem', 'p5.Vector.prototype.div'].includes(path); - }, _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/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js index 537ff1a110..d0fe08bce5 100644 --- a/test/unit/math/p5.Vector.js +++ b/test/unit/math/p5.Vector.js @@ -21,14 +21,16 @@ suite('p5.Vector', function () { ); // The following mocks simulate the validation decorator - Vector.prototype.add = _validatedVectorOperation(Vector.prototype.add); - Vector.prototype.sub = _validatedVectorOperation(Vector.prototype.sub); - Vector.prototype.mult = _validatedVectorOperationtk(Vector.prototype.mult); - Vector.prototype.rem = _validatedVectorOperationtk(Vector.prototype.rem); - Vector.prototype.div = _validatedVectorOperationjt(Vector.prototype.div); + 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); } }; @@ -100,7 +102,7 @@ suite('p5.Vector', function () { }); - suite('p5.prototype.createVector()', function () { + suite.todo('p5.prototype.createVector()', function () { beforeEach(function () { v = mockP5Prototype.createVector(); }); @@ -148,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 Vector(1, 2, undefined); + v = new Vector(1, 2); }); test('should have x, y, z be initialized to 1,2,0', function () { @@ -542,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 () { @@ -609,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', () => { @@ -1013,47 +1019,51 @@ suite('p5.Vector', function () { suite('smaller dimension', function () { - let v0, v1, v2, v3; + let v1, v2, v3; beforeEach(function () { - v0 = new Vector(); - v1 = new Vector([1]); - v2 = new Vector([2, 3]); - v3 = new Vector([4, 5, 6]); + 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, [2]); - assert.deepEqual(v1.add(v2).dimension, 1); - assert.deepEqual(v3.add(v2).values, [8,15]); - assert.deepEqual(v3.add(v2).dimension, 2); + 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]); - assert.deepEqual(v1.sub(v2).dimension, 1); + expect(v1.sub(v2).dimensions).to.eql(1); + assert.deepEqual(v3.sub(v2).values, [2, 2]); - assert.deepEqual(v3.sub(v2).dimension, 2); + expect(v3.sub(v2).dimensions).to.eql(2); }); test('should be prioritized in mult()', function () { assert.deepEqual(v1.mult(v2).values, [2]); - assert.deepEqual(v1.mult(v2).dimension, 1); + expect(v1.mult(v2).dimensions).to.eql(1); + assert.deepEqual(v3.mult(v2).values, [8, 15]); - assert.deepEqual(v3.mult(v2).dimension, 2); + expect(v3.mult(v2).dimensions).to.eql(2); }); test('should be prioritized in div()', function () { assert.deepEqual(v1.div(v2).values, [1/2]); - assert.deepEqual(v1.div(v2).dimension, 1); + expect(v1.div(v2).dimensions).to.eql(1); + assert.deepEqual(v3.div(v2).values, [2, 5/3]); - assert.deepEqual(v3.div(v2).dimension, 2); + expect(v3.div(v2).dimensions).to.eql(2); }); test('should be prioritized in rem()', function () { assert.deepEqual(v1.rem(v2).values, [1]); - assert.deepEqual(v1.rem(v2).dimension, 1); + expect(v1.rem(v2).dimensions).to.eql(1); + assert.deepEqual(v3.rem(v2).values, [0, 2]); - assert.deepEqual(v3.rem(v2).dimension, 2); + expect(v3.rem(v2).dimensions).to.eql(2); }); }); @@ -2078,11 +2088,12 @@ suite('p5.Vector', function () { 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 Vector(1, 2, 3, 4); - assert.equal(vect.getValue(5), 1); + globalThis.FESCalled = false; + assert.equal(vect.getValue(5), undefined); + assert.equal(globalThis.FESCalled, true); } ); }); @@ -2097,11 +2108,12 @@ 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 Vector(1, 2, 3, 4); + globalThis.FESCalled = false; vect.setValue(100, 7); + assert.equal(globalThis.FESCalled, true); } ); }); diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index 6241c88973..c1c45039d7 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -2091,7 +2091,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'); From 719b4bd62b9522fe36335cfe575b04ad7738593b Mon Sep 17 00:00:00 2001 From: kit Date: Wed, 25 Mar 2026 14:30:18 +0100 Subject: [PATCH 13/14] Update createVector doc --- src/math/math.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/math/math.js b/src/math/math.js index 89c44956a1..0cfbb593bb 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -6,9 +6,6 @@ function math(p5, fn) { /** - * - * Testing - * * Creates a new p5.Vector object. * * A vector can be thought of in different ways. In one view, a vector is like @@ -41,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 From 32e351a15d422ef5ca052ee114e29716dfe0ae29 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Wed, 25 Mar 2026 17:55:58 +0000 Subject: [PATCH 14/14] Prevent minification mangling from messing up decoration --- src/core/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); }