From 7e152bb2b0e1e798c017bc7f437fb3117e4890b1 Mon Sep 17 00:00:00 2001 From: Michael Reinstein Date: Tue, 26 Mar 2024 17:54:38 -0700 Subject: [PATCH] vecX: add truncate and midpoint functions. fixes #8 (#26) * vecX: add truncate and midpoint functions * who lints ya, baby? --- src/vec2-impl.ts | 29 ++++++++++++++++++++++++++++ src/vec3-impl.ts | 29 ++++++++++++++++++++++++++++ src/vec4-impl.ts | 29 ++++++++++++++++++++++++++++ test/tests/vec2-test.js | 42 +++++++++++++++++++++++++++++++++++++++++ test/tests/vec3-test.js | 42 +++++++++++++++++++++++++++++++++++++++++ test/tests/vec4-test.js | 42 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+) diff --git a/src/vec2-impl.ts b/src/vec2-impl.ts index a195b4a..2011d27 100644 --- a/src/vec2-impl.ts +++ b/src/vec2-impl.ts @@ -680,3 +680,32 @@ export function setLength(a: Vec2, len: number, dst?: Vec2) { normalize(a, dst); return mulScalar(dst, len, dst); } + +/** + * Ensure a vector is not longer than a max length + * + * @param a The vec2 to limit + * @param maxLen The longest length of the resulting vector + * @returns The vector, shortened to maxLen if it's too long + */ +export function truncate(a: Vec2, maxLen: number, dst?: Vec2) { + dst = dst || new VecType(2); + + if (length(a) > maxLen) { + return setLength(a, maxLen, dst); + } + + return copy(a, dst); +} + +/** + * Return the vector exactly between 2 endpoint vectors + * + * @param a Endpoint 1 + * @param b Endpoint 2 + * @returns The vector exactly residing between endpoints 1 and 2 + */ +export function midpoint(a: Vec2, b: Vec2, dst?: Vec2) { + dst = dst || new VecType(2); + return lerp(a, b, 0.5, dst); +} diff --git a/src/vec3-impl.ts b/src/vec3-impl.ts index b584dbe..7a9b564 100644 --- a/src/vec3-impl.ts +++ b/src/vec3-impl.ts @@ -896,3 +896,32 @@ export function setLength(a: Vec3, len: number, dst?: Vec3) { normalize(a, dst); return mulScalar(dst, len, dst); } + +/** + * Ensure a vector is not longer than a max length + * + * @param a The vec3 to limit + * @param maxLen The longest length of the resulting vector + * @returns The vector, shortened to maxLen if it's too long + */ +export function truncate(a: Vec3, maxLen: number, dst?: Vec3) { + dst = dst || new VecType(3); + + if (length(a) > maxLen) { + return setLength(a, maxLen, dst); + } + + return copy(a, dst); +} + +/** + * Return the vector exactly between 2 endpoint vectors + * + * @param a Endpoint 1 + * @param b Endpoint 2 + * @returns The vector exactly residing between endpoints 1 and 2 + */ +export function midpoint(a: Vec3, b: Vec3, dst?: Vec3) { + dst = dst || new VecType(3); + return lerp(a, b, 0.5, dst); +} diff --git a/src/vec4-impl.ts b/src/vec4-impl.ts index f738e55..c5c52c2 100644 --- a/src/vec4-impl.ts +++ b/src/vec4-impl.ts @@ -646,3 +646,32 @@ export function setLength(a: Vec4, len: number, dst?: Vec4) { normalize(a, dst); return mulScalar(dst, len, dst); } + +/** + * Ensure a vector is not longer than a max length + * + * @param a The vec4 to limit + * @param maxLen The longest length of the resulting vector + * @returns The vector, shortened to maxLen if it's too long + */ +export function truncate(a: Vec4, maxLen: number, dst?: Vec4) { + dst = dst || new VecType(4); + + if (length(a) > maxLen) { + return setLength(a, maxLen, dst); + } + + return copy(a, dst); +} + +/** + * Return the vector exactly between 2 endpoint vectors + * + * @param a Endpoint 1 + * @param b Endpoint 2 + * @returns The vector exactly residing between endpoints 1 and 2 + */ +export function midpoint(a: Vec4, b: Vec4, dst?: Vec4) { + dst = dst || new VecType(4); + return lerp(a, b, 0.5, dst); +} diff --git a/test/tests/vec2-test.js b/test/tests/vec2-test.js index 6ca981a..93090a3 100644 --- a/test/tests/vec2-test.js +++ b/test/tests/vec2-test.js @@ -510,5 +510,47 @@ describe('vec2', () => { }); }); + describe('truncate', function() { + describe('limit a vector to a max length', function() { + let vecA; + + beforeEach(function () { + vecA = [10.323759005323593, 10.323759005323593]; + }); + + it("should shorten the vector", function () { + const result = vec2.truncate(vecA, 4.0); + assertEqualApproximately(result, [2.82842712474619, 2.82842712474619]); + assertEqualApproximately(vec2.length(result), 4.0); + }); + + it("should preserve the vector when shorter than maxLen", function () { + const result = vec2.truncate(vecA, 18.0); + assertEqualApproximately(result, [10.323759005323593, 10.323759005323593]); + assertEqualApproximately(vec2.length(result), 14.6); + }); + }); + }); + + describe('midpoint', function() { + describe('return the midpoint between 2 vectors', function() { + + it("should return the midpoint", function () { + const vecA = [ 0, 0 ] + const vecB = [ 10, 10 ] + const result = vec2.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 5, 5 ]); + }); + + it("should handle negatives", function () { + const vecA = [ -10, -10 ] + const vecB = [ 10, 10 ] + const result = vec2.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 0, 0 ]); + }); + + }); + }); + }); diff --git a/test/tests/vec3-test.js b/test/tests/vec3-test.js index 6b51dd6..b054cd4 100644 --- a/test/tests/vec3-test.js +++ b/test/tests/vec3-test.js @@ -566,5 +566,47 @@ describe('vec3', () => { }); }); + describe('truncate', function() { + describe('limit a vector to a max length', function() { + let vecA; + + beforeEach(function () { + vecA = [8.429313930168536, 8.429313930168536, 8.429313930168536]; + }); + + it("should shorten the vector", function () { + const result = vec3.truncate(vecA, 4.0); + assertEqualApproximately(result, [2.309401076758503, 2.309401076758503, 2.309401076758503]); + assertEqualApproximately(vec3.length(result), 4.0); + }); + + it("should preserve the vector when shorter than maxLen", function () { + const result = vec3.truncate(vecA, 18.0); + assertEqualApproximately(result, [8.429313930168536, 8.429313930168536, 8.429313930168536]); + assertEqualApproximately(vec3.length(result), 14.6); + }); + }); + }); + + describe('midpoint', function() { + describe('return the midpoint between 2 vectors', function() { + + it("should return the midpoint", function () { + const vecA = [ 0, 0, 0 ] + const vecB = [ 10, 10, 10 ] + const result = vec3.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 5, 5, 5 ]); + }); + + it("should handle negatives", function () { + const vecA = [ -10, -10, -10 ] + const vecB = [ 10, 10, 10 ] + const result = vec3.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 0, 0, 0 ]); + }); + + }); + }); + }); diff --git a/test/tests/vec4-test.js b/test/tests/vec4-test.js index 1a33f8d..28ff528 100644 --- a/test/tests/vec4-test.js +++ b/test/tests/vec4-test.js @@ -419,5 +419,47 @@ describe('vec4', () => { }); }); + describe('truncate', function() { + describe('limit a vector to a max length', function() { + let vecA; + + beforeEach(function () { + vecA = [8.429313930168536, 8.429313930168536, 8.429313930168536, 8.429313930168536]; + }); + + it("should shorten the vector", function () { + const result = vec4.truncate(vecA, 4.0); + assertEqualApproximately(result, [2, 2, 2, 2]); + assertEqualApproximately(vec4.length(result), 4.0); + }); + + it("should preserve the vector when shorter than maxLen", function () { + const result = vec4.truncate(vecA, 18.0); + assertEqualApproximately(result, [8.429313930168536, 8.429313930168536, 8.429313930168536, 8.429313930168536]); + assertEqualApproximately(vec4.length(result), 16.858627860337073); + }); + }); + }); + + describe('midpoint', function() { + describe('return the midpoint between 2 vectors', function() { + + it("should return the midpoint", function () { + const vecA = [ 0, 0, 0, 0 ] + const vecB = [ 10, 10, 10, 10 ] + const result = vec4.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 5, 5, 5, 5 ]); + }); + + it("should handle negatives", function () { + const vecA = [ -10, -10, -10, -10 ] + const vecB = [ 10, 10, 10, 10 ] + const result = vec4.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 0, 0, 0, 0 ]); + }); + + }); + }); + });