UNPKG

p5

Version:

[![npm version](https://badge.fury.io/js/p5.svg)](https://www.npmjs.com/package/p5)

1,816 lines (1,781 loc) 107 kB
import { f as TWO_PI } from '../constants-BRcElHU3.js'; /** * @module Math * @requires 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; } return this; }; class Vector { // 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<number>} 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<number>} 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(); } } /** * Gets the x component of the vector. * * This method returns the value of the x component of the vector. * Think of the x component as the horizontal position or the first number in the vector. * If the x component is not defined, it will return 0. * * @returns {Number} The x component of the vector. Returns 0 if the value is not defined. */ get x() { return this._values[0] || 0; } /** * Retrieves the value at the specified index from the vector. * * This method allows you to get the value of a specific component of the vector * by providing its index. Think of the vector as a list of numbers, where each * number represents a different direction (like x, y, or z). The index is just * the position of the number in that list. * * For example, if you have a vector with values 10, 20, 30 the index 0 would * give you the first value 10, index 1 would give you the second value 20, * and so on. * * @param {Number} index - The position of the value you want to get from the vector. * @returns {Number} The value at the specified position in the vector. * @throws Will throw an error if the index is out of bounds, meaning if you try to * get a value from a position that doesn't exist in the vector. */ getValue(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", "p5.Vector.setValue" ); } } /** * Sets the value at the specified index of the vector. * * This method allows you to change a specific component of the vector by providing its index and the new value you want to set. * Think of the vector as a list of numbers, where each number represents a different direction (like x, y, or z). * The index is just the position of the number in that list. * * For example, if you have a vector with values [0, 20, 30], and you want to change the second value (20) to 50, * you would use this method with index 1 (since indexes start at 0) and value 50. * * @param {Number} index - The position in the vector where you want to set the new value. * @param {Number} value - The new value you want to set at the specified position. * @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; } else { p5._friendlyError( "The index parameter is trying to set a value outside the bounds of the vector", "p5.Vector.setValue" ); } } /** * Gets the y component of the vector. * * This method returns the value of the y component of the vector. * Think of the y component as the vertical position or the second number in the vector. * If the y component is not defined, it will return 0. * * @returns {Number} The y component of the vector. Returns 0 if the value is not defined. */ get y() { return this._values[1] || 0; } /** * Gets the z component of the vector. * * This method returns the value of the z component of the vector. * Think of the z component as the depth or the third number in the vector. * If the z component is not defined, it will return 0. * * @returns {Number} The z component of the vector. Returns 0 if the value is not defined. */ get z() { return this._values[2] || 0; } /** * Gets the w component of the vector. * * This method returns the value of the w component of the vector. * Think of the w component as the fourth number in the vector. * If the w component is not defined, it will return 0. * * @returns {Number} The w component of the vector. Returns 0 if the value is not defined. */ get w() { return this._values[3] || 0; } /** * Sets the x component of the vector. * * This method allows you to change the x value of the vector. * The x value is the first number in the vector, representing the horizontal position. * By calling this method, you can update the x value to a new number. * * @param {Number} xVal - The new value for the x component. */ set x(xVal) { if (this._values.length > 1) { this._values[0] = xVal; } } /** * Sets the y component of the vector. * * This method allows you to change the y value of the vector. * The y value is the second number in the vector, representing the vertical position. * By calling this method, you can update the y value to a new number. * * @param {Number} yVal - The new value for the y component. */ set y(yVal) { if (this._values.length > 1) { this._values[1] = yVal; } } /** * Sets the z component of the vector. * * This method allows you to change the z value of the vector. * The z value is the third number in the vector, representing the depth or the third dimension. * By calling this method, you can update the z value to a new number. * * @param {Number} zVal - The new value for the z component. */ set z(zVal) { if (this._values.length > 2) { this._values[2] = zVal; } } /** * Sets the w component of the vector. * * This method allows you to change the w value of the vector. * The w value is the fourth number in the vector, representing the fourth dimension. * By calling this method, you can update the w value to a new number. * * @param {Number} wVal - The new value for the w component. */ set w(wVal) { if (this._values.length > 3) { this._values[3] = wVal; } } /** * Returns a string representation of a vector. * * Calling `toString()` is useful for printing vectors to the console while * debugging. * * @return {String} string representation of the vector. * * @example * <div class = "norender"> * <code> * function setup() { * let v = createVector(20, 30); * * // Prints 'p5.Vector Object : [20, 30, 0]'. * print(v.toString()); * } * </code> * </div> */ toString() { return `[${this.values.join(", ")}]`; } /** * Sets the vector's `x`, `y`, and `z` components. * * `set()` can use separate numbers, as in `v.set(1, 2, 3)`, a * <a href="#/p5.Vector">p5.Vector</a> object, as in `v.set(v2)`, or an * array of numbers, as in `v.set([1, 2, 3])`. * * If a value isn't provided for a component, it will be set to 0. For * example, `v.set(4, 5)` sets `v.x` to 4, `v.y` to 5, and `v.z` to 0. * Calling `set()` with no arguments, as in `v.set()`, sets all the vector's * components to 0. * * @param {Number} [x] x component of the vector. * @param {Number} [y] y component of the vector. * @param {Number} [z] z component of the vector. * @chainable * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top left. * let pos = createVector(25, 25); * point(pos); * * // Top right. * // set() with numbers. * pos.set(75, 25); * point(pos); * * // Bottom right. * // set() with a p5.Vector. * let p2 = createVector(75, 75); * pos.set(p2); * point(pos); * * // Bottom left. * // set() with an array. * let arr = [25, 75]; * pos.set(arr); * point(pos); * * describe('Four black dots arranged in a square on a gray background.'); * } * </code> * </div> */ /** * @param {p5.Vector|Number[]} value vector to set. * @chainable */ set(...args) { if (args[0] instanceof Vector) { this.values = args[0].values.slice(); } else if (Array.isArray(args[0])) { this.values = args[0].map((arg) => arg || 0); } else { this.values = args.map((arg) => arg || 0); } this.dimensions = this.values.length; return this; } /** * Returns a copy of the <a href="#/p5.Vector">p5.Vector</a> object. * * @return {p5.Vector} copy of the <a href="#/p5.Vector">p5.Vector</a> object. * * @example * <div> * <code> * function setup() { * createCanvas(100 ,100); * * background(200); * * // Create a p5.Vector object. * let pos = createVector(50, 50); * * // Make a copy. * let pc = pos.copy(); * * // Draw the point. * strokeWeight(5); * point(pc); * * describe('A black point drawn in the middle of a gray square.'); * } * </code> * </div> */ copy() { if (this.isPInst) { return new Vector(this._fromRadians, this._toRadians, ...this.values); } else { return new Vector(...this.values); } } /** * Adds to a vector's components. * * `add()` can use separate numbers, as in `v.add(1, 2, 3)`, * another <a href="#/p5.Vector">p5.Vector</a> 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`. * Calling `add()` with no arguments, as in `v.add()`, has no effect. * * This method supports N-dimensional vectors. * * The static version of `add()`, as in `p5.Vector.add(v2, v1)`, returns a new * <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * originals. * * @param {Number|Array} x x component of the vector to be added or an array of components. * @param {Number} [y] y component of the vector to be added. * @param {Number} [z] z component of the vector to be added. * @chainable * * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top left. * let pos = createVector(25, 25); * point(pos); * * // Top right. * // Add numbers. * pos.add(50, 0); * point(pos); * * // Bottom right. * // Add a p5.Vector. * let p2 = createVector(0, 50); * pos.add(p2); * point(pos); * * // Bottom left. * // Add an array. * let arr = [-50, 0]; * pos.add(arr); * point(pos); * * describe('Four black dots arranged in a square on a gray background.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Top left. * let p1 = createVector(25, 25); * * // Center. * let p2 = createVector(50, 50); * * // Bottom right. * // Add p1 and p2. * let p3 = p5.Vector.add(p1, p2); * * // Draw the points. * strokeWeight(5); * point(p1); * point(p2); * point(p3); * * describe('Three black dots in a diagonal line from top left to bottom right.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe('Three arrows drawn on a gray square. A red arrow extends from the top left corner to the center. A blue arrow extends from the tip of the red arrow. A purple arrow extends from the origin to the tip of the blue arrow.'); * } * * function draw() { * background(200); * * let origin = createVector(0, 0); * * // Draw the red arrow. * let v1 = createVector(50, 50); * drawArrow(origin, v1, 'red'); * * // Draw the blue arrow. * let v2 = createVector(-30, 20); * drawArrow(v1, v2, 'blue'); * * // Purple arrow. * let v3 = p5.Vector.add(v1, v2); * drawArrow(origin, v3, 'purple'); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ /** * @param {p5.Vector|Number[]} value The vector to add * @chainable */ add(...args) { if (args[0] instanceof Vector) { args = args[0].values; } else if (Array.isArray(args[0])) { args = args[0]; } args.forEach((value, index) => { this.values[index] = (this.values[index] || 0) + (value || 0); }); return this; } /** * Performs modulo (remainder) division with a vector's `x`, `y`, and `z` * components. * * `rem()` can use separate numbers, as in `v.rem(1, 2, 3)`, * another <a href="#/p5.Vector">p5.Vector</a> object, as in `v.rem(v2)`, or * 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 * arguments, as in `v.rem()`, has no effect. * * The static version of `rem()`, as in `p5.Vector.rem(v2, v1)`, returns a * new <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * originals. * * @param {Number} x x component of divisor vector. * @param {Number} y y component of divisor vector. * @param {Number} z z component of divisor vector. * @chainable * * @example * <div class='norender'> * <code> * function setup() { * // Create a p5.Vector object. * let v = createVector(3, 4, 5); * * // Divide numbers. * v.rem(2); * * // Prints 'p5.Vector Object : [1, 0, 1]'. * print(v.toString()); * } * </code> * </div> * * <div class='norender'> * <code> * function setup() { * // Create a p5.Vector object. * let v = createVector(3, 4, 5); * * // Divide numbers. * v.rem(2, 3); * * // Prints 'p5.Vector Object : [1, 1, 5]'. * print(v.toString()); * } * </code> * </div> * * <div class='norender'> * <code> * function setup() { * // Create a p5.Vector object. * let v = createVector(3, 4, 5); * * // Divide numbers. * v.rem(2, 3, 4); * * // Prints 'p5.Vector Object : [1, 1, 1]'. * print(v.toString()); * } * </code> * </div> * * <div class='norender'> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(3, 4, 5); * let v2 = createVector(2, 3, 4); * * // Divide a p5.Vector. * v1.rem(v2); * * // Prints 'p5.Vector Object : [1, 1, 1]'. * print(v1.toString()); * } * </code> * </div> * * <div class='norender'> * <code> * function setup() { * // Create a p5.Vector object. * let v = createVector(3, 4, 5); * * // Divide an array. * let arr = [2, 3, 4]; * v.rem(arr); * * // Prints 'p5.Vector Object : [1, 1, 1]'. * print(v.toString()); * } * </code> * </div> * * <div class="norender"> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(3, 4, 5); * let v2 = createVector(2, 3, 4); * * // Divide without modifying the original vectors. * let v3 = p5.Vector.rem(v1, v2); * * // Prints 'p5.Vector Object : [1, 1, 1]'. * print(v3.toString()); * } * </code> * </div> */ /** * @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] ); } } } } /** * Subtracts from a vector's `x`, `y`, and `z` components. * * `sub()` can use separate numbers, as in `v.sub(1, 2, 3)`, another * <a href="#/p5.Vector">p5.Vector</a> 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. * * The static version of `sub()`, as in `p5.Vector.sub(v2, v1)`, returns a new * <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * originals. * * @param {Number} x x component of the vector to subtract. * @param {Number} [y] y component of the vector to subtract. * @param {Number} [z] z component of the vector to subtract. * @chainable * * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Bottom right. * let pos = createVector(75, 75); * point(pos); * * // Top right. * // Subtract numbers. * pos.sub(0, 50); * point(pos); * * // Top left. * // Subtract a p5.Vector. * let p2 = createVector(50, 0); * pos.sub(p2); * point(pos); * * // Bottom left. * // Subtract an array. * let arr = [0, -50]; * pos.sub(arr); * point(pos); * * describe('Four black dots arranged in a square on a gray background.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create p5.Vector objects. * let p1 = createVector(75, 75); * let p2 = createVector(50, 50); * * // Subtract with modifying the original vectors. * let p3 = p5.Vector.sub(p1, p2); * * // Draw the points. * strokeWeight(5); * point(p1); * point(p2); * point(p3); * * describe('Three black dots in a diagonal line from top left to bottom right.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe('Three arrows drawn on a gray square. A red and a blue arrow extend from the top left. A purple arrow extends from the tip of the red arrow to the tip of the blue arrow.'); * } * * function draw() { * background(200); * * let origin = createVector(0, 0); * * // Draw the red arrow. * let v1 = createVector(50, 50); * drawArrow(origin, v1, 'red'); * * // Draw the blue arrow. * let v2 = createVector(20, 70); * drawArrow(origin, v2, 'blue'); * * // Purple arrow. * let v3 = p5.Vector.sub(v2, v1); * drawArrow(v1, v3, 'purple'); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ /** * @param {p5.Vector|Number[]} value the vector to subtract * @chainable */ sub(...args) { if (args[0] instanceof Vector) { args[0].values.forEach((value, index) => { this.values[index] -= value || 0; }); } else if (Array.isArray(args[0])) { args[0].forEach((value, index) => { this.values[index] -= value || 0; }); } else { args.forEach((value, index) => { this.values[index] -= value || 0; }); } return this; } /** * Multiplies a vector's `x`, `y`, and `z` components. * * `mult()` can use separate numbers, as in `v.mult(1, 2, 3)`, another * <a href="#/p5.Vector">p5.Vector</a> object, as in `v.mult(v2)`, or an array * 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 * no effect. * * The static version of `mult()`, as in `p5.Vector.mult(v, 2)`, returns a new * <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * originals. * * @method mult * @param {Number} n The number to multiply with the vector * @chainable * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top-left. * let p = createVector(25, 25); * point(p); * * // Center. * // Multiply all components by 2. * p.mult(2); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * strokeWeight(5); * * // Top-left. * let p = createVector(25, 25); * point(p); * * // Bottom-right. * // Multiply p.x * 2 and p.y * 3 * p.mult(2, 3); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top-left. * let p = createVector(25, 25); * point(p); * * // Bottom-right. * // Multiply p.x * 2 and p.y * 3 * let arr = [2, 3]; * p.mult(arr); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top-left. * let p = createVector(25, 25); * point(p); * * // Bottom-right. * // Multiply p.x * p2.x and p.y * p2.y * let p2 = createVector(2, 3); * p.mult(p2); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Top-left. * let p = createVector(25, 25); * point(p); * * // Bottom-right. * // Create a new p5.Vector with * // p3.x = p.x * p2.x * // p3.y = p.y * p2.y * let p2 = createVector(2, 3); * let p3 = p5.Vector.mult(p, p2); * point(p3); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe('Two arrows extending from the top left corner. The blue arrow is twice the length of the red arrow.'); * } * * function draw() { * background(200); * * let origin = createVector(0, 0); * * // Draw the red arrow. * let v1 = createVector(25, 25); * drawArrow(origin, v1, 'red'); * * // Draw the blue arrow. * let v2 = p5.Vector.mult(v1, 2); * drawArrow(origin, v2, 'blue'); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ /** * @param {Number} x number to multiply with the x component of the vector. * @param {Number} y number to multiply with the y component of the vector. * @param {Number} [z] number to multiply with the z component of the vector. * @chainable */ /** * @param {Number[]} arr array to multiply with the components of the vector. * @chainable */ /** * @param {p5.Vector} v vector to multiply with the components of the original vector. * @chainable */ mult(...args) { if (args.length === 1 && args[0] instanceof Vector) { const v = args[0]; const maxLen = Math.min(this.values.length, v.values.length); for (let i = 0; i < maxLen; i++) { if (Number.isFinite(v.values[i]) && typeof v.values[i] === "number") { this._values[i] *= v.values[i]; } else { console.warn( "p5.Vector.prototype.mult:", "v contains components that are either undefined or not finite numbers" ); return this; } } } else if (args.length === 1 && Array.isArray(args[0])) { const arr = args[0]; const maxLen = Math.min(this.values.length, arr.length); for (let i = 0; i < maxLen; i++) { if (Number.isFinite(arr[i]) && typeof arr[i] === "number") { this._values[i] *= arr[i]; } else { console.warn( "p5.Vector.prototype.mult:", "arr contains elements that are either undefined or not finite numbers" ); return this; } } } else if ( args.length === 1 && typeof args[0] === "number" && Number.isFinite(args[0]) ) { for (let i = 0; i < this._values.length; i++) { this._values[i] *= args[0]; } } return this; } /** * Divides a vector's `x`, `y`, and `z` components. * * `div()` can use separate numbers, as in `v.div(1, 2, 3)`, another * <a href="#/p5.Vector">p5.Vector</a> object, as in `v.div(v2)`, or an array * 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 * no effect. * * The static version of `div()`, as in `p5.Vector.div(v, 2)`, returns a new * <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * originals. * * @param {Number} n The number to divide the vector by * @chainable * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Center. * let p = createVector(50, 50); * point(p); * * // Top-left. * // Divide p.x / 2 and p.y / 2 * p.div(2); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Bottom-right. * let p = createVector(50, 75); * point(p); * * // Top-left. * // Divide p.x / 2 and p.y / 3 * p.div(2, 3); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Bottom-right. * let p = createVector(50, 75); * point(p); * * // Top-left. * // Divide p.x / 2 and p.y / 3 * let arr = [2, 3]; * p.div(arr); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Bottom-right. * let p = createVector(50, 75); * point(p); * * // Top-left. * // Divide p.x / 2 and p.y / 3 * let p2 = createVector(2, 3); * p.div(p2); * point(p); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Style the points. * strokeWeight(5); * * // Bottom-right. * let p = createVector(50, 75); * point(p); * * // Top-left. * // Create a new p5.Vector with * // p3.x = p.x / p2.x * // p3.y = p.y / p2.y * let p2 = createVector(2, 3); * let p3 = p5.Vector.div(p, p2); * point(p3); * * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.'); * } * </code> * </div> * * <div> * <code> * function draw() { * background(200); * * let origin = createVector(0, 0); * * // Draw the red arrow. * let v1 = createVector(50, 50); * drawArrow(origin, v1, 'red'); * * // Draw the blue arrow. * let v2 = p5.Vector.div(v1, 2); * drawArrow(origin, v2, 'blue'); * * describe('Two arrows extending from the top left corner. The blue arrow is half the length of the red arrow.'); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ /** * @param {Number} x number to divide with the x component of the vector. * @param {Number} y number to divide with the y component of the vector. * @param {Number} [z] number to divide with the z component of the vector. * @chainable */ /** * @param {Number[]} arr array to divide the components of the vector by. * @chainable */ /** * @param {p5.Vector} v vector to divide the components of the original vector by. * @chainable */ div(...args) { if (args.length === 0) return this; if (args.length === 1 && args[0] instanceof Vector) { const v = args[0]; if ( v._values.every( (val) => Number.isFinite(val) && typeof val === "number" ) ) { if (v._values.some((val) => val === 0)) { console.warn("p5.Vector.prototype.div:", "divide by 0"); return this; } this._values = this._values.map((val, i) => val / v._values[i]); } else { console.warn( "p5.Vector.prototype.div:", "vector contains components that are either undefined or not finite numbers" ); } return this; } if (args.length === 1 && Array.isArray(args[0])) { const arr = args[0]; if (arr.every((val) => Number.isFinite(val) && typeof val === "number")) { if (arr.some((val) => val === 0)) { console.warn("p5.Vector.prototype.div:", "divide by 0"); return this; } this._values = this._values.map((val, i) => val / arr[i]); } else { console.warn( "p5.Vector.prototype.div:", "array contains components that are either undefined or not finite numbers" ); } return this; } if (args.every((val) => Number.isFinite(val) && typeof val === "number")) { if (args.some((val) => val === 0)) { console.warn("p5.Vector.prototype.div:", "divide by 0"); return this; } this._values = this._values.map((val, i) => val / args[0]); } else { console.warn( "p5.Vector.prototype.div:", "arguments contain components that are either undefined or not finite numbers" ); } return this; } /** * Calculates the magnitude (length) of the vector. * * Use <a href="#/p5/mag">mag()</a> to calculate the magnitude of a 2D vector * using components as in `mag(x, y)`. * * @return {Number} magnitude of the vector. * * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create a p5.Vector object. * let p = createVector(30, 40); * * // Draw a line from the origin. * line(0, 0, p.x, p.y); * * // Style the text. * textAlign(CENTER); * textSize(16); * * // Display the vector's magnitude. * let m = p.mag(); * text(m, p.x, p.y); * * describe('A diagonal black line extends from the top left corner of a gray square. The number 50 is written at the end of the line.'); * } * </code> * </div> */ mag() { return Math.sqrt(this.magSq()); } /** * Calculates the magnitude (length) of the vector squared. * * @return {Number} squared magnitude of the vector. * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create a p5.Vector object. * let p = createVector(30, 40); * * // Draw a line from the origin. * line(0, 0, p.x, p.y); * * // Style the text. * textAlign(CENTER); * textSize(16); * * // Display the vector's magnitude squared. * let m = p.magSq(); * text(m, p.x, p.y); * * describe('A diagonal black line extends from the top left corner of a gray square. The number 2500 is written at the end of the line.'); * } * </code> * </div> */ magSq() { return this._values.reduce( (sum, component) => sum + component * component, 0 ); } /** * Calculates the dot product of two vectors. * * The dot product is a number that describes the overlap between two vectors. * Visually, the dot product can be thought of as the "shadow" one vector * casts on another. The dot product's magnitude is largest when two vectors * point in the same or opposite directions. Its magnitude is 0 when two * vectors form a right angle. * * The version of `dot()` with one parameter interprets it as another * <a href="#/p5.Vector">p5.Vector</a> object. * * The version of `dot()` with multiple parameters interprets them as the * `x`, `y`, and `z` components of another vector. * * The static version of `dot()`, as in `p5.Vector.dot(v1, v2)`, is the same * as calling `v1.dot(v2)`. * * @param {Number} x x component of the vector. * @param {Number} [y] y component of the vector. * @param {Number} [z] z component of the vector. * @return {Number} dot product. * * @example * <div class="norender"> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(3, 4); * let v2 = createVector(3, 0); * * // Calculate the dot product. * let dp = v1.dot(v2); * * // Prints "9" to the console. * print(dp); * } * </code> * </div> * * <div class="norender"> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(1, 0); * let v2 = createVector(0, 1); * * // Calculate the dot product. * let dp = p5.Vector.dot(v1, v2); * * // Prints "0" to the console. * print(dp); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe('Two arrows drawn on a gray square. A black arrow points to the right and a red arrow follows the mouse. The text "v1 • v2 = something" changes as the mouse moves.'); * } * * function draw() { * background(200); * * // Center. * let v0 = createVector(50, 50); * * // Draw the black arrow. * let v1 = createVector(30, 0); * drawArrow(v0, v1, 'black'); * * // Draw the red arrow. * let v2 = createVector(mouseX - 50, mouseY - 50); * drawArrow(v0, v2, 'red'); * * // Display the dot product. * let dp = v2.dot(v1); * text(`v2 • v1 = ${dp}`, 10, 20); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ /** * @param {p5.Vector} v <a href="#/p5.Vector">p5.Vector</a> to be dotted. * @return {Number} */ dot(...args) { if (args[0] instanceof Vector) { return this.dot(...args[0]._values); } return this._values.reduce((sum, component, index) => { return sum + component * (args[index] || 0); }, 0); } /** * Calculates the cross product of two vectors. * * The cross product is a vector that points straight out of the plane created * by two vectors. The cross product's magnitude is the area of the parallelogram * formed by the original two vectors. * * The static version of `cross()`, as in `p5.Vector.cross(v1, v2)`, is the same * as calling `v1.cross(v2)`. * * @param {p5.Vector} v <a href="#/p5.Vector">p5.Vector</a> to be crossed. * @return {p5.Vector} cross product as a <a href="#/p5.Vector">p5.Vector</a>. * * @example * <div class="norender"> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(1, 0); * let v2 = createVector(3, 4); * * // Calculate the cross product. * let cp = v1.cross(v2); * * // Prints "p5.Vector Object : [0, 0, 4]" to the console. * print(cp.toString()); * } * </code> * </div> * * <div class="norender"> * <code> * function setup() { * // Create p5.Vector objects. * let v1 = createVector(1, 0); * let v2 = createVector(3, 4); * * // Calculate the cross product. * let cp = p5.Vector.cross(v1, v2); * * // Prints "p5.Vector Object : [0, 0, 4]" to the console. * print(cp.toString()); * } * </code> * </div> */ cross(v) { const x = this.y * v.z - this.z * v.y; const y = this.z * v.x - this.x * v.z; const z = this.x * v.y - this.y * v.x; if (this.isPInst) { return new Vector(this._fromRadians, this._toRadians, x, y, z); } else { return new Vector(x, y, z); } } /** * Calculates the distance between two points represented by vectors. * * A point's coordinates can be represented by the components of a vector * that extends from the origin to the point. * * The static version of `dist()`, as in `p5.Vector.dist(v1, v2)`, is the same * as calling `v1.dist(v2)`. * * Use <a href="#/p5/dist">dist()</a> to calculate the distance between points * using coordinates as in `dist(x1, y1, x2, y2)`. * * @method dist * @submodule p5.Vector * @param {p5.Vector} v x, y, and z coordinates of a <a href="#/p5.Vector">p5.Vector</a>. * @return {Number} distance. * * @example * <div class="norender"> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create p5.Vector objects. * let v1 = createVector(1, 0); * let v2 = createVector(0, 1); * * // Calculate the distance between them. * let d = v1.dist(v2); * * // Prints "1.414..." to the console. * print(d); * } * </code> * </div> * * <div class="norender"> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create p5.Vector objects. * let v1 = createVector(1, 0); * let v2 = createVector(0, 1); * * // Calculate the distance between them. * let d = p5.Vector.dist(v1, v2); * * // Prints "1.414..." to the console. * print(d); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe('Three arrows drawn on a gray square. A red and a blue arrow extend from the top left. A purple arrow extends from the tip of the red arrow to the tip of the blue arrow. The number 36 is written in black near the purple arrow.'); * } * * function draw() { * background(200); * * let origin = createVector(0, 0); * * // Draw the red arrow. * let v1 = createVector(50, 50); * drawArrow(origin, v1, 'red'); * * // Draw the blue arrow. * let v2 = createVector(20, 70); * drawArrow(origin, v2, 'blue'); * * // Purple arrow. * let v3 = p5.Vector.sub(v2, v1); * drawArrow(v1, v3, 'purple'); * * // Style the text. * textAlign(CENTER); * * // Display the magnitude. The same as floor(v3.mag()); * let m = floor(p5.Vector.dist(v1, v2)); * text(m, 50, 75); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ dist(v) { return v.copy().sub(this).mag(); } /** * Scales the components of a <a href="#/p5.Vector">p5.Vector</a> object so * that its magnitude is 1. * * The static version of `normalize()`, as in `p5.Vector.normalize(v)`, * returns a new <a href="#/p5.Vector">p5.Vector</a> object and doesn't change * the original. * * @return {p5.Vector} normalized <a href="#/p5.Vector">p5.Vector</a>. * * @example * <div class="norender"> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create a p5.Vector. * let v = createVector(10, 20, 2); * * // Normalize. * v.normalize(); * * // Prints "p5.Vector Object : [0.445..., 0.890..., 0.089...]" to the console. * print(v.toString()); * } * </code> * </div> * * <div class="norender"> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create a p5.Vector. * let v0 = createVector(10, 20, 2); * * // Create a normalized copy. * let v1 = p5.Vector.normalize(v0); * * // Prints "p5.Vector Object : [10, 20, 2]" to the console. * print(v0.toString()); * // Prints "p5.Vector Object : [0.445..., 0.890..., 0.089...]" to the console. * print(v1.toString()); * } * </code> * </div> * * <div> * <code> * function setup() { * createCanvas(100, 100); * * describe("A red and blue arrow extend from the center of a circle. Both arrows follow the mouse, but the blue arrow's length is fixed to the circle's radius."); * } * * function draw() { * background(240); * * // Vector to the center. * let v0 = createVector(50, 50); * * // Vector from the center to the mouse. * let v1 = createVector(mouseX - 50, mouseY - 50); * * // Circle's radius. * let r = 25; * * // Draw the red arrow. * drawArrow(v0, v1, 'red'); * * // Draw the blue arrow. * v1.normalize(); * drawArrow(v0, v1.mult(r), 'blue'); * * // Draw the circle. * noFill(); * circle(50, 50, r * 2); * } * * // Draws an arrow between two vectors. * function drawArrow(base, vec, myColor) { * push(); * stroke(myColor); * strokeWeight(3); * fill(myColor); * translate(base.x, base.y); * line(0, 0, vec.x, vec.y); * rotate(vec.heading()); * let arrowSize = 7; * translate(vec.mag() - arrowSize, 0); * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0); * pop(); * } * </code> * </div> */ normalize() { const len = this.mag(); // here we multiply by the reciprocal instead of calling 'div()' // since div duplicates this zero check. if (len !== 0) this.mult(1 / len); return this; } /** * Limits a vector's magnitude to a maximum value. * * The static version of `limit()`, as in `p5.Vector.limit(v, 5)`, returns a * new <a href="#/p5.Vector">p5.Vector</a> object and doesn't change the * original. * * @param {Number} max maximum magnitude for the vector. * @chainable * * @example * <div class="norender"> * <code> * fu