From b6531507a52f38051766501e8e2202ce32042e2d Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sat, 10 Oct 2020 11:10:45 +1100 Subject: [PATCH] Updated Vector2 docs --- src/helpers/vector2.js | 174 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 16 deletions(-) diff --git a/src/helpers/vector2.js b/src/helpers/vector2.js index 7697b9b..8eecb65 100644 --- a/src/helpers/vector2.js +++ b/src/helpers/vector2.js @@ -4,14 +4,33 @@ import { lerp as lerpNumber, } from "./shared"; +/** + * Vector class with x and y + * @typedef {Object} Vector2 + * @property {number} x - X component of the vector + * @property {number} y - Y component of the vector + */ + +/** + * @param {Vector2} p + * @returns {number} Length squared of `p` + */ export function lengthSquared(p) { return p.x * p.x + p.y * p.y; } +/** + * @param {Vector2} p + * @returns {number} Length of `p` + */ export function length(p) { return Math.sqrt(lengthSquared(p)); } +/** + * @param {Vector2} p + * @returns {Vector2} `p` normalized, if length of `p` is 0 `{x: 0, y: 0}` is returned + */ export function normalize(p) { const l = length(p); if (l === 0) { @@ -20,10 +39,20 @@ export function normalize(p) { return divide(p, l); } +/** + * @param {Vector2} a + * @param {Vector2} b + * @returns {number} Dot product between `a` and `b` + */ export function dot(a, b) { return a.x * b.x + a.y * b.y; } +/** + * @param {Vector2} a + * @param {(Vector2 | number)} b + * @returns {Vector2} a - b + */ export function subtract(a, b) { if (typeof b === "number") { return { x: a.x - b, y: a.y - b }; @@ -32,6 +61,11 @@ export function subtract(a, b) { } } +/** + * @param {Vector2} a + * @param {(Vector2 | number)} b + * @returns {Vector2} a + b + */ export function add(a, b) { if (typeof b === "number") { return { x: a.x + b, y: a.y + b }; @@ -40,6 +74,11 @@ export function add(a, b) { } } +/** + * @param {Vector2} a + * @param {(Vector2 | number)} b + * @returns {Vector2} a * b + */ export function multiply(a, b) { if (typeof b === "number") { return { x: a.x * b, y: a.y * b }; @@ -48,6 +87,11 @@ export function multiply(a, b) { } } +/** + * @param {Vector2} a + * @param {(Vector2 | number)} b + * @returns {Vector2} a / b + */ export function divide(a, b) { if (typeof b === "number") { return { x: a.x / b, y: a.y / b }; @@ -56,6 +100,13 @@ export function divide(a, b) { } } +/** + * Rotates a point around a given origin by an angle in degrees + * @param {Vector2} point Point to rotate + * @param {Vector2} origin Origin of the rotation + * @param {number} angle Angle of rotation in degrees + * @returns {Vector2} Rotated point + */ export function rotate(point, origin, angle) { const cos = Math.cos(toRadians(angle)); const sin = Math.sin(toRadians(angle)); @@ -66,18 +117,53 @@ export function rotate(point, origin, angle) { }; } +/** + * Rotates a direction by a given angle in degrees + * @param {Vector2} direction Direction to rotate + * @param {number} angle Angle of rotation in degrees + * @returns {Vector2} Rotated direction + */ export function rotateDirection(direction, angle) { return rotate(direction, { x: 0, y: 0 }, angle); } -export function min(a) { - return a.x < a.y ? a.x : a.y; -} - -export function max(a) { - return a.x > a.y ? a.x : a.y; +/** + * Returns the min of `value` and `minimum`, if `minimum` is undefined component wise min is returned instead + * @param {Vector2} a + * @param {(Vector2 | number)} [minimum] Value to compare + * @returns {(Vector2 | number)} + */ +export function min(a, minimum) { + if (minimum === undefined) { + return a.x < a.y ? a.x : a.y; + } else if (typeof minimum === "number") { + return { x: Math.min(a.x, minimum), y: Math.min(a.y, minimum) }; + } else { + return { x: Math.min(a.x, minimum.x), y: Math.min(a.y, minimum.y) }; + } +} +/** + * Returns the max of `a` and `maximum`, if `maximum` is undefined component wise max is returned instead + * @param {Vector2} a + * @param {(Vector2 | number)} [maximum] Value to compare + * @returns {(Vector2 | number)} + */ +export function max(a, maximum) { + if (maximum === undefined) { + return a.x > a.y ? a.x : a.y; + } else if (typeof maximum === "number") { + return { x: Math.max(a.x, maximum), y: Math.max(a.y, maximum) }; + } else { + return { x: Math.max(a.x, maximum.x), y: Math.max(a.y, maximum.y) }; + } } +/** + * Rounds `p` to the nearest value of `to` + * @param {Vector2} p + * @param {Vector2} to + * @returns {Vector2} + */ export function roundTo(p, to) { return { x: roundToNumber(p.x, to.x), @@ -85,14 +171,27 @@ export function roundTo(p, to) { }; } +/** + * @param {Vector2} a + * @returns {Vector2} The component wise sign of `a` + */ export function sign(a) { return { x: Math.sign(a.x), y: Math.sign(a.y) }; } +/** + * @param {Vector2} a + * @returns {Vector2} The component wise absolute of `a` + */ export function abs(a) { return { x: Math.abs(a.x), y: Math.abs(a.y) }; } +/** + * @param {Vector2} a + * @param {(Vector2 | number)} b + * @returns {Vector2} `a` to the power of `b` + */ export function pow(a, b) { if (typeof b === "number") { return { x: Math.pow(a.x, b), y: Math.pow(a.y, b) }; @@ -101,10 +200,21 @@ export function pow(a, b) { } } +/** + * @param {Vector2} a + * @returns {number} The dot product between `a` and `a` + */ export function dot2(a) { return dot(a, a); } +/** + * Clamps `a` between `min` and `max` + * @param {Vector2} a + * @param {number} min + * @param {number} max + * @returns {Vector2} + */ export function clamp(a, min, max) { return { x: Math.min(Math.max(a.x, min), max), @@ -112,7 +222,14 @@ export function clamp(a, min, max) { }; } -// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d +/** + * Calculates the distance between a point and a line segment + * See more at {@link https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm} + * @param {Vector2} p Point + * @param {Vector2} a Start of the line + * @param {Vector2} b End of the line + * @returns {Object} The distance to and the closest point on the line segment + */ export function distanceToLine(p, a, b) { const pa = subtract(p, a); const ba = subtract(b, a); @@ -122,8 +239,16 @@ export function distanceToLine(p, a, b) { return { distance, point }; } -// TODO: Fix the robustness of this to allow smoothing on fog layers -// https://www.shadertoy.com/view/MlKcDD +/** + * Calculates the distance between a point and a quadratic bezier curve + * See more at {@link https://www.shadertoy.com/view/MlKcDD} + * @todo Fix the robustness of this to allow smoothing on fog layers + * @param {Vector2} pos Position + * @param {Vector2} A Start of the curve + * @param {Vector2} B Control point of the curve + * @param {Vector2} C End of the curve + * @returns {Object} The distance to and the closest point on the curve + */ export function distanceToQuadraticBezier(pos, A, B, C) { let distance = 0; let point = { x: pos.x, y: pos.y }; @@ -174,6 +299,11 @@ export function distanceToQuadraticBezier(pos, A, B, C) { return { distance: Math.sqrt(distance), point: point }; } +/** + * Calculates an axis-aligned bounding box around an array of point + * @param {Vector2[]} points + * @returns {Object} + */ export function getBounds(points) { let minX = Number.MAX_VALUE; let maxX = Number.MIN_VALUE; @@ -188,9 +318,14 @@ export function getBounds(points) { return { minX, maxX, minY, maxY }; } -// Check bounds then use ray casting algorithm -// https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm -// https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon/2922778 +/** + * Checks to see if a point is in a polygon using ray casting + * See more at {@link https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm} + * and {@link https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon/2922778} + * @param {Vector2} p + * @param {Vector2[]} points + * @returns {boolean} + */ export function pointInPolygon(p, points) { const { minX, maxX, minY, maxY } = getBounds(points); if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) { @@ -215,7 +350,7 @@ export function pointInPolygon(p, points) { } /** - * Returns true if a the distance between a and b is under threshold + * Returns true if a the distance between `a` and `b` is under `threshold` * @param {Vector2} a * @param {Vector2} b * @param {number} threshold @@ -228,7 +363,7 @@ export function compare(a, b, threshold) { * Returns the distance between two vectors * @param {Vector2} a * @param {Vector2} b - * @param {string} type - "chebyshev" | "euclidean" | "manhattan" + * @param {string} type - `chebyshev | euclidean | manhattan` */ export function distance(a, b, type) { switch (type) { @@ -243,13 +378,20 @@ export function distance(a, b, type) { } } +/** + * Linear interpolate between `a` and `b` by `alpha` + * @param {Vector2} a + * @param {Vector2} b + * @param {number} alpha + * @returns {Vector2} + */ export function lerp(a, b, alpha) { return { x: lerpNumber(a.x, b.x, alpha), y: lerpNumber(a.y, b.y, alpha) }; } /** * Returns total length of a an array of points treated as a path - * @param {Array} points the array of points in the path + * @param {Vector2[]} points the array of points in the path */ export function pathLength(points) { let l = 0; @@ -262,7 +404,7 @@ export function pathLength(points) { /** * Resample a path to n number of evenly distributed points * based off of http://depts.washington.edu/acelab/proj/dollar/index.html - * @param {Array} points the points to resample + * @param {Vector2[]} points the points to resample * @param {number} n the number of new points */ export function resample(points, n) {