@fluixi/utils 1.0.0-alpha.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/lib/array/advanced.cjs +1 -0
- package/dist/lib/array/advanced.d.ts +1116 -0
- package/dist/lib/array/advanced.d.ts.map +1 -0
- package/dist/lib/array/advanced.js +2429 -0
- package/dist/lib/array/advanced.mjs +1 -0
- package/dist/lib/array/array.cjs +1 -0
- package/dist/lib/array/array.d.ts +6 -0
- package/dist/lib/array/array.d.ts.map +1 -0
- package/dist/lib/array/array.js +31 -0
- package/dist/lib/array/array.mjs +1 -0
- package/dist/lib/array/index.cjs +1 -0
- package/dist/lib/array/index.d.ts +4 -0
- package/dist/lib/array/index.d.ts.map +1 -0
- package/dist/lib/array/index.js +3 -0
- package/dist/lib/array/index.mjs +1 -0
- package/dist/lib/array/utils.cjs +1 -0
- package/dist/lib/array/utils.d.ts +100 -0
- package/dist/lib/array/utils.d.ts.map +1 -0
- package/dist/lib/array/utils.js +165 -0
- package/dist/lib/array/utils.mjs +1 -0
- package/dist/lib/color/color.cjs +1 -0
- package/dist/lib/color/color.d.ts +366 -0
- package/dist/lib/color/color.d.ts.map +1 -0
- package/dist/lib/color/color.js +722 -0
- package/dist/lib/color/color.mjs +1 -0
- package/dist/lib/color/constants.cjs +1 -0
- package/dist/lib/color/constants.d.ts +361 -0
- package/dist/lib/color/constants.d.ts.map +1 -0
- package/dist/lib/color/constants.js +358 -0
- package/dist/lib/color/constants.mjs +1 -0
- package/dist/lib/color/conversions.cjs +1 -0
- package/dist/lib/color/conversions.d.ts +166 -0
- package/dist/lib/color/conversions.d.ts.map +1 -0
- package/dist/lib/color/conversions.js +766 -0
- package/dist/lib/color/conversions.mjs +1 -0
- package/dist/lib/color/helpers.cjs +1 -0
- package/dist/lib/color/helpers.d.ts +75 -0
- package/dist/lib/color/helpers.d.ts.map +1 -0
- package/dist/lib/color/helpers.js +419 -0
- package/dist/lib/color/helpers.mjs +1 -0
- package/dist/lib/color/index.cjs +1 -0
- package/dist/lib/color/index.d.ts +12 -0
- package/dist/lib/color/index.d.ts.map +1 -0
- package/dist/lib/color/index.js +15 -0
- package/dist/lib/color/index.mjs +1 -0
- package/dist/lib/color/types.cjs +1 -0
- package/dist/lib/color/types.d.ts +179 -0
- package/dist/lib/color/types.d.ts.map +1 -0
- package/dist/lib/color/types.js +1 -0
- package/dist/lib/color/types.mjs +0 -0
- package/dist/lib/color/utils.cjs +1 -0
- package/dist/lib/color/utils.d.ts +199 -0
- package/dist/lib/color/utils.d.ts.map +1 -0
- package/dist/lib/color/utils.js +858 -0
- package/dist/lib/color/utils.mjs +1 -0
- package/dist/lib/compare/comparators.cjs +1 -0
- package/dist/lib/compare/comparators.d.ts +65 -0
- package/dist/lib/compare/comparators.d.ts.map +1 -0
- package/dist/lib/compare/comparators.js +210 -0
- package/dist/lib/compare/comparators.mjs +1 -0
- package/dist/lib/compare/index.cjs +1 -0
- package/dist/lib/compare/index.d.ts +2 -0
- package/dist/lib/compare/index.d.ts.map +1 -0
- package/dist/lib/compare/index.js +1 -0
- package/dist/lib/compare/index.mjs +1 -0
- package/dist/lib/crypto/hash.cjs +1 -0
- package/dist/lib/crypto/hash.d.ts +6 -0
- package/dist/lib/crypto/hash.d.ts.map +1 -0
- package/dist/lib/crypto/hash.js +53 -0
- package/dist/lib/crypto/hash.mjs +1 -0
- package/dist/lib/crypto/index.cjs +1 -0
- package/dist/lib/crypto/index.d.ts +2 -0
- package/dist/lib/crypto/index.d.ts.map +1 -0
- package/dist/lib/crypto/index.js +1 -0
- package/dist/lib/crypto/index.mjs +1 -0
- package/dist/lib/dom/events.cjs +1 -0
- package/dist/lib/dom/events.d.ts +159 -0
- package/dist/lib/dom/events.d.ts.map +1 -0
- package/dist/lib/dom/events.js +50 -0
- package/dist/lib/dom/events.mjs +1 -0
- package/dist/lib/dom/index.cjs +1 -0
- package/dist/lib/dom/index.d.ts +3 -0
- package/dist/lib/dom/index.d.ts.map +1 -0
- package/dist/lib/dom/index.js +2 -0
- package/dist/lib/dom/index.mjs +1 -0
- package/dist/lib/dom/utils.cjs +1 -0
- package/dist/lib/dom/utils.d.ts +6 -0
- package/dist/lib/dom/utils.d.ts.map +1 -0
- package/dist/lib/dom/utils.js +37 -0
- package/dist/lib/dom/utils.mjs +1 -0
- package/dist/lib/functions/functions.cjs +1 -0
- package/dist/lib/functions/functions.d.ts +329 -0
- package/dist/lib/functions/functions.d.ts.map +1 -0
- package/dist/lib/functions/functions.js +585 -0
- package/dist/lib/functions/functions.mjs +1 -0
- package/dist/lib/functions/index.cjs +1 -0
- package/dist/lib/functions/index.d.ts +3 -0
- package/dist/lib/functions/index.d.ts.map +1 -0
- package/dist/lib/functions/index.js +1 -0
- package/dist/lib/functions/index.mjs +1 -0
- package/dist/lib/functions/types.cjs +1 -0
- package/dist/lib/functions/types.d.ts +2 -0
- package/dist/lib/functions/types.d.ts.map +1 -0
- package/dist/lib/functions/types.js +1 -0
- package/dist/lib/functions/types.mjs +0 -0
- package/dist/lib/helpers.cjs +1 -0
- package/dist/lib/helpers.d.ts +13 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +66 -0
- package/dist/lib/helpers.mjs +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/maths/constants.cjs +1 -0
- package/dist/lib/maths/constants.d.ts +84 -0
- package/dist/lib/maths/constants.d.ts.map +1 -0
- package/dist/lib/maths/constants.js +79 -0
- package/dist/lib/maths/constants.mjs +1 -0
- package/dist/lib/maths/fn/advanced.cjs +1 -0
- package/dist/lib/maths/fn/advanced.d.ts +149 -0
- package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
- package/dist/lib/maths/fn/advanced.js +264 -0
- package/dist/lib/maths/fn/advanced.mjs +1 -0
- package/dist/lib/maths/fn/basic.cjs +1 -0
- package/dist/lib/maths/fn/basic.d.ts +182 -0
- package/dist/lib/maths/fn/basic.d.ts.map +1 -0
- package/dist/lib/maths/fn/basic.js +343 -0
- package/dist/lib/maths/fn/basic.mjs +1 -0
- package/dist/lib/maths/fn/complex.cjs +1 -0
- package/dist/lib/maths/fn/complex.d.ts +153 -0
- package/dist/lib/maths/fn/complex.d.ts.map +1 -0
- package/dist/lib/maths/fn/complex.js +383 -0
- package/dist/lib/maths/fn/complex.mjs +1 -0
- package/dist/lib/maths/fn/index.cjs +1 -0
- package/dist/lib/maths/fn/index.d.ts +10 -0
- package/dist/lib/maths/fn/index.d.ts.map +1 -0
- package/dist/lib/maths/fn/index.js +9 -0
- package/dist/lib/maths/fn/index.mjs +1 -0
- package/dist/lib/maths/fn/trigo.cjs +1 -0
- package/dist/lib/maths/fn/trigo.d.ts +147 -0
- package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
- package/dist/lib/maths/fn/trigo.js +227 -0
- package/dist/lib/maths/fn/trigo.mjs +1 -0
- package/dist/lib/maths/fn/vector.cjs +1 -0
- package/dist/lib/maths/fn/vector.d.ts +512 -0
- package/dist/lib/maths/fn/vector.d.ts.map +1 -0
- package/dist/lib/maths/fn/vector.js +1136 -0
- package/dist/lib/maths/fn/vector.mjs +1 -0
- package/dist/lib/maths/formatter.cjs +1 -0
- package/dist/lib/maths/formatter.d.ts +35 -0
- package/dist/lib/maths/formatter.d.ts.map +1 -0
- package/dist/lib/maths/formatter.js +56 -0
- package/dist/lib/maths/formatter.mjs +1 -0
- package/dist/lib/maths/guards.cjs +1 -0
- package/dist/lib/maths/guards.d.ts +166 -0
- package/dist/lib/maths/guards.d.ts.map +1 -0
- package/dist/lib/maths/guards.js +315 -0
- package/dist/lib/maths/guards.mjs +1 -0
- package/dist/lib/maths/index.cjs +1 -0
- package/dist/lib/maths/index.d.ts +24 -0
- package/dist/lib/maths/index.d.ts.map +1 -0
- package/dist/lib/maths/index.js +29 -0
- package/dist/lib/maths/index.mjs +1 -0
- package/dist/lib/maths/parser.cjs +1 -0
- package/dist/lib/maths/parser.d.ts +20 -0
- package/dist/lib/maths/parser.d.ts.map +1 -0
- package/dist/lib/maths/parser.js +78 -0
- package/dist/lib/maths/parser.mjs +1 -0
- package/dist/lib/maths/random.cjs +1 -0
- package/dist/lib/maths/random.d.ts +27 -0
- package/dist/lib/maths/random.d.ts.map +1 -0
- package/dist/lib/maths/random.js +49 -0
- package/dist/lib/maths/random.mjs +1 -0
- package/dist/lib/maths/types/advanced.cjs +1 -0
- package/dist/lib/maths/types/advanced.d.ts +357 -0
- package/dist/lib/maths/types/advanced.d.ts.map +1 -0
- package/dist/lib/maths/types/advanced.js +5 -0
- package/dist/lib/maths/types/advanced.mjs +0 -0
- package/dist/lib/maths/types/basic.cjs +1 -0
- package/dist/lib/maths/types/basic.d.ts +95 -0
- package/dist/lib/maths/types/basic.d.ts.map +1 -0
- package/dist/lib/maths/types/basic.js +5 -0
- package/dist/lib/maths/types/basic.mjs +0 -0
- package/dist/lib/maths/types/index.cjs +1 -0
- package/dist/lib/maths/types/index.d.ts +4 -0
- package/dist/lib/maths/types/index.d.ts.map +1 -0
- package/dist/lib/maths/types/index.js +3 -0
- package/dist/lib/maths/types/index.mjs +0 -0
- package/dist/lib/maths/types/negative.cjs +1 -0
- package/dist/lib/maths/types/negative.d.ts +51 -0
- package/dist/lib/maths/types/negative.d.ts.map +1 -0
- package/dist/lib/maths/types/negative.js +5 -0
- package/dist/lib/maths/types/negative.mjs +0 -0
- package/dist/lib/maths/types/operations.cjs +1 -0
- package/dist/lib/maths/types/operations.d.ts +188 -0
- package/dist/lib/maths/types/operations.d.ts.map +1 -0
- package/dist/lib/maths/types/operations.js +6 -0
- package/dist/lib/maths/types/operations.mjs +0 -0
- package/dist/lib/object/clone.cjs +1 -0
- package/dist/lib/object/clone.d.ts +46 -0
- package/dist/lib/object/clone.d.ts.map +1 -0
- package/dist/lib/object/clone.js +104 -0
- package/dist/lib/object/clone.mjs +1 -0
- package/dist/lib/object/diff.cjs +1 -0
- package/dist/lib/object/diff.d.ts +203 -0
- package/dist/lib/object/diff.d.ts.map +1 -0
- package/dist/lib/object/diff.js +805 -0
- package/dist/lib/object/diff.mjs +1 -0
- package/dist/lib/object/getter-setter.cjs +1 -0
- package/dist/lib/object/getter-setter.d.ts +361 -0
- package/dist/lib/object/getter-setter.d.ts.map +1 -0
- package/dist/lib/object/getter-setter.js +1771 -0
- package/dist/lib/object/getter-setter.mjs +1 -0
- package/dist/lib/object/index.cjs +6 -0
- package/dist/lib/object/index.d.ts +13 -0
- package/dist/lib/object/index.d.ts.map +1 -0
- package/dist/lib/object/index.js +12 -0
- package/dist/lib/object/index.mjs +6 -0
- package/dist/lib/object/merge.cjs +1 -0
- package/dist/lib/object/merge.d.ts +93 -0
- package/dist/lib/object/merge.d.ts.map +1 -0
- package/dist/lib/object/merge.js +474 -0
- package/dist/lib/object/merge.mjs +1 -0
- package/dist/lib/object/object.cjs +1 -0
- package/dist/lib/object/object.d.ts +128 -0
- package/dist/lib/object/object.d.ts.map +1 -0
- package/dist/lib/object/object.js +432 -0
- package/dist/lib/object/object.mjs +1 -0
- package/dist/lib/object/omit.cjs +1 -0
- package/dist/lib/object/omit.d.ts +351 -0
- package/dist/lib/object/omit.d.ts.map +1 -0
- package/dist/lib/object/omit.js +656 -0
- package/dist/lib/object/omit.mjs +1 -0
- package/dist/lib/object/reconcile.cjs +1 -0
- package/dist/lib/object/reconcile.d.ts +7 -0
- package/dist/lib/object/reconcile.d.ts.map +1 -0
- package/dist/lib/object/reconcile.js +122 -0
- package/dist/lib/object/reconcile.mjs +1 -0
- package/dist/lib/object/selector.cjs +1 -0
- package/dist/lib/object/selector.d.ts +50 -0
- package/dist/lib/object/selector.d.ts.map +1 -0
- package/dist/lib/object/selector.js +371 -0
- package/dist/lib/object/selector.mjs +1 -0
- package/dist/lib/object/sort.cjs +1 -0
- package/dist/lib/object/sort.d.ts +149 -0
- package/dist/lib/object/sort.d.ts.map +1 -0
- package/dist/lib/object/sort.js +298 -0
- package/dist/lib/object/sort.mjs +1 -0
- package/dist/lib/object/transform.cjs +1 -0
- package/dist/lib/object/transform.d.ts +620 -0
- package/dist/lib/object/transform.d.ts.map +1 -0
- package/dist/lib/object/transform.js +1058 -0
- package/dist/lib/object/transform.mjs +1 -0
- package/dist/lib/object/types.cjs +1 -0
- package/dist/lib/object/types.d.ts +250 -0
- package/dist/lib/object/types.d.ts.map +1 -0
- package/dist/lib/object/types.js +1 -0
- package/dist/lib/object/types.mjs +0 -0
- package/dist/lib/object/utils.cjs +6 -0
- package/dist/lib/object/utils.d.ts +165 -0
- package/dist/lib/object/utils.d.ts.map +1 -0
- package/dist/lib/object/utils.js +444 -0
- package/dist/lib/object/utils.mjs +6 -0
- package/dist/lib/primitive/boolean/boolean.cjs +1 -0
- package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
- package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/boolean.js +615 -0
- package/dist/lib/primitive/boolean/boolean.mjs +1 -0
- package/dist/lib/primitive/boolean/index.cjs +1 -0
- package/dist/lib/primitive/boolean/index.d.ts +3 -0
- package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/index.js +2 -0
- package/dist/lib/primitive/boolean/index.mjs +1 -0
- package/dist/lib/primitive/boolean/types.cjs +1 -0
- package/dist/lib/primitive/boolean/types.d.ts +14 -0
- package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/types.js +1 -0
- package/dist/lib/primitive/boolean/types.mjs +0 -0
- package/dist/lib/primitive/date/date.cjs +1 -0
- package/dist/lib/primitive/date/date.d.ts +641 -0
- package/dist/lib/primitive/date/date.d.ts.map +1 -0
- package/dist/lib/primitive/date/date.js +1998 -0
- package/dist/lib/primitive/date/date.mjs +1 -0
- package/dist/lib/primitive/date/index.cjs +1 -0
- package/dist/lib/primitive/date/index.d.ts +3 -0
- package/dist/lib/primitive/date/index.d.ts.map +1 -0
- package/dist/lib/primitive/date/index.js +2 -0
- package/dist/lib/primitive/date/index.mjs +1 -0
- package/dist/lib/primitive/date/types.cjs +1 -0
- package/dist/lib/primitive/date/types.d.ts +205 -0
- package/dist/lib/primitive/date/types.d.ts.map +1 -0
- package/dist/lib/primitive/date/types.js +7 -0
- package/dist/lib/primitive/date/types.mjs +0 -0
- package/dist/lib/primitive/index.cjs +5 -0
- package/dist/lib/primitive/index.d.ts +5 -0
- package/dist/lib/primitive/index.d.ts.map +1 -0
- package/dist/lib/primitive/index.js +4 -0
- package/dist/lib/primitive/index.mjs +5 -0
- package/dist/lib/primitive/string/index.cjs +5 -0
- package/dist/lib/primitive/string/index.d.ts +3 -0
- package/dist/lib/primitive/string/index.d.ts.map +1 -0
- package/dist/lib/primitive/string/index.js +2 -0
- package/dist/lib/primitive/string/index.mjs +5 -0
- package/dist/lib/primitive/string/parser-2.cjs +8 -0
- package/dist/lib/primitive/string/parser-2.d.ts +17 -0
- package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser-2.js +656 -0
- package/dist/lib/primitive/string/parser-2.mjs +8 -0
- package/dist/lib/primitive/string/parser.cjs +3 -0
- package/dist/lib/primitive/string/parser.d.ts +9 -0
- package/dist/lib/primitive/string/parser.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser.js +775 -0
- package/dist/lib/primitive/string/parser.mjs +3 -0
- package/dist/lib/primitive/string/string.cjs +5 -0
- package/dist/lib/primitive/string/string.d.ts +535 -0
- package/dist/lib/primitive/string/string.d.ts.map +1 -0
- package/dist/lib/primitive/string/string.js +1301 -0
- package/dist/lib/primitive/string/string.mjs +5 -0
- package/dist/lib/primitive/string/types.cjs +1 -0
- package/dist/lib/primitive/string/types.d.ts +318 -0
- package/dist/lib/primitive/string/types.d.ts.map +1 -0
- package/dist/lib/primitive/string/types.js +5 -0
- package/dist/lib/primitive/string/types.mjs +0 -0
- package/dist/lib/primitive/types.cjs +1 -0
- package/dist/lib/primitive/types.d.ts +27 -0
- package/dist/lib/primitive/types.d.ts.map +1 -0
- package/dist/lib/primitive/types.js +1 -0
- package/dist/lib/primitive/types.mjs +0 -0
- package/dist/lib/url/index.cjs +1 -0
- package/dist/lib/url/index.d.ts +3 -0
- package/dist/lib/url/index.d.ts.map +1 -0
- package/dist/lib/url/index.js +2 -0
- package/dist/lib/url/index.mjs +1 -0
- package/dist/lib/url/parser.cjs +1 -0
- package/dist/lib/url/parser.d.ts +216 -0
- package/dist/lib/url/parser.d.ts.map +1 -0
- package/dist/lib/url/parser.js +488 -0
- package/dist/lib/url/parser.mjs +1 -0
- package/dist/lib/url/types.cjs +1 -0
- package/dist/lib/url/types.d.ts +18 -0
- package/dist/lib/url/types.d.ts.map +1 -0
- package/dist/lib/url/types.js +1 -0
- package/dist/lib/url/types.mjs +0 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,1136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector operations for 2D, 3D, and 4D vectors.
|
|
3
|
+
* @module VectorFunctions
|
|
4
|
+
*/
|
|
5
|
+
// Constants
|
|
6
|
+
// const EPSILON = Math.E;
|
|
7
|
+
// const DEG_TO_RAD = Math.PI / 180;
|
|
8
|
+
// const RAD_TO_DEG = 180 / Math.PI;
|
|
9
|
+
/**
|
|
10
|
+
* Validates that a value is a finite number.
|
|
11
|
+
* @param value - The value to validate
|
|
12
|
+
* @param name - Name of the value for error messages
|
|
13
|
+
* @throws {Error} If value is not a finite number
|
|
14
|
+
*/
|
|
15
|
+
function validateNumber(value, name) {
|
|
16
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
17
|
+
throw new Error(`${name} must be a finite number, got ${value}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a value is approximately zero within epsilon.
|
|
22
|
+
* @param value - The value to check
|
|
23
|
+
* @returns True if the value is effectively zero
|
|
24
|
+
*/
|
|
25
|
+
function isApproximatelyZero(value) {
|
|
26
|
+
return Math.abs(value) < 1e-9;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Checks if two numbers are approximately equal.
|
|
30
|
+
* @param a - First number
|
|
31
|
+
* @param b - Second number
|
|
32
|
+
* @returns True if numbers are approximately equal
|
|
33
|
+
*/
|
|
34
|
+
function approximatelyEqual(a, b) {
|
|
35
|
+
return Math.abs(a - b) < 1e-6;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validates a 2D vector.
|
|
39
|
+
* @param vector - The vector to validate
|
|
40
|
+
* @param name - Name for error messages
|
|
41
|
+
* @throws {Error} If vector is invalid
|
|
42
|
+
*/
|
|
43
|
+
function validateVector2D(vector, name = 'Vector2D') {
|
|
44
|
+
if (!vector || typeof vector !== 'object') {
|
|
45
|
+
throw new Error(`${name} must be an object`);
|
|
46
|
+
}
|
|
47
|
+
validateNumber(vector.x, `${name}.x`);
|
|
48
|
+
validateNumber(vector.y, `${name}.y`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates a 3D vector.
|
|
52
|
+
* @param vector - The vector to validate
|
|
53
|
+
* @param name - Name for error messages
|
|
54
|
+
* @throws {Error} If vector is invalid
|
|
55
|
+
*/
|
|
56
|
+
function validateVector3D(vector, name = 'Vector3D') {
|
|
57
|
+
if (!vector || typeof vector !== 'object') {
|
|
58
|
+
throw new Error(`${name} must be an object`);
|
|
59
|
+
}
|
|
60
|
+
validateNumber(vector.x, `${name}.x`);
|
|
61
|
+
validateNumber(vector.y, `${name}.y`);
|
|
62
|
+
validateNumber(vector.z, `${name}.z`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validates a 4D vector.
|
|
66
|
+
* @param vector - The vector to validate
|
|
67
|
+
* @param name - Name for error messages
|
|
68
|
+
* @throws {Error} If vector is invalid
|
|
69
|
+
*/
|
|
70
|
+
function validateVector4D(vector, name = 'Vector4D') {
|
|
71
|
+
if (!vector || typeof vector !== 'object') {
|
|
72
|
+
throw new Error(`${name} must be an object`);
|
|
73
|
+
}
|
|
74
|
+
validateNumber(vector.x, `${name}.x`);
|
|
75
|
+
validateNumber(vector.y, `${name}.y`);
|
|
76
|
+
validateNumber(vector.z, `${name}.z`);
|
|
77
|
+
validateNumber(vector.w, `${name}.w`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates a 2D vector with validation.
|
|
81
|
+
* @param x - X component
|
|
82
|
+
* @param y - Y component
|
|
83
|
+
* @returns Validated 2D vector
|
|
84
|
+
*/
|
|
85
|
+
export function createVector2D(x, y) {
|
|
86
|
+
validateNumber(x, 'x');
|
|
87
|
+
validateNumber(y, 'y');
|
|
88
|
+
return { x, y };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates a 3D vector with validation.
|
|
92
|
+
* @param x - X component
|
|
93
|
+
* @param y - Y component
|
|
94
|
+
* @param z - Z component
|
|
95
|
+
* @returns Validated 3D vector
|
|
96
|
+
*/
|
|
97
|
+
export function createVector3D(x, y, z) {
|
|
98
|
+
validateNumber(x, 'x');
|
|
99
|
+
validateNumber(y, 'y');
|
|
100
|
+
validateNumber(z, 'z');
|
|
101
|
+
return { x, y, z };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates a 4D vector with validation.
|
|
105
|
+
* @param x - X component
|
|
106
|
+
* @param y - Y component
|
|
107
|
+
* @param z - Z component
|
|
108
|
+
* @param w - W component
|
|
109
|
+
* @returns Validated 4D vector
|
|
110
|
+
*/
|
|
111
|
+
export function createVector4D(x, y, z, w) {
|
|
112
|
+
validateNumber(x, 'x');
|
|
113
|
+
validateNumber(y, 'y');
|
|
114
|
+
validateNumber(z, 'z');
|
|
115
|
+
validateNumber(w, 'w');
|
|
116
|
+
return { x, y, z, w };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Calculates the magnitude (length) of a 2D vector.
|
|
120
|
+
* @param vector - The 2D vector
|
|
121
|
+
* @returns The magnitude of the vector (non-negative)
|
|
122
|
+
*/
|
|
123
|
+
export function magnitude2D(vector) {
|
|
124
|
+
validateVector2D(vector);
|
|
125
|
+
const magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
|
126
|
+
return isApproximatelyZero(magnitude) ? 0 : magnitude;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Calculates the squared magnitude of a 2D vector (faster, no sqrt).
|
|
130
|
+
* @param vector - The 2D vector
|
|
131
|
+
* @returns The squared magnitude
|
|
132
|
+
*/
|
|
133
|
+
export function magnitudeSquared2D(vector) {
|
|
134
|
+
validateVector2D(vector);
|
|
135
|
+
return vector.x * vector.x + vector.y * vector.y;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Calculates the magnitude (length) of a 3D vector.
|
|
139
|
+
* @param vector - The 3D vector
|
|
140
|
+
* @returns The magnitude of the vector (non-negative)
|
|
141
|
+
*/
|
|
142
|
+
export function magnitude3D(vector) {
|
|
143
|
+
validateVector3D(vector);
|
|
144
|
+
const magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
|
|
145
|
+
return isApproximatelyZero(magnitude) ? 0 : magnitude;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Calculates the squared magnitude of a 3D vector (faster, no sqrt).
|
|
149
|
+
* @param vector - The 3D vector
|
|
150
|
+
* @returns The squared magnitude
|
|
151
|
+
*/
|
|
152
|
+
export function magnitudeSquared3D(vector) {
|
|
153
|
+
validateVector3D(vector);
|
|
154
|
+
return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Calculates the magnitude (length) of a 4D vector.
|
|
158
|
+
* @param vector - The 4D vector
|
|
159
|
+
* @returns The magnitude of the vector (non-negative)
|
|
160
|
+
*/
|
|
161
|
+
export function magnitude4D(vector) {
|
|
162
|
+
validateVector4D(vector);
|
|
163
|
+
const magnitude = Math.sqrt(vector.x * vector.x +
|
|
164
|
+
vector.y * vector.y +
|
|
165
|
+
vector.z * vector.z +
|
|
166
|
+
vector.w * vector.w);
|
|
167
|
+
return isApproximatelyZero(magnitude) ? 0 : magnitude;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Calculates the squared magnitude of a 4D vector (faster, no sqrt).
|
|
171
|
+
* @param vector - The 4D vector
|
|
172
|
+
* @returns The squared magnitude
|
|
173
|
+
*/
|
|
174
|
+
export function magnitudeSquared4D(vector) {
|
|
175
|
+
validateVector4D(vector);
|
|
176
|
+
return (vector.x * vector.x +
|
|
177
|
+
vector.y * vector.y +
|
|
178
|
+
vector.z * vector.z +
|
|
179
|
+
vector.w * vector.w);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Normalizes a 2D vector (makes it unit length).
|
|
183
|
+
* @param vector - The 2D vector to normalize
|
|
184
|
+
* @returns The normalized vector (unit vector)
|
|
185
|
+
* @throws {Error} If the vector has zero or near-zero magnitude
|
|
186
|
+
*/
|
|
187
|
+
export function normalize2D(vector) {
|
|
188
|
+
validateVector2D(vector);
|
|
189
|
+
const mag = magnitude2D(vector);
|
|
190
|
+
if (isApproximatelyZero(mag)) {
|
|
191
|
+
throw new Error('Cannot normalize zero or near-zero vector');
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
x: vector.x / mag,
|
|
195
|
+
y: vector.y / mag,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Safely normalizes a 2D vector, returns zero vector if magnitude is near zero.
|
|
200
|
+
* @param vector - The 2D vector to normalize
|
|
201
|
+
* @returns The normalized vector or zero vector
|
|
202
|
+
*/
|
|
203
|
+
export function safeNormalize2D(vector) {
|
|
204
|
+
validateVector2D(vector);
|
|
205
|
+
const mag = magnitude2D(vector);
|
|
206
|
+
if (isApproximatelyZero(mag)) {
|
|
207
|
+
return { x: 0, y: 0 };
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
x: vector.x / mag,
|
|
211
|
+
y: vector.y / mag,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Normalizes a 3D vector (makes it unit length).
|
|
216
|
+
* @param vector - The 3D vector to normalize
|
|
217
|
+
* @returns The normalized vector (unit vector)
|
|
218
|
+
* @throws {Error} If the vector has zero or near-zero magnitude
|
|
219
|
+
*/
|
|
220
|
+
export function normalize3D(vector) {
|
|
221
|
+
validateVector3D(vector);
|
|
222
|
+
const mag = magnitude3D(vector);
|
|
223
|
+
if (isApproximatelyZero(mag)) {
|
|
224
|
+
throw new Error('Cannot normalize zero or near-zero vector');
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
x: vector.x / mag,
|
|
228
|
+
y: vector.y / mag,
|
|
229
|
+
z: vector.z / mag,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Safely normalizes a 3D vector, returns zero vector if magnitude is near zero.
|
|
234
|
+
* @param vector - The 3D vector to normalize
|
|
235
|
+
* @returns The normalized vector or zero vector
|
|
236
|
+
*/
|
|
237
|
+
export function safeNormalize3D(vector) {
|
|
238
|
+
validateVector3D(vector);
|
|
239
|
+
const mag = magnitude3D(vector);
|
|
240
|
+
if (isApproximatelyZero(mag)) {
|
|
241
|
+
return { x: 0, y: 0, z: 0 };
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
x: vector.x / mag,
|
|
245
|
+
y: vector.y / mag,
|
|
246
|
+
z: vector.z / mag,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Normalizes a 4D vector (makes it unit length).
|
|
251
|
+
* @param vector - The 4D vector to normalize
|
|
252
|
+
* @returns The normalized vector (unit vector)
|
|
253
|
+
* @throws {Error} If the vector has zero or near-zero magnitude
|
|
254
|
+
*/
|
|
255
|
+
export function normalize4D(vector) {
|
|
256
|
+
validateVector4D(vector);
|
|
257
|
+
const mag = magnitude4D(vector);
|
|
258
|
+
if (isApproximatelyZero(mag)) {
|
|
259
|
+
throw new Error('Cannot normalize zero or near-zero vector');
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
x: vector.x / mag,
|
|
263
|
+
y: vector.y / mag,
|
|
264
|
+
z: vector.z / mag,
|
|
265
|
+
w: vector.w / mag,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Safely normalizes a 4D vector, returns zero vector if magnitude is near zero.
|
|
270
|
+
* @param vector - The 4D vector to normalize
|
|
271
|
+
* @returns The normalized vector or zero vector
|
|
272
|
+
*/
|
|
273
|
+
export function safeNormalize4D(vector) {
|
|
274
|
+
validateVector4D(vector);
|
|
275
|
+
const mag = magnitude4D(vector);
|
|
276
|
+
if (isApproximatelyZero(mag)) {
|
|
277
|
+
return { x: 0, y: 0, z: 0, w: 0 };
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
x: vector.x / mag,
|
|
281
|
+
y: vector.y / mag,
|
|
282
|
+
z: vector.z / mag,
|
|
283
|
+
w: vector.w / mag,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Calculates the dot product of two 2D vectors.
|
|
288
|
+
* @param a - First vector
|
|
289
|
+
* @param b - Second vector
|
|
290
|
+
* @returns The dot product (scalar)
|
|
291
|
+
*/
|
|
292
|
+
export function dotProduct2D(a, b) {
|
|
293
|
+
validateVector2D(a, 'First vector');
|
|
294
|
+
validateVector2D(b, 'Second vector');
|
|
295
|
+
return a.x * b.x + a.y * b.y;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Calculates the dot product of two 3D vectors.
|
|
299
|
+
* @param a - First vector
|
|
300
|
+
* @param b - Second vector
|
|
301
|
+
* @returns The dot product (scalar)
|
|
302
|
+
*/
|
|
303
|
+
export function dotProduct3D(a, b) {
|
|
304
|
+
validateVector3D(a, 'First vector');
|
|
305
|
+
validateVector3D(b, 'Second vector');
|
|
306
|
+
return a.x * b.x + a.y * b.y + a.z * b.z;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Calculates the dot product of two 4D vectors.
|
|
310
|
+
* @param a - First vector
|
|
311
|
+
* @param b - Second vector
|
|
312
|
+
* @returns The dot product (scalar)
|
|
313
|
+
*/
|
|
314
|
+
export function dotProduct4D(a, b) {
|
|
315
|
+
validateVector4D(a, 'First vector');
|
|
316
|
+
validateVector4D(b, 'Second vector');
|
|
317
|
+
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Calculates the cross product of two 3D vectors.
|
|
321
|
+
* @param a - First vector
|
|
322
|
+
* @param b - Second vector
|
|
323
|
+
* @returns The cross product vector (orthogonal to both inputs)
|
|
324
|
+
*/
|
|
325
|
+
export function crossProduct3D(a, b) {
|
|
326
|
+
validateVector3D(a, 'First vector');
|
|
327
|
+
validateVector3D(b, 'Second vector');
|
|
328
|
+
return {
|
|
329
|
+
x: a.y * b.z - a.z * b.y,
|
|
330
|
+
y: a.z * b.x - a.x * b.z,
|
|
331
|
+
z: a.x * b.y - a.y * b.x,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Adds two 2D vectors.
|
|
336
|
+
* @param a - First vector
|
|
337
|
+
* @param b - Second vector
|
|
338
|
+
* @returns The sum vector
|
|
339
|
+
*/
|
|
340
|
+
export function addVectors2D(a, b) {
|
|
341
|
+
validateVector2D(a, 'First vector');
|
|
342
|
+
validateVector2D(b, 'Second vector');
|
|
343
|
+
return {
|
|
344
|
+
x: a.x + b.x,
|
|
345
|
+
y: a.y + b.y,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Adds two 3D vectors.
|
|
350
|
+
* @param a - First vector
|
|
351
|
+
* @param b - Second vector
|
|
352
|
+
* @returns The sum vector
|
|
353
|
+
*/
|
|
354
|
+
export function addVectors3D(a, b) {
|
|
355
|
+
validateVector3D(a, 'First vector');
|
|
356
|
+
validateVector3D(b, 'Second vector');
|
|
357
|
+
return {
|
|
358
|
+
x: a.x + b.x,
|
|
359
|
+
y: a.y + b.y,
|
|
360
|
+
z: a.z + b.z,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Adds two 4D vectors.
|
|
365
|
+
* @param a - First vector
|
|
366
|
+
* @param b - Second vector
|
|
367
|
+
* @returns The sum vector
|
|
368
|
+
*/
|
|
369
|
+
export function addVectors4D(a, b) {
|
|
370
|
+
validateVector4D(a, 'First vector');
|
|
371
|
+
validateVector4D(b, 'Second vector');
|
|
372
|
+
return {
|
|
373
|
+
x: a.x + b.x,
|
|
374
|
+
y: a.y + b.y,
|
|
375
|
+
z: a.z + b.z,
|
|
376
|
+
w: a.w + b.w,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Subtracts two 2D vectors.
|
|
381
|
+
* @param a - First vector
|
|
382
|
+
* @param b - Second vector
|
|
383
|
+
* @returns The difference vector (a - b)
|
|
384
|
+
*/
|
|
385
|
+
export function subtractVectors2D(a, b) {
|
|
386
|
+
validateVector2D(a, 'First vector');
|
|
387
|
+
validateVector2D(b, 'Second vector');
|
|
388
|
+
return {
|
|
389
|
+
x: a.x - b.x,
|
|
390
|
+
y: a.y - b.y,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Subtracts two 3D vectors.
|
|
395
|
+
* @param a - First vector
|
|
396
|
+
* @param b - Second vector
|
|
397
|
+
* @returns The difference vector (a - b)
|
|
398
|
+
*/
|
|
399
|
+
export function subtractVectors3D(a, b) {
|
|
400
|
+
validateVector3D(a, 'First vector');
|
|
401
|
+
validateVector3D(b, 'Second vector');
|
|
402
|
+
return {
|
|
403
|
+
x: a.x - b.x,
|
|
404
|
+
y: a.y - b.y,
|
|
405
|
+
z: a.z - b.z,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Subtracts two 4D vectors.
|
|
410
|
+
* @param a - First vector
|
|
411
|
+
* @param b - Second vector
|
|
412
|
+
* @returns The difference vector (a - b)
|
|
413
|
+
*/
|
|
414
|
+
export function subtractVectors4D(a, b) {
|
|
415
|
+
validateVector4D(a, 'First vector');
|
|
416
|
+
validateVector4D(b, 'Second vector');
|
|
417
|
+
return {
|
|
418
|
+
x: a.x - b.x,
|
|
419
|
+
y: a.y - b.y,
|
|
420
|
+
z: a.z - b.z,
|
|
421
|
+
w: a.w - b.w,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Multiplies a 2D vector by a scalar.
|
|
426
|
+
* @param vector - The vector
|
|
427
|
+
* @param scalar - The scalar
|
|
428
|
+
* @returns The scaled vector
|
|
429
|
+
*/
|
|
430
|
+
export function scaleVector2D(vector, scalar) {
|
|
431
|
+
validateVector2D(vector);
|
|
432
|
+
validateNumber(scalar, 'scalar');
|
|
433
|
+
return {
|
|
434
|
+
x: vector.x * scalar,
|
|
435
|
+
y: vector.y * scalar,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Multiplies a 3D vector by a scalar.
|
|
440
|
+
* @param vector - The vector
|
|
441
|
+
* @param scalar - The scalar
|
|
442
|
+
* @returns The scaled vector
|
|
443
|
+
*/
|
|
444
|
+
export function scaleVector3D(vector, scalar) {
|
|
445
|
+
validateVector3D(vector);
|
|
446
|
+
validateNumber(scalar, 'scalar');
|
|
447
|
+
return {
|
|
448
|
+
x: vector.x * scalar,
|
|
449
|
+
y: vector.y * scalar,
|
|
450
|
+
z: vector.z * scalar,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Multiplies a 4D vector by a scalar.
|
|
455
|
+
* @param vector - The vector
|
|
456
|
+
* @param scalar - The scalar
|
|
457
|
+
* @returns The scaled vector
|
|
458
|
+
*/
|
|
459
|
+
export function scaleVector4D(vector, scalar) {
|
|
460
|
+
validateVector4D(vector);
|
|
461
|
+
validateNumber(scalar, 'scalar');
|
|
462
|
+
return {
|
|
463
|
+
x: vector.x * scalar,
|
|
464
|
+
y: vector.y * scalar,
|
|
465
|
+
z: vector.z * scalar,
|
|
466
|
+
w: vector.w * scalar,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Calculates the angle between two 2D vectors in radians.
|
|
471
|
+
* @param a - First vector
|
|
472
|
+
* @param b - Second vector
|
|
473
|
+
* @returns The angle in radians [0, π]
|
|
474
|
+
* @throws {Error} If either vector has zero or near-zero magnitude
|
|
475
|
+
*/
|
|
476
|
+
export function angleBetween2D(a, b) {
|
|
477
|
+
validateVector2D(a, 'First vector');
|
|
478
|
+
validateVector2D(b, 'Second vector');
|
|
479
|
+
const dot = dotProduct2D(a, b);
|
|
480
|
+
const magA = magnitude2D(a);
|
|
481
|
+
const magB = magnitude2D(b);
|
|
482
|
+
if (isApproximatelyZero(magA) || isApproximatelyZero(magB)) {
|
|
483
|
+
throw new Error('Cannot calculate angle with zero or near-zero vector');
|
|
484
|
+
}
|
|
485
|
+
// Clamp to [-1, 1] to avoid floating-point errors
|
|
486
|
+
const cosTheta = Math.max(-1, Math.min(1, dot / (magA * magB)));
|
|
487
|
+
return Math.acos(cosTheta);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Calculates the angle between two 3D vectors in radians.
|
|
491
|
+
* @param a - First vector
|
|
492
|
+
* @param b - Second vector
|
|
493
|
+
* @returns The angle in radians [0, π]
|
|
494
|
+
* @throws {Error} If either vector has zero or near-zero magnitude
|
|
495
|
+
*/
|
|
496
|
+
export function angleBetween3D(a, b) {
|
|
497
|
+
validateVector3D(a, 'First vector');
|
|
498
|
+
validateVector3D(b, 'Second vector');
|
|
499
|
+
const dot = dotProduct3D(a, b);
|
|
500
|
+
const magA = magnitude3D(a);
|
|
501
|
+
const magB = magnitude3D(b);
|
|
502
|
+
if (isApproximatelyZero(magA) || isApproximatelyZero(magB)) {
|
|
503
|
+
throw new Error('Cannot calculate angle with zero or near-zero vector');
|
|
504
|
+
}
|
|
505
|
+
// Clamp to [-1, 1] to avoid floating-point errors
|
|
506
|
+
const cosTheta = Math.max(-1, Math.min(1, dot / (magA * magB)));
|
|
507
|
+
return Math.acos(cosTheta);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Calculates the signed angle between two 2D vectors in radians.
|
|
511
|
+
* @param a - First vector
|
|
512
|
+
* @param b - Second vector
|
|
513
|
+
* @returns The signed angle in radians (-π to π)
|
|
514
|
+
* @throws {Error} If either vector has zero or near-zero magnitude
|
|
515
|
+
*/
|
|
516
|
+
export function signedAngleBetween2D(a, b) {
|
|
517
|
+
validateVector2D(a, 'First vector');
|
|
518
|
+
validateVector2D(b, 'Second vector');
|
|
519
|
+
const angle = angleBetween2D(a, b);
|
|
520
|
+
const cross = a.x * b.y - a.y * b.x; // 2D cross product (scalar)
|
|
521
|
+
// Return signed angle
|
|
522
|
+
return cross >= 0 ? angle : -angle;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Calculates the distance between two 2D points.
|
|
526
|
+
* @param a - First point
|
|
527
|
+
* @param b - Second point
|
|
528
|
+
* @returns The Euclidean distance (non-negative)
|
|
529
|
+
*/
|
|
530
|
+
export function distance2D(a, b) {
|
|
531
|
+
validateVector2D(a, 'First point');
|
|
532
|
+
validateVector2D(b, 'Second point');
|
|
533
|
+
const dx = b.x - a.x;
|
|
534
|
+
const dy = b.y - a.y;
|
|
535
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
536
|
+
return isApproximatelyZero(distance) ? 0 : distance;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Calculates the squared distance between two 2D points (faster, no sqrt).
|
|
540
|
+
* @param a - First point
|
|
541
|
+
* @param b - Second point
|
|
542
|
+
* @returns The squared Euclidean distance
|
|
543
|
+
*/
|
|
544
|
+
export function distanceSquared2D(a, b) {
|
|
545
|
+
validateVector2D(a, 'First point');
|
|
546
|
+
validateVector2D(b, 'Second point');
|
|
547
|
+
const dx = b.x - a.x;
|
|
548
|
+
const dy = b.y - a.y;
|
|
549
|
+
return dx * dx + dy * dy;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Calculates the distance between two 3D points.
|
|
553
|
+
* @param a - First point
|
|
554
|
+
* @param b - Second point
|
|
555
|
+
* @returns The Euclidean distance (non-negative)
|
|
556
|
+
*/
|
|
557
|
+
export function distance3D(a, b) {
|
|
558
|
+
validateVector3D(a, 'First point');
|
|
559
|
+
validateVector3D(b, 'Second point');
|
|
560
|
+
const dx = b.x - a.x;
|
|
561
|
+
const dy = b.y - a.y;
|
|
562
|
+
const dz = b.z - a.z;
|
|
563
|
+
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
564
|
+
return isApproximatelyZero(distance) ? 0 : distance;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Calculates the squared distance between two 3D points (faster, no sqrt).
|
|
568
|
+
* @param a - First point
|
|
569
|
+
* @param b - Second point
|
|
570
|
+
* @returns The squared Euclidean distance
|
|
571
|
+
*/
|
|
572
|
+
export function distanceSquared3D(a, b) {
|
|
573
|
+
validateVector3D(a, 'First point');
|
|
574
|
+
validateVector3D(b, 'Second point');
|
|
575
|
+
const dx = b.x - a.x;
|
|
576
|
+
const dy = b.y - a.y;
|
|
577
|
+
const dz = b.z - a.z;
|
|
578
|
+
return dx * dx + dy * dy + dz * dz;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Calculates the distance between two 4D points.
|
|
582
|
+
* @param a - First point
|
|
583
|
+
* @param b - Second point
|
|
584
|
+
* @returns The Euclidean distance (non-negative)
|
|
585
|
+
*/
|
|
586
|
+
export function distance4D(a, b) {
|
|
587
|
+
validateVector4D(a, 'First point');
|
|
588
|
+
validateVector4D(b, 'Second point');
|
|
589
|
+
const dx = b.x - a.x;
|
|
590
|
+
const dy = b.y - a.y;
|
|
591
|
+
const dz = b.z - a.z;
|
|
592
|
+
const dw = b.w - a.w;
|
|
593
|
+
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw);
|
|
594
|
+
return isApproximatelyZero(distance) ? 0 : distance;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Calculates the squared distance between two 4D points (faster, no sqrt).
|
|
598
|
+
* @param a - First point
|
|
599
|
+
* @param b - Second point
|
|
600
|
+
* @returns The squared Euclidean distance
|
|
601
|
+
*/
|
|
602
|
+
export function distanceSquared4D(a, b) {
|
|
603
|
+
validateVector4D(a, 'First point');
|
|
604
|
+
validateVector4D(b, 'Second point');
|
|
605
|
+
const dx = b.x - a.x;
|
|
606
|
+
const dy = b.y - a.y;
|
|
607
|
+
const dz = b.z - a.z;
|
|
608
|
+
const dw = b.w - a.w;
|
|
609
|
+
return dx * dx + dy * dy + dz * dz + dw * dw;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Linearly interpolates between two 2D vectors.
|
|
613
|
+
* @param start - Starting vector
|
|
614
|
+
* @param end - Ending vector
|
|
615
|
+
* @param t - Interpolation factor (unclamped, can extrapolate)
|
|
616
|
+
* @returns The interpolated vector
|
|
617
|
+
*/
|
|
618
|
+
export function lerp2D(start, end, t) {
|
|
619
|
+
validateVector2D(start, 'Start vector');
|
|
620
|
+
validateVector2D(end, 'End vector');
|
|
621
|
+
validateNumber(t, 't');
|
|
622
|
+
// Linear interpolation without clamping (allows extrapolation)
|
|
623
|
+
return {
|
|
624
|
+
x: start.x + (end.x - start.x) * t,
|
|
625
|
+
y: start.y + (end.y - start.y) * t,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Linearly interpolates between two 2D vectors with clamping.
|
|
630
|
+
* @param start - Starting vector
|
|
631
|
+
* @param end - Ending vector
|
|
632
|
+
* @param t - Interpolation factor (clamped to [0, 1])
|
|
633
|
+
* @returns The interpolated vector within the segment
|
|
634
|
+
*/
|
|
635
|
+
export function lerpClamped2D(start, end, t) {
|
|
636
|
+
validateVector2D(start, 'Start vector');
|
|
637
|
+
validateVector2D(end, 'End vector');
|
|
638
|
+
validateNumber(t, 't');
|
|
639
|
+
// Clamp t to [0, 1]
|
|
640
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
641
|
+
return {
|
|
642
|
+
x: start.x + (end.x - start.x) * clampedT,
|
|
643
|
+
y: start.y + (end.y - start.y) * clampedT,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Linearly interpolates between two 3D vectors.
|
|
648
|
+
* @param start - Starting vector
|
|
649
|
+
* @param end - Ending vector
|
|
650
|
+
* @param t - Interpolation factor (unclamped, can extrapolate)
|
|
651
|
+
* @returns The interpolated vector
|
|
652
|
+
*/
|
|
653
|
+
export function lerp3D(start, end, t) {
|
|
654
|
+
validateVector3D(start, 'Start vector');
|
|
655
|
+
validateVector3D(end, 'End vector');
|
|
656
|
+
validateNumber(t, 't');
|
|
657
|
+
return {
|
|
658
|
+
x: start.x + (end.x - start.x) * t,
|
|
659
|
+
y: start.y + (end.y - start.y) * t,
|
|
660
|
+
z: start.z + (end.z - start.z) * t,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Linearly interpolates between two 3D vectors with clamping.
|
|
665
|
+
* @param start - Starting vector
|
|
666
|
+
* @param end - Ending vector
|
|
667
|
+
* @param t - Interpolation factor (clamped to [0, 1])
|
|
668
|
+
* @returns The interpolated vector within the segment
|
|
669
|
+
*/
|
|
670
|
+
export function lerpClamped3D(start, end, t) {
|
|
671
|
+
validateVector3D(start, 'Start vector');
|
|
672
|
+
validateVector3D(end, 'End vector');
|
|
673
|
+
validateNumber(t, 't');
|
|
674
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
675
|
+
return {
|
|
676
|
+
x: start.x + (end.x - start.x) * clampedT,
|
|
677
|
+
y: start.y + (end.y - start.y) * clampedT,
|
|
678
|
+
z: start.z + (end.z - start.z) * clampedT,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Linearly interpolates between two 4D vectors.
|
|
683
|
+
* @param start - Starting vector
|
|
684
|
+
* @param end - Ending vector
|
|
685
|
+
* @param t - Interpolation factor (unclamped, can extrapolate)
|
|
686
|
+
* @returns The interpolated vector
|
|
687
|
+
*/
|
|
688
|
+
export function lerp4D(start, end, t) {
|
|
689
|
+
validateVector4D(start, 'Start vector');
|
|
690
|
+
validateVector4D(end, 'End vector');
|
|
691
|
+
validateNumber(t, 't');
|
|
692
|
+
return {
|
|
693
|
+
x: start.x + (end.x - start.x) * t,
|
|
694
|
+
y: start.y + (end.y - start.y) * t,
|
|
695
|
+
z: start.z + (end.z - start.z) * t,
|
|
696
|
+
w: start.w + (end.w - start.w) * t,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Linearly interpolates between two 4D vectors with clamping.
|
|
701
|
+
* @param start - Starting vector
|
|
702
|
+
* @param end - Ending vector
|
|
703
|
+
* @param t - Interpolation factor (clamped to [0, 1])
|
|
704
|
+
* @returns The interpolated vector within the segment
|
|
705
|
+
*/
|
|
706
|
+
export function lerpClamped4D(start, end, t) {
|
|
707
|
+
validateVector4D(start, 'Start vector');
|
|
708
|
+
validateVector4D(end, 'End vector');
|
|
709
|
+
validateNumber(t, 't');
|
|
710
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
711
|
+
return {
|
|
712
|
+
x: start.x + (end.x - start.x) * clampedT,
|
|
713
|
+
y: start.y + (end.y - start.y) * clampedT,
|
|
714
|
+
z: start.z + (end.z - start.z) * clampedT,
|
|
715
|
+
w: start.w + (end.w - start.w) * clampedT,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Spherically interpolates between two 3D vectors (SLERP).
|
|
720
|
+
* @param start - Starting unit vector
|
|
721
|
+
* @param end - Ending unit vector
|
|
722
|
+
* @param t - Interpolation factor (clamped to [0, 1])
|
|
723
|
+
* @returns The interpolated unit vector
|
|
724
|
+
* @throws {Error} If vectors are not unit length or have near-zero magnitude
|
|
725
|
+
*/
|
|
726
|
+
export function slerp3D(start, end, t) {
|
|
727
|
+
validateVector3D(start, 'Start vector');
|
|
728
|
+
validateVector3D(end, 'End vector');
|
|
729
|
+
validateNumber(t, 't');
|
|
730
|
+
// Ensure vectors are normalized
|
|
731
|
+
const startMag = magnitude3D(start);
|
|
732
|
+
const endMag = magnitude3D(end);
|
|
733
|
+
if (Math.abs(startMag - 1) > 1e-8 || Math.abs(endMag - 1) > 1e-8) {
|
|
734
|
+
throw new Error('SLERP requires unit vectors');
|
|
735
|
+
}
|
|
736
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
737
|
+
const dot = dotProduct3D(start, end);
|
|
738
|
+
// Clamp dot product to handle floating-point errors
|
|
739
|
+
const clampedDot = Math.max(-1, Math.min(1, dot));
|
|
740
|
+
// If vectors are nearly identical, use linear interpolation
|
|
741
|
+
if (clampedDot > 0.9995) {
|
|
742
|
+
return normalize3D(lerp3D(start, end, clampedT));
|
|
743
|
+
}
|
|
744
|
+
const theta = Math.acos(clampedDot);
|
|
745
|
+
const sinTheta = Math.sin(theta);
|
|
746
|
+
const factor1 = Math.sin((1 - clampedT) * theta) / sinTheta;
|
|
747
|
+
const factor2 = Math.sin(clampedT * theta) / sinTheta;
|
|
748
|
+
return addVectors3D(scaleVector3D(start, factor1), scaleVector3D(end, factor2));
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Smoothly interpolates between two vectors using cubic Hermite interpolation.
|
|
752
|
+
* @param start - Starting vector
|
|
753
|
+
* @param end - Ending vector
|
|
754
|
+
* @param t - Interpolation factor (unclamped)
|
|
755
|
+
* @returns The interpolated vector
|
|
756
|
+
*/
|
|
757
|
+
export function smoothstep2D(start, end, t) {
|
|
758
|
+
validateVector2D(start, 'Start vector');
|
|
759
|
+
validateVector2D(end, 'End vector');
|
|
760
|
+
validateNumber(t, 't');
|
|
761
|
+
// Apply smoothstep function: 3t² - 2t³
|
|
762
|
+
const smoothedT = t * t * (3 - 2 * t);
|
|
763
|
+
return {
|
|
764
|
+
x: start.x + (end.x - start.x) * smoothedT,
|
|
765
|
+
y: start.y + (end.y - start.y) * smoothedT,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Smoothly interpolates with clamping.
|
|
770
|
+
* @param start - Starting vector
|
|
771
|
+
* @param end - Ending vector
|
|
772
|
+
* @param t - Interpolation factor (clamped to [0, 1])
|
|
773
|
+
* @returns The interpolated vector
|
|
774
|
+
*/
|
|
775
|
+
export function smoothstepClamped2D(start, end, t) {
|
|
776
|
+
validateVector2D(start, 'Start vector');
|
|
777
|
+
validateVector2D(end, 'End vector');
|
|
778
|
+
validateNumber(t, 't');
|
|
779
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
780
|
+
const smoothedT = clampedT * clampedT * (3 - 2 * clampedT);
|
|
781
|
+
return {
|
|
782
|
+
x: start.x + (end.x - start.x) * smoothedT,
|
|
783
|
+
y: start.y + (end.y - start.y) * smoothedT,
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Smoother interpolation using quintic function.
|
|
788
|
+
* @param start - Starting vector
|
|
789
|
+
* @param end - Ending vector
|
|
790
|
+
* @param t - Interpolation factor (unclamped)
|
|
791
|
+
* @returns The interpolated vector
|
|
792
|
+
*/
|
|
793
|
+
export function smootherstep2D(start, end, t) {
|
|
794
|
+
validateVector2D(start, 'Start vector');
|
|
795
|
+
validateVector2D(end, 'End vector');
|
|
796
|
+
validateNumber(t, 't');
|
|
797
|
+
// Apply smootherstep function: 6t⁵ - 15t⁴ + 10t³
|
|
798
|
+
const smootherT = t * t * t * (t * (t * 6 - 15) + 10);
|
|
799
|
+
return {
|
|
800
|
+
x: start.x + (end.x - start.x) * smootherT,
|
|
801
|
+
y: start.y + (end.y - start.y) * smootherT,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Interpolates between three vectors (quadratic bezier).
|
|
806
|
+
* @param p0 - Start point
|
|
807
|
+
* @param p1 - Control point
|
|
808
|
+
* @param p2 - End point
|
|
809
|
+
* @param t - Interpolation factor
|
|
810
|
+
* @returns The interpolated vector
|
|
811
|
+
*/
|
|
812
|
+
export function quadraticBezier2D(p0, p1, p2, t) {
|
|
813
|
+
validateVector2D(p0, 'Start point');
|
|
814
|
+
validateVector2D(p1, 'Control point');
|
|
815
|
+
validateVector2D(p2, 'End point');
|
|
816
|
+
validateNumber(t, 't');
|
|
817
|
+
const oneMinusT = 1 - t;
|
|
818
|
+
// Quadratic Bezier formula: (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
|
|
819
|
+
const x = oneMinusT * oneMinusT * p0.x + 2 * oneMinusT * t * p1.x + t * t * p2.x;
|
|
820
|
+
const y = oneMinusT * oneMinusT * p0.y + 2 * oneMinusT * t * p1.y + t * t * p2.y;
|
|
821
|
+
return { x, y };
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Interpolates between four vectors (cubic bezier).
|
|
825
|
+
* @param p0 - Start point
|
|
826
|
+
* @param p1 - First control point
|
|
827
|
+
* @param p2 - Second control point
|
|
828
|
+
* @param p3 - End point
|
|
829
|
+
* @param t - Interpolation factor
|
|
830
|
+
* @returns The interpolated vector
|
|
831
|
+
*/
|
|
832
|
+
export function cubicBezier2D(p0, p1, p2, p3, t) {
|
|
833
|
+
validateVector2D(p0, 'Start point');
|
|
834
|
+
validateVector2D(p1, 'First control point');
|
|
835
|
+
validateVector2D(p2, 'Second control point');
|
|
836
|
+
validateVector2D(p3, 'End point');
|
|
837
|
+
validateNumber(t, 't');
|
|
838
|
+
const oneMinusT = 1 - t;
|
|
839
|
+
const oneMinusT2 = oneMinusT * oneMinusT;
|
|
840
|
+
const oneMinusT3 = oneMinusT2 * oneMinusT;
|
|
841
|
+
const t2 = t * t;
|
|
842
|
+
const t3 = t2 * t;
|
|
843
|
+
// Cubic Bezier formula:
|
|
844
|
+
// (1-t)³P₀ + 3(1-t)²tP₁ + 3(1-t)t²P₂ + t³P₃
|
|
845
|
+
const x = oneMinusT3 * p0.x +
|
|
846
|
+
3 * oneMinusT2 * t * p1.x +
|
|
847
|
+
3 * oneMinusT * t2 * p2.x +
|
|
848
|
+
t3 * p3.x;
|
|
849
|
+
const y = oneMinusT3 * p0.y +
|
|
850
|
+
3 * oneMinusT2 * t * p1.y +
|
|
851
|
+
3 * oneMinusT * t2 * p2.y +
|
|
852
|
+
t3 * p3.y;
|
|
853
|
+
return { x, y };
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Maps a value from one range to another range.
|
|
857
|
+
* @param value - The value to map
|
|
858
|
+
* @param inMin - Minimum of input range
|
|
859
|
+
* @param inMax - Maximum of input range
|
|
860
|
+
* @param outMin - Minimum of output range
|
|
861
|
+
* @param outMax - Maximum of output range
|
|
862
|
+
* @returns The mapped value
|
|
863
|
+
*/
|
|
864
|
+
export function mapRange(value, inMin, inMax, outMin, outMax) {
|
|
865
|
+
validateNumber(value, 'value');
|
|
866
|
+
validateNumber(inMin, 'inMin');
|
|
867
|
+
validateNumber(inMax, 'inMax');
|
|
868
|
+
validateNumber(outMin, 'outMin');
|
|
869
|
+
validateNumber(outMax, 'outMax');
|
|
870
|
+
// Avoid division by zero
|
|
871
|
+
if (approximatelyEqual(inMin, inMax)) {
|
|
872
|
+
return outMin;
|
|
873
|
+
}
|
|
874
|
+
return outMin + ((outMax - outMin) * (value - inMin)) / (inMax - inMin);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Maps and clamps a value from one range to another.
|
|
878
|
+
* @param value - The value to map
|
|
879
|
+
* @param inMin - Minimum of input range
|
|
880
|
+
* @param inMax - Maximum of input range
|
|
881
|
+
* @param outMin - Minimum of output range
|
|
882
|
+
* @param outMax - Maximum of output range
|
|
883
|
+
* @returns The mapped value, clamped to output range
|
|
884
|
+
*/
|
|
885
|
+
export function mapRangeClamped(value, inMin, inMax, outMin, outMax) {
|
|
886
|
+
validateNumber(value, 'value');
|
|
887
|
+
validateNumber(inMin, 'inMin');
|
|
888
|
+
validateNumber(inMax, 'inMax');
|
|
889
|
+
validateNumber(outMin, 'outMin');
|
|
890
|
+
validateNumber(outMax, 'outMax');
|
|
891
|
+
// Avoid division by zero
|
|
892
|
+
if (approximatelyEqual(inMin, inMax)) {
|
|
893
|
+
return outMin;
|
|
894
|
+
}
|
|
895
|
+
const t = (value - inMin) / (inMax - inMin);
|
|
896
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
897
|
+
return outMin + (outMax - outMin) * clampedT;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Rotates a 2D vector by an angle (in radians).
|
|
901
|
+
* @param vector - The vector to rotate
|
|
902
|
+
* @param angle - The angle in radians
|
|
903
|
+
* @returns The rotated vector
|
|
904
|
+
*/
|
|
905
|
+
export function rotate2D(vector, angle) {
|
|
906
|
+
validateVector2D(vector);
|
|
907
|
+
validateNumber(angle, 'angle');
|
|
908
|
+
const cos = Math.cos(angle);
|
|
909
|
+
const sin = Math.sin(angle);
|
|
910
|
+
return {
|
|
911
|
+
x: vector.x * cos - vector.y * sin,
|
|
912
|
+
y: vector.x * sin + vector.y * cos,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Rotates a 2D vector by an angle (in degrees).
|
|
917
|
+
* @param vector - The vector to rotate
|
|
918
|
+
* @param degrees - The angle in degrees
|
|
919
|
+
* @returns The rotated vector
|
|
920
|
+
*/
|
|
921
|
+
export function rotate2DDegrees(vector, degrees) {
|
|
922
|
+
return rotate2D(vector, degrees * (Math.PI / 180));
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Reflects a 2D vector across a normal vector.
|
|
926
|
+
* @param vector - The vector to reflect
|
|
927
|
+
* @param normal - The normal vector (must be unit length)
|
|
928
|
+
* @returns The reflected vector
|
|
929
|
+
* @throws {Error} If normal is not unit length
|
|
930
|
+
*/
|
|
931
|
+
export function reflect2D(vector, normal) {
|
|
932
|
+
validateVector2D(vector, 'Vector');
|
|
933
|
+
validateVector2D(normal, 'Normal');
|
|
934
|
+
// Ensure normal is unit vector
|
|
935
|
+
const normalMag = magnitude2D(normal);
|
|
936
|
+
if (Math.abs(normalMag - 1) > 1e-10) {
|
|
937
|
+
throw new Error('Normal vector must be unit length for reflection');
|
|
938
|
+
}
|
|
939
|
+
const dot = dotProduct2D(vector, normal);
|
|
940
|
+
return {
|
|
941
|
+
x: vector.x - 2 * dot * normal.x,
|
|
942
|
+
y: vector.y - 2 * dot * normal.y,
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Projects vector a onto vector b.
|
|
947
|
+
* @param a - The vector to project
|
|
948
|
+
* @param b - The vector to project onto
|
|
949
|
+
* @returns The projection of a onto b
|
|
950
|
+
* @throws {Error} If b has zero or near-zero magnitude
|
|
951
|
+
*/
|
|
952
|
+
export function project2D(a, b) {
|
|
953
|
+
validateVector2D(a, 'First vector');
|
|
954
|
+
validateVector2D(b, 'Second vector');
|
|
955
|
+
const bMagSquared = magnitudeSquared2D(b);
|
|
956
|
+
if (isApproximatelyZero(bMagSquared)) {
|
|
957
|
+
throw new Error('Cannot project onto zero or near-zero vector');
|
|
958
|
+
}
|
|
959
|
+
const scale = dotProduct2D(a, b) / bMagSquared;
|
|
960
|
+
return {
|
|
961
|
+
x: b.x * scale,
|
|
962
|
+
y: b.y * scale,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Checks if two 2D vectors are approximately equal.
|
|
967
|
+
* @param a - First vector
|
|
968
|
+
* @param b - Second vector
|
|
969
|
+
* @returns True if vectors are approximately equal
|
|
970
|
+
*/
|
|
971
|
+
export function vectorsEqual2D(a, b) {
|
|
972
|
+
validateVector2D(a, 'First vector');
|
|
973
|
+
validateVector2D(b, 'Second vector');
|
|
974
|
+
return approximatelyEqual(a.x, b.x) && approximatelyEqual(a.y, b.y);
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Checks if two 3D vectors are approximately equal.
|
|
978
|
+
* @param a - First vector
|
|
979
|
+
* @param b - Second vector
|
|
980
|
+
* @returns True if vectors are approximately equal
|
|
981
|
+
*/
|
|
982
|
+
export function vectorsEqual3D(a, b) {
|
|
983
|
+
validateVector3D(a, 'First vector');
|
|
984
|
+
validateVector3D(b, 'Second vector');
|
|
985
|
+
return (approximatelyEqual(a.x, b.x) &&
|
|
986
|
+
approximatelyEqual(a.y, b.y) &&
|
|
987
|
+
approximatelyEqual(a.z, b.z));
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Checks if two 4D vectors are approximately equal.
|
|
991
|
+
* @param a - First vector
|
|
992
|
+
* @param b - Second vector
|
|
993
|
+
* @returns True if vectors are approximately equal
|
|
994
|
+
*/
|
|
995
|
+
export function vectorsEqual4D(a, b) {
|
|
996
|
+
validateVector4D(a, 'First vector');
|
|
997
|
+
validateVector4D(b, 'Second vector');
|
|
998
|
+
return (approximatelyEqual(a.x, b.x) &&
|
|
999
|
+
approximatelyEqual(a.y, b.y) &&
|
|
1000
|
+
approximatelyEqual(a.z, b.z) &&
|
|
1001
|
+
approximatelyEqual(a.w, b.w));
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Calculates the midpoint between two 2D points.
|
|
1005
|
+
* @param a - First point
|
|
1006
|
+
* @param b - Second point
|
|
1007
|
+
* @returns The midpoint
|
|
1008
|
+
*/
|
|
1009
|
+
export function midpoint2D(a, b) {
|
|
1010
|
+
validateVector2D(a, 'First point');
|
|
1011
|
+
validateVector2D(b, 'Second point');
|
|
1012
|
+
return {
|
|
1013
|
+
x: (a.x + b.x) / 2,
|
|
1014
|
+
y: (a.y + b.y) / 2,
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Calculates the centroid of multiple 2D points.
|
|
1019
|
+
* @param points - Array of points
|
|
1020
|
+
* @returns The centroid
|
|
1021
|
+
* @throws {Error} If points array is empty
|
|
1022
|
+
*/
|
|
1023
|
+
export function centroid2D(points) {
|
|
1024
|
+
if (!Array.isArray(points) || points.length === 0) {
|
|
1025
|
+
throw new Error('Points array must not be empty');
|
|
1026
|
+
}
|
|
1027
|
+
let sumX = 0;
|
|
1028
|
+
let sumY = 0;
|
|
1029
|
+
for (let i = 0; i < points.length; i++) {
|
|
1030
|
+
validateVector2D(points[i], `points[${i}]`);
|
|
1031
|
+
sumX += points[i].x;
|
|
1032
|
+
sumY += points[i].y;
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
x: sumX / points.length,
|
|
1036
|
+
y: sumY / points.length,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Negates a 2D vector (reverses its direction).
|
|
1041
|
+
* @param vector - The vector to negate
|
|
1042
|
+
* @returns The negated vector
|
|
1043
|
+
*/
|
|
1044
|
+
export function negateVector2D(vector) {
|
|
1045
|
+
validateVector2D(vector);
|
|
1046
|
+
return { x: -vector.x, y: -vector.y };
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Negates a 3D vector (reverses its direction).
|
|
1050
|
+
* @param vector - The vector to negate
|
|
1051
|
+
* @returns The negated vector
|
|
1052
|
+
*/
|
|
1053
|
+
export function negateVector3D(vector) {
|
|
1054
|
+
validateVector3D(vector);
|
|
1055
|
+
return { x: -vector.x, y: -vector.y, z: -vector.z };
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Negates a 4D vector (reverses its direction).
|
|
1059
|
+
* @param vector - The vector to negate
|
|
1060
|
+
* @returns The negated vector
|
|
1061
|
+
*/
|
|
1062
|
+
export function negateVector4D(vector) {
|
|
1063
|
+
validateVector4D(vector);
|
|
1064
|
+
return { x: -vector.x, y: -vector.y, z: -vector.z, w: -vector.w };
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Calculates the perpendicular 2D vector (90° rotation).
|
|
1068
|
+
* @param vector - The vector
|
|
1069
|
+
* @param clockwise - True for clockwise rotation, false for counterclockwise
|
|
1070
|
+
* @returns The perpendicular vector
|
|
1071
|
+
*/
|
|
1072
|
+
export function perpendicular2D(vector, clockwise = true) {
|
|
1073
|
+
validateVector2D(vector);
|
|
1074
|
+
if (clockwise) {
|
|
1075
|
+
return { x: vector.y, y: -vector.x }; // 90° clockwise
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
return { x: -vector.y, y: vector.x }; // 90° counterclockwise
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Creates a zero 2D vector.
|
|
1083
|
+
* @returns Zero vector
|
|
1084
|
+
*/
|
|
1085
|
+
export function zeroVector2D() {
|
|
1086
|
+
return { x: 0, y: 0 };
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Creates a zero 3D vector.
|
|
1090
|
+
* @returns Zero vector
|
|
1091
|
+
*/
|
|
1092
|
+
export function zeroVector3D() {
|
|
1093
|
+
return { x: 0, y: 0, z: 0 };
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Creates a zero 4D vector.
|
|
1097
|
+
* @returns Zero vector
|
|
1098
|
+
*/
|
|
1099
|
+
export function zeroVector4D() {
|
|
1100
|
+
return { x: 0, y: 0, z: 0, w: 0 };
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Creates a 2D unit vector along the X axis.
|
|
1104
|
+
* @returns Unit vector (1, 0)
|
|
1105
|
+
*/
|
|
1106
|
+
export function unitX2D() {
|
|
1107
|
+
return { x: 1, y: 0 };
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Creates a 2D unit vector along the Y axis.
|
|
1111
|
+
* @returns Unit vector (0, 1)
|
|
1112
|
+
*/
|
|
1113
|
+
export function unitY2D() {
|
|
1114
|
+
return { x: 0, y: 1 };
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Creates a 3D unit vector along the X axis.
|
|
1118
|
+
* @returns Unit vector (1, 0, 0)
|
|
1119
|
+
*/
|
|
1120
|
+
export function unitX3D() {
|
|
1121
|
+
return { x: 1, y: 0, z: 0 };
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Creates a 3D unit vector along the Y axis.
|
|
1125
|
+
* @returns Unit vector (0, 1, 0)
|
|
1126
|
+
*/
|
|
1127
|
+
export function unitY3D() {
|
|
1128
|
+
return { x: 0, y: 1, z: 0 };
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Creates a 3D unit vector along the Z axis.
|
|
1132
|
+
* @returns Unit vector (0, 0, 1)
|
|
1133
|
+
*/
|
|
1134
|
+
export function unitZ3D() {
|
|
1135
|
+
return { x: 0, y: 0, z: 1 };
|
|
1136
|
+
}
|