Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 113 additions & 22 deletions src/math/p5.Vector.js
Original file line number Diff line number Diff line change
Expand Up @@ -2314,63 +2314,149 @@ class Vector {
}

/**
* Calculates new `x`, `y`, and `z` components that are proportionally the
* Calculates new vector components that are proportionally the
* same distance between two vectors.
*
* `lerp()` can use separate numbers, as in `v.lerp(3, 3, 0.5)`, another
* <a href="#/p5.Vector">p5.Vector</a> object, as in `v.lerp(v2, 0.5)`, or an
* array of numbers, as in `v.lerp([3, 3], 0.5)`. When using numbers, the last
* argument is always the interpolation amount.
*
* The `amt` parameter is the amount to interpolate between the old vector and
* the new vector. 0.0 keeps all components equal to the old vector's, 0.5 is
* halfway between, and 1.0 sets all components equal to the new vector's.
* Values of `amt` outside the range 0 to 1 are allowed and extrapolate beyond
* the target.
*
* This method supports N-dimensional vectors. You should interpolate vectors
* only when they are the same size. When two vectors of different sizes are
* used, the smaller dimension will be used, and any additional values of the
* longer vector will be ignored.
*
* The static version of `lerp()`, as in `p5.Vector.lerp(v0, v1, 0.5)`,
* returns a new <a href="#/p5.Vector">p5.Vector</a> object and doesn't change
* the original.
* the originals.
*
* @param {Number} x x component.
* @param {Number} y y component.
* @param {Number} z z component.
* @param {Number} amt amount of interpolation between 0.0 (old vector)
* and 1.0 (new vector). 0.5 is halfway between.
* @param {...Number} values target vector components
* @param {Number} amt amount of interpolation between 0.0 (old vector)
* and 1.0 (new vector). 0.5 is halfway between.
* @chainable
*
* @example
* // META:norender
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create a p5.Vector object.
* let v0 = createVector(1, 1, 1);
* let v1 = createVector(3, 3, 3);
*
* // Interpolate.
* v0.lerp(v1, 0.5);
*
* // Prints "p5.Vector Object : [2, 2, 2]" to the console.
* print(v0.toString());
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v0.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [2, 2, 2]" written on a gray square.');
* }
*
* @example
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create a p5.Vector object.
* let v = createVector(1, 1);
*
* // Interpolate with numbers. The last argument is the amount.
* v.lerp(3, 3, 0.5);
*
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [2, 2]" written on a gray square.');
* }
*
* @example
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create a p5.Vector object.
* let v = createVector(1, 1, 1);
*
* // Interpolate with an array.
* v.lerp([3, 3, 3], 0.5);
*
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [2, 2, 2]" written on a gray square.');
* }
*
* @example
* // META:norender
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create a p5.Vector object.
* let v = createVector(1, 1, 1);
*
* // Interpolate.
* v.lerp(3, 3, 3, 0.5);
*
* // Prints "p5.Vector Object : [2, 2, 2]" to the console.
* print(v.toString());
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [2, 2, 2]" written on a gray square.');
* }
*
* @example
* // META:norender
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create p5.Vector objects.
* let v0 = createVector(1, 1, 1);
* let v1 = createVector(3, 3, 3);
*
* // Interpolate.
* let v2 = p5.Vector.lerp(v0, v1, 0.5);
*
* // Prints "p5.Vector Object : [2, 2, 2]" to the console.
* print(v2.toString());
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v2.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [2, 2, 2]" written on a gray square.');
* }
*
* @example
* function setup() {
* createCanvas(100, 100);
*
* background(200);
*
* // Create p5.Vector objects.
* let v0 = createVector(0, 1, 0, 1);
* let v1 = createVector(1, 0, 1, 0);
*
* // Interpolate.
* v0.lerp(v1, 0.5);
*
* // Display the result.
* textAlign(CENTER, CENTER);
* text(v0.toString(), 0, 0, width, height);
*
* describe('The text "p5.Vector Object : [0.5, 0.5, 0.5, 0.5]" written on a gray square.');
* }
*
* @example
Expand Down Expand Up @@ -2415,18 +2501,23 @@ class Vector {
* pop();
* }
*/
/**
* @param {Number[]} arr array to lerp towards.
* @param {Number} amt
* @chainable
*/
/**
* @param {p5.Vector} v <a href="#/p5.Vector">p5.Vector</a> to lerp toward.
* @param {Number} amt
* @chainable
*/
lerp(x, y, z, amt) {
if (x instanceof Vector) {
return this.lerp(x.x, x.y, x.z, y);
lerp(values, amt) {
const minDimension = prioritizeSmallerDimension(this.dimensions, values);
shrinkToDimension(this.values, minDimension);

for (let i = 0; i < this.values.length; i++) {
this.values[i] += (values[i] - this.values[i]) * amt;
}
this.x += (x - this.x) * amt || 0;
this.y += (y - this.y) * amt || 0;
this.z += (z - this.z) * amt || 0;
return this;
}

Expand Down
85 changes: 67 additions & 18 deletions src/math/patch-vector.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Vector } from './p5.Vector.js';

function _vectorError(instance, message = 'Requires valid arguments') {
if (!instance.friendlyErrorsDisabled()) {
instance._friendlyError(message, 'p5.Vector');
}

return instance;
}

/**
* @private
* @internal
Expand All @@ -25,17 +33,69 @@ export function _defaultEmptyVector(target){
/**
* @private
* @internal
* @param {Boolean} expectsSoloNumberArgument whether a single number scales
* every component (mult/div/rem)
* @param {Number} [trailingArgCount=0] number of trailing args to peel off
* before treating the rest as vector components (e.g. 1 for lerp's amt)
* @param {Number[]} [trailingDefaults] if provided, used when only component
* values are passed (no trailing args), e.g. `[0]` so `v.lerp(other)` uses amt 0
*/
export function _validatedVectorOperation(expectsSoloNumberArgument){
export function _validatedVectorOperation(
expectsSoloNumberArgument,
trailingArgCount = 0,
trailingDefaults
){
return function(target){
return function (...args) {
if (args.length === 0) {
const trailing = [];

if (trailingArgCount > 0) {
const onlyValues =
args.length === 1 &&
(args[0] instanceof Vector || Array.isArray(args[0]));

if (onlyValues && trailingDefaults) {
for (let i = 0; i < trailingDefaults.length; i++) {
trailing.push(trailingDefaults[i]);
}
} else if (args.length < trailingArgCount) {
if (trailingDefaults && args.length === 0) {
// No arguments? No action (same as other vector ops)
return this;
}
return _vectorError(this);
} else {
for (let i = 0; i < trailingArgCount; i++) {
trailing.unshift(args.pop());
}

for (let i = 0; i < trailing.length; i++) {
const t = trailing[i];
if (typeof t !== 'number' || !Number.isFinite(t)) {
return _vectorError(this, 'Arguments contain non-finite numbers');
}
}

if (args.length === 0) {
return _vectorError(this);
}
}
} else if (args.length === 0) {
// No arguments? No action
return this;
} else if (args[0] instanceof Vector) {
}

if (args[0] instanceof Vector) {
// Do not allow extra args after a vector when trailing args are used
if (trailingArgCount > 0 && args.length > 1) {
return _vectorError(this);
}
// First argument is a vector? Make it an array
args = args[0].values;
} else if (Array.isArray(args[0])) {
if (trailingArgCount > 0 && args.length > 1) {
return _vectorError(this);
}
// First argument is an array? Great, keep it!
args = args[0];
} else if (args.length === 1){
Expand All @@ -49,28 +109,16 @@ export function _validatedVectorOperation(expectsSoloNumberArgument){
for (let i = 0; i < args.length; i++) {
const v = args[i];
if (typeof v !== 'number' || !Number.isFinite(v)) {
if (!this.friendlyErrorsDisabled()) {
this._friendlyError(
'Arguments contain non-finite numbers',
'p5.Vector'
);
}
return this;
return _vectorError(this, 'Arguments contain non-finite numbers');
}
}
} else {
if (typeof args !== 'number' || !Number.isFinite(args)) {
if (!this.friendlyErrorsDisabled()) {
this._friendlyError(
'Arguments contain non-finite numbers',
'p5.Vector'
);
}
return this;
return _vectorError(this, 'Arguments contain non-finite numbers');
}
}

return target.call(this, args);
return target.call(this, args, ...trailing);
};
};
}
Expand All @@ -90,5 +138,6 @@ export default function vectorValidation(p5, fn, lifecycles){
p5.registerDecorator('p5.Vector.prototype.div', _validatedVectorOperation(true));
p5.registerDecorator('p5.Vector.prototype.add', _validatedVectorOperation(false));
p5.registerDecorator('p5.Vector.prototype.sub', _validatedVectorOperation(false));
p5.registerDecorator('p5.Vector.prototype.lerp', _validatedVectorOperation(false, 1, [0]));

}
Loading
Loading