@emasoft/svg-matrix 1.0.4 → 1.0.6

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.
@@ -11,21 +11,78 @@ const D = x => (x instanceof Decimal ? x : new Decimal(x));
11
11
  /**
12
12
  * 3D Affine Transforms using 4x4 homogeneous matrices.
13
13
  *
14
+ * ## Mathematical Foundation
15
+ *
16
+ * In 3D computer graphics, affine transformations (translation, rotation, scaling, reflection)
17
+ * are represented using 4x4 matrices in homogeneous coordinates. A 3D point (x, y, z) is
18
+ * represented as a 4D vector [x, y, z, 1]ᵀ, where the extra 1 enables translation via
19
+ * matrix multiplication.
20
+ *
21
+ * The general form of a 4x4 affine transformation matrix is:
22
+ *
23
+ * ```
24
+ * | a b c tx |
25
+ * | d e f ty |
26
+ * | g h i tz |
27
+ * | 0 0 0 1 |
28
+ * ```
29
+ *
30
+ * Where the 3x3 upper-left submatrix handles rotation/scaling/shear, and the rightmost
31
+ * column (tx, ty, tz) handles translation.
32
+ *
33
+ * ## Transform Composition
34
+ *
14
35
  * All transforms return 4x4 Matrix objects that can be composed via multiplication.
15
- * Transform composition is right-to-left: T.mul(R).mul(S) applies S first, then R, then T.
36
+ * **IMPORTANT**: Transform composition is right-to-left: `T.mul(R).mul(S)` applies S first,
37
+ * then R, then T. This follows standard matrix multiplication order.
38
+ *
39
+ * Example: To rotate then translate:
40
+ * ```js
41
+ * const M = translation(10, 0, 0).mul(rotateZ(Math.PI/4));
42
+ * // Applies rotation first, then translation
43
+ * ```
44
+ *
45
+ * ## Rotation Convention
16
46
  *
17
47
  * Rotation matrices use the right-hand rule: positive angles rotate counterclockwise
18
- * when looking down the axis toward the origin.
48
+ * when looking down the axis toward the origin. This means:
49
+ * - rotateX: Y→Z (thumb points +X, fingers curl Y toward Z)
50
+ * - rotateY: Z→X (thumb points +Y, fingers curl Z toward X)
51
+ * - rotateZ: X→Y (thumb points +Z, fingers curl X toward Y)
19
52
  *
20
53
  * @module Transforms3D
21
54
  */
22
55
 
23
56
  /**
24
57
  * Create a 3D translation matrix.
25
- * @param {number|string|Decimal} tx - Translation in X direction
26
- * @param {number|string|Decimal} ty - Translation in Y direction
27
- * @param {number|string|Decimal} tz - Translation in Z direction
28
- * @returns {Matrix} 4x4 translation matrix
58
+ *
59
+ * Produces a 4x4 matrix that translates points by the vector (tx, ty, tz).
60
+ * Translation matrices have the form:
61
+ *
62
+ * ```
63
+ * | 1 0 0 tx |
64
+ * | 0 1 0 ty |
65
+ * | 0 0 1 tz |
66
+ * | 0 0 0 1 |
67
+ * ```
68
+ *
69
+ * When applied to a point P = [x, y, z, 1]ᵀ, the result is [x+tx, y+ty, z+tz, 1]ᵀ.
70
+ *
71
+ * @param {number|string|Decimal} tx - Translation distance in X direction (right/left)
72
+ * @param {number|string|Decimal} ty - Translation distance in Y direction (up/down)
73
+ * @param {number|string|Decimal} tz - Translation distance in Z direction (forward/back)
74
+ * @returns {Matrix} 4x4 translation matrix with arbitrary precision
75
+ *
76
+ * @example
77
+ * // Move point 5 units right, 3 units up, 2 units forward
78
+ * const T = translation(5, 3, 2);
79
+ * const [x, y, z] = applyTransform(T, 0, 0, 0);
80
+ * // Result: x=5, y=3, z=2
81
+ *
82
+ * @example
83
+ * // Compose with rotation: translate then rotate
84
+ * const M = rotateZ(Math.PI/4).mul(translation(10, 0, 0));
85
+ * // First translates by (10,0,0), then rotates around Z
29
86
  */
30
87
  export function translation(tx, ty, tz) {
31
88
  return Matrix.from([
@@ -38,10 +95,40 @@ export function translation(tx, ty, tz) {
38
95
 
39
96
  /**
40
97
  * Create a 3D scaling matrix.
41
- * @param {number|string|Decimal} sx - Scale factor in X direction
42
- * @param {number|string|Decimal} [sy=sx] - Scale factor in Y direction (defaults to sx)
43
- * @param {number|string|Decimal} [sz=sx] - Scale factor in Z direction (defaults to sx)
44
- * @returns {Matrix} 4x4 scaling matrix
98
+ *
99
+ * Produces a 4x4 matrix that scales points by factors (sx, sy, sz) along each axis.
100
+ * Scaling matrices have the form:
101
+ *
102
+ * ```
103
+ * | sx 0 0 0 |
104
+ * | 0 sy 0 0 |
105
+ * | 0 0 sz 0 |
106
+ * | 0 0 0 1 |
107
+ * ```
108
+ *
109
+ * When applied to a point P = [x, y, z, 1]ᵀ, the result is [sx·x, sy·y, sz·z, 1]ᵀ.
110
+ * Uniform scaling occurs when sx = sy = sz. Non-uniform scaling stretches/compresses
111
+ * along individual axes.
112
+ *
113
+ * @param {number|string|Decimal} sx - Scale factor in X direction (width multiplier)
114
+ * @param {number|string|Decimal} [sy=sx] - Scale factor in Y direction (height multiplier, defaults to sx for uniform scaling)
115
+ * @param {number|string|Decimal} [sz=sx] - Scale factor in Z direction (depth multiplier, defaults to sx for uniform scaling)
116
+ * @returns {Matrix} 4x4 scaling matrix with arbitrary precision
117
+ *
118
+ * @example
119
+ * // Uniform scaling: double size in all dimensions
120
+ * const S1 = scale(2);
121
+ * // Equivalent to scale(2, 2, 2)
122
+ *
123
+ * @example
124
+ * // Non-uniform scaling: stretch X by 2, compress Y by 0.5, keep Z unchanged
125
+ * const S2 = scale(2, 0.5, 1);
126
+ * const [x, y, z] = applyTransform(S2, 1, 1, 1);
127
+ * // Result: x=2, y=0.5, z=1
128
+ *
129
+ * @example
130
+ * // Negative scale factors create reflections
131
+ * const mirror = scale(-1, 1, 1); // Flip X axis
45
132
  */
46
133
  export function scale(sx, sy = null, sz = null) {
47
134
  if (sy === null) sy = sx;
@@ -57,13 +144,33 @@ export function scale(sx, sy = null, sz = null) {
57
144
  /**
58
145
  * Create a rotation matrix around the X axis.
59
146
  *
147
+ * Rotates points counterclockwise around the X axis (right-hand rule: thumb points +X,
148
+ * fingers curl from +Y toward +Z). This rotates the YZ plane while leaving X coordinates
149
+ * unchanged.
150
+ *
151
+ * Matrix form:
152
+ * ```
60
153
  * | 1 0 0 0 |
61
154
  * | 0 cos(θ) -sin(θ) 0 |
62
155
  * | 0 sin(θ) cos(θ) 0 |
63
156
  * | 0 0 0 1 |
157
+ * ```
64
158
  *
65
- * @param {number|string|Decimal} theta - Rotation angle in radians
66
- * @returns {Matrix} 4x4 rotation matrix
159
+ * The rotation is applied in the YZ plane: Y' = Y·cos(θ) - Z·sin(θ), Z' = Y·sin(θ) + Z·cos(θ)
160
+ *
161
+ * @param {number|string|Decimal} theta - Rotation angle in radians. Positive angles rotate
162
+ * counterclockwise when looking down +X toward origin.
163
+ * @returns {Matrix} 4x4 rotation matrix with arbitrary precision
164
+ *
165
+ * @example
166
+ * // Rotate 90° around X axis: Y→Z, Z→-Y
167
+ * const R = rotateX(Math.PI / 2);
168
+ * const [x, y, z] = applyTransform(R, 0, 1, 0);
169
+ * // Result: x≈0, y≈0, z≈1 (point on +Y axis moves to +Z axis)
170
+ *
171
+ * @example
172
+ * // Pitch rotation in 3D graphics (nodding head up/down)
173
+ * const pitch = rotateX(-0.1); // Slight downward tilt
67
174
  */
68
175
  export function rotateX(theta) {
69
176
  const t = D(theta);
@@ -80,13 +187,34 @@ export function rotateX(theta) {
80
187
  /**
81
188
  * Create a rotation matrix around the Y axis.
82
189
  *
190
+ * Rotates points counterclockwise around the Y axis (right-hand rule: thumb points +Y,
191
+ * fingers curl from +Z toward +X). This rotates the XZ plane while leaving Y coordinates
192
+ * unchanged.
193
+ *
194
+ * Matrix form:
195
+ * ```
83
196
  * | cos(θ) 0 sin(θ) 0 |
84
197
  * | 0 1 0 0 |
85
198
  * | -sin(θ) 0 cos(θ) 0 |
86
199
  * | 0 0 0 1 |
200
+ * ```
87
201
  *
88
- * @param {number|string|Decimal} theta - Rotation angle in radians
89
- * @returns {Matrix} 4x4 rotation matrix
202
+ * The rotation is applied in the XZ plane: X' = X·cos(θ) + Z·sin(θ), Z' = -X·sin(θ) + Z·cos(θ)
203
+ * Note the sign pattern differs from rotateX/rotateZ due to maintaining right-hand rule consistency.
204
+ *
205
+ * @param {number|string|Decimal} theta - Rotation angle in radians. Positive angles rotate
206
+ * counterclockwise when looking down +Y toward origin.
207
+ * @returns {Matrix} 4x4 rotation matrix with arbitrary precision
208
+ *
209
+ * @example
210
+ * // Rotate 90° around Y axis: Z→X, X→-Z
211
+ * const R = rotateY(Math.PI / 2);
212
+ * const [x, y, z] = applyTransform(R, 0, 0, 1);
213
+ * // Result: x≈1, y≈0, z≈0 (point on +Z axis moves to +X axis)
214
+ *
215
+ * @example
216
+ * // Yaw rotation in 3D graphics (turning head left/right)
217
+ * const yaw = rotateY(0.5); // Turn right by ~28.6°
90
218
  */
91
219
  export function rotateY(theta) {
92
220
  const t = D(theta);
@@ -103,13 +231,35 @@ export function rotateY(theta) {
103
231
  /**
104
232
  * Create a rotation matrix around the Z axis.
105
233
  *
234
+ * Rotates points counterclockwise around the Z axis (right-hand rule: thumb points +Z,
235
+ * fingers curl from +X toward +Y). This rotates the XY plane while leaving Z coordinates
236
+ * unchanged. This is the most common rotation in 2D graphics extended to 3D.
237
+ *
238
+ * Matrix form:
239
+ * ```
106
240
  * | cos(θ) -sin(θ) 0 0 |
107
241
  * | sin(θ) cos(θ) 0 0 |
108
242
  * | 0 0 1 0 |
109
243
  * | 0 0 0 1 |
244
+ * ```
110
245
  *
111
- * @param {number|string|Decimal} theta - Rotation angle in radians
112
- * @returns {Matrix} 4x4 rotation matrix
246
+ * The rotation is applied in the XY plane: X' = X·cos(θ) - Y·sin(θ), Y' = X·sin(θ) + Y·cos(θ)
247
+ * This is identical to the standard 2D rotation extended with Z and W components.
248
+ *
249
+ * @param {number|string|Decimal} theta - Rotation angle in radians. Positive angles rotate
250
+ * counterclockwise when looking down +Z toward origin
251
+ * (standard 2D counterclockwise from above).
252
+ * @returns {Matrix} 4x4 rotation matrix with arbitrary precision
253
+ *
254
+ * @example
255
+ * // Rotate 90° around Z axis: X→Y, Y→-X
256
+ * const R = rotateZ(Math.PI / 2);
257
+ * const [x, y, z] = applyTransform(R, 1, 0, 0);
258
+ * // Result: x≈0, y≈1, z≈0 (point on +X axis moves to +Y axis)
259
+ *
260
+ * @example
261
+ * // Roll rotation in 3D graphics (tilting head left/right)
262
+ * const roll = rotateZ(0.2); // Slight clockwise tilt from viewer perspective
113
263
  */
114
264
  export function rotateZ(theta) {
115
265
  const t = D(theta);
@@ -125,16 +275,57 @@ export function rotateZ(theta) {
125
275
 
126
276
  /**
127
277
  * Create a rotation matrix around an arbitrary axis through the origin.
128
- * Uses Rodrigues' rotation formula.
129
278
  *
130
- * The axis vector (ux, uy, uz) is automatically normalized.
279
+ * Uses Rodrigues' rotation formula to construct a rotation matrix for any axis direction.
280
+ * This is the general form of 3D rotation - rotateX, rotateY, and rotateZ are special cases
281
+ * where the axis is aligned with a coordinate axis.
282
+ *
283
+ * ## Rodrigues' Rotation Formula
284
+ *
285
+ * For a unit axis vector **u** = (ux, uy, uz) and angle θ, the rotation matrix is:
286
+ *
287
+ * **R** = **I** + sin(θ)**K** + (1 - cos(θ))**K**²
131
288
  *
132
- * @param {number|string|Decimal} ux - X component of rotation axis
133
- * @param {number|string|Decimal} uy - Y component of rotation axis
134
- * @param {number|string|Decimal} uz - Z component of rotation axis
289
+ * where **K** is the cross-product matrix of **u**:
290
+ * ```
291
+ * K = | 0 -uz uy |
292
+ * | uz 0 -ux |
293
+ * | -uy ux 0 |
294
+ * ```
295
+ *
296
+ * Alternatively, in component form:
297
+ * ```
298
+ * R_ij = cos(θ)δ_ij + (1-cos(θ))u_i·u_j + sin(θ)ε_ijk·u_k
299
+ * ```
300
+ *
301
+ * where δ_ij is Kronecker delta and ε_ijk is Levi-Civita symbol.
302
+ *
303
+ * The axis vector (ux, uy, uz) is automatically normalized to unit length before use.
304
+ * The rotation follows the right-hand rule: positive angles rotate counterclockwise when
305
+ * looking down the axis toward the origin.
306
+ *
307
+ * @param {number|string|Decimal} ux - X component of rotation axis (need not be normalized)
308
+ * @param {number|string|Decimal} uy - Y component of rotation axis (need not be normalized)
309
+ * @param {number|string|Decimal} uz - Z component of rotation axis (need not be normalized)
135
310
  * @param {number|string|Decimal} theta - Rotation angle in radians
136
- * @returns {Matrix} 4x4 rotation matrix
137
- * @throws {Error} If axis is zero vector
311
+ * @returns {Matrix} 4x4 rotation matrix with arbitrary precision
312
+ * @throws {Error} If axis is zero vector (undefined rotation)
313
+ *
314
+ * @example
315
+ * // Rotate 45° around the diagonal axis (1,1,1)
316
+ * const R = rotateAroundAxis(1, 1, 1, Math.PI / 4);
317
+ * // The axis is automatically normalized to (1/√3, 1/√3, 1/√3)
318
+ *
319
+ * @example
320
+ * // Rotate around arbitrary axis pointing northeast and up
321
+ * const axis = [1, 1, 2]; // Not normalized
322
+ * const angle = Math.PI / 3; // 60 degrees
323
+ * const R = rotateAroundAxis(...axis, angle);
324
+ *
325
+ * @example
326
+ * // Reproducing rotateX using arbitrary axis
327
+ * const Rx = rotateAroundAxis(1, 0, 0, Math.PI / 2);
328
+ * // Equivalent to rotateX(Math.PI / 2)
138
329
  */
139
330
  export function rotateAroundAxis(ux, uy, uz, theta) {
140
331
  const u = [D(ux), D(uy), D(uz)];
@@ -172,16 +363,45 @@ export function rotateAroundAxis(ux, uy, uz, theta) {
172
363
 
173
364
  /**
174
365
  * Create a rotation matrix around an arbitrary axis through a specific point.
175
- * Equivalent to: translate(px, py, pz) × rotateAroundAxis(...) × translate(-px, -py, -pz)
176
366
  *
177
- * @param {number|string|Decimal} ux - X component of rotation axis
178
- * @param {number|string|Decimal} uy - Y component of rotation axis
179
- * @param {number|string|Decimal} uz - Z component of rotation axis
367
+ * By default, rotateAroundAxis rotates around an axis passing through the origin.
368
+ * This function rotates around an axis passing through an arbitrary point (px, py, pz).
369
+ *
370
+ * The transformation is mathematically equivalent to:
371
+ * 1. Translate the pivot point to origin: T(-px, -py, -pz)
372
+ * 2. Perform rotation around axis through origin: R(axis, θ)
373
+ * 3. Translate back: T(px, py, pz)
374
+ *
375
+ * Matrix composition: **M** = T(p) · R(u,θ) · T(-p)
376
+ *
377
+ * This is essential for rotating objects around their center or any specific point
378
+ * rather than around the world origin.
379
+ *
380
+ * @param {number|string|Decimal} ux - X component of rotation axis (need not be normalized)
381
+ * @param {number|string|Decimal} uy - Y component of rotation axis (need not be normalized)
382
+ * @param {number|string|Decimal} uz - Z component of rotation axis (need not be normalized)
180
383
  * @param {number|string|Decimal} theta - Rotation angle in radians
181
- * @param {number|string|Decimal} px - X coordinate of rotation center
182
- * @param {number|string|Decimal} py - Y coordinate of rotation center
183
- * @param {number|string|Decimal} pz - Z coordinate of rotation center
184
- * @returns {Matrix} 4x4 rotation matrix around point (px, py, pz)
384
+ * @param {number|string|Decimal} px - X coordinate of pivot point on rotation axis
385
+ * @param {number|string|Decimal} py - Y coordinate of pivot point on rotation axis
386
+ * @param {number|string|Decimal} pz - Z coordinate of pivot point on rotation axis
387
+ * @returns {Matrix} 4x4 rotation matrix around axis through point (px, py, pz)
388
+ *
389
+ * @example
390
+ * // Rotate a cube around its center at (5, 5, 5) instead of world origin
391
+ * const center = [5, 5, 5];
392
+ * const R = rotateAroundPoint(0, 0, 1, Math.PI/4, ...center);
393
+ * // Rotates 45° around Z axis passing through (5,5,5)
394
+ *
395
+ * @example
396
+ * // Swing a pendulum: rotate around pivot point at top
397
+ * const pivot = [0, 10, 0]; // Pivot 10 units above origin
398
+ * const swing = rotateAroundPoint(0, 0, 1, 0.3, ...pivot);
399
+ * // Rotates around Z axis through (0,10,0)
400
+ *
401
+ * @example
402
+ * // Rotate around diagonal axis through a specific point
403
+ * const R = rotateAroundPoint(1, 1, 1, Math.PI/2, 10, 20, 30);
404
+ * // Complex rotation around axis (1,1,1) passing through (10,20,30)
185
405
  */
186
406
  export function rotateAroundPoint(ux, uy, uz, theta, px, py, pz) {
187
407
  const pxD = D(px), pyD = D(py), pzD = D(pz);
@@ -191,14 +411,49 @@ export function rotateAroundPoint(ux, uy, uz, theta, px, py, pz) {
191
411
  }
192
412
 
193
413
  /**
194
- * Apply a 3D transform matrix to a point.
195
- * Uses homogeneous coordinates with perspective division.
196
- *
197
- * @param {Matrix} M - 4x4 transformation matrix
198
- * @param {number|string|Decimal} x - X coordinate of point
199
- * @param {number|string|Decimal} y - Y coordinate of point
200
- * @param {number|string|Decimal} z - Z coordinate of point
201
- * @returns {Decimal[]} Transformed point as [x', y', z'] array of Decimals
414
+ * Apply a 3D transformation matrix to a point.
415
+ *
416
+ * Converts the 3D point (x, y, z) to homogeneous coordinates [x, y, z, 1]ᵀ,
417
+ * multiplies by the transformation matrix M, then converts back to 3D via
418
+ * perspective division.
419
+ *
420
+ * ## Homogeneous Coordinates
421
+ *
422
+ * A 3D point P = (x, y, z) is represented as a 4D vector [x, y, z, 1]ᵀ.
423
+ * After transformation: P' = M · P = [x', y', z', w']ᵀ
424
+ *
425
+ * The final 3D point is obtained by perspective division:
426
+ * ```
427
+ * result = (x'/w', y'/w', z'/w')
428
+ * ```
429
+ *
430
+ * For affine transformations (translation, rotation, scaling), w' is always 1,
431
+ * so division has no effect. For perspective projections, w' varies and creates
432
+ * the perspective effect.
433
+ *
434
+ * @param {Matrix} M - 4x4 transformation matrix to apply
435
+ * @param {number|string|Decimal} x - X coordinate of input point
436
+ * @param {number|string|Decimal} y - Y coordinate of input point
437
+ * @param {number|string|Decimal} z - Z coordinate of input point
438
+ * @returns {Decimal[]} Transformed point as [x', y', z'] array of Decimal values
439
+ *
440
+ * @example
441
+ * // Translate a point
442
+ * const T = translation(10, 5, 3);
443
+ * const [x, y, z] = applyTransform(T, 0, 0, 0);
444
+ * // Result: x=10, y=5, z=3
445
+ *
446
+ * @example
447
+ * // Apply composed transformation
448
+ * const M = translation(5, 0, 0).mul(rotateZ(Math.PI/4)).mul(scale(2));
449
+ * const [x, y, z] = applyTransform(M, 1, 0, 0);
450
+ * // First scales (2,0,0), then rotates, then translates
451
+ *
452
+ * @example
453
+ * // Transform multiple points in a loop
454
+ * const R = rotateY(Math.PI / 6);
455
+ * const vertices = [[1,0,0], [0,1,0], [0,0,1]];
456
+ * const transformed = vertices.map(([x,y,z]) => applyTransform(R, x, y, z));
202
457
  */
203
458
  export function applyTransform(M, x, y, z) {
204
459
  const P = Matrix.from([[D(x)], [D(y)], [D(z)], [new Decimal(1)]]);
@@ -209,8 +464,35 @@ export function applyTransform(M, x, y, z) {
209
464
  }
210
465
 
211
466
  /**
212
- * Create a reflection matrix across the XY plane (flips Z).
213
- * @returns {Matrix} 4x4 reflection matrix
467
+ * Create a reflection matrix across the XY plane.
468
+ *
469
+ * Reflects points across the XY plane (the plane where z=0), effectively flipping
470
+ * the Z coordinate while leaving X and Y unchanged. This is equivalent to scaling
471
+ * by (1, 1, -1).
472
+ *
473
+ * Matrix form:
474
+ * ```
475
+ * | 1 0 0 0 |
476
+ * | 0 1 0 0 |
477
+ * | 0 0 -1 0 |
478
+ * | 0 0 0 1 |
479
+ * ```
480
+ *
481
+ * Transformation: (x, y, z) → (x, y, -z)
482
+ *
483
+ * @returns {Matrix} 4x4 reflection matrix with determinant -1
484
+ *
485
+ * @example
486
+ * // Mirror a point across XY plane
487
+ * const M = reflectXY();
488
+ * const [x, y, z] = applyTransform(M, 1, 2, 3);
489
+ * // Result: x=1, y=2, z=-3
490
+ *
491
+ * @example
492
+ * // Create symmetric geometry above and below XY plane
493
+ * const original = [2, 3, 5];
494
+ * const mirrored = applyTransform(reflectXY(), ...original);
495
+ * // mirrored = [2, 3, -5]
214
496
  */
215
497
  export function reflectXY() {
216
498
  return Matrix.from([
@@ -222,8 +504,34 @@ export function reflectXY() {
222
504
  }
223
505
 
224
506
  /**
225
- * Create a reflection matrix across the XZ plane (flips Y).
226
- * @returns {Matrix} 4x4 reflection matrix
507
+ * Create a reflection matrix across the XZ plane.
508
+ *
509
+ * Reflects points across the XZ plane (the plane where y=0), effectively flipping
510
+ * the Y coordinate while leaving X and Z unchanged. This is equivalent to scaling
511
+ * by (1, -1, 1).
512
+ *
513
+ * Matrix form:
514
+ * ```
515
+ * | 1 0 0 0 |
516
+ * | 0 -1 0 0 |
517
+ * | 0 0 1 0 |
518
+ * | 0 0 0 1 |
519
+ * ```
520
+ *
521
+ * Transformation: (x, y, z) → (x, -y, z)
522
+ *
523
+ * @returns {Matrix} 4x4 reflection matrix with determinant -1
524
+ *
525
+ * @example
526
+ * // Mirror a point across XZ plane
527
+ * const M = reflectXZ();
528
+ * const [x, y, z] = applyTransform(M, 1, 2, 3);
529
+ * // Result: x=1, y=-2, z=3
530
+ *
531
+ * @example
532
+ * // Create left-right symmetry in horizontal plane
533
+ * const M = reflectXZ();
534
+ * // Useful for mirroring floor plans or terrain
227
535
  */
228
536
  export function reflectXZ() {
229
537
  return Matrix.from([
@@ -235,8 +543,34 @@ export function reflectXZ() {
235
543
  }
236
544
 
237
545
  /**
238
- * Create a reflection matrix across the YZ plane (flips X).
239
- * @returns {Matrix} 4x4 reflection matrix
546
+ * Create a reflection matrix across the YZ plane.
547
+ *
548
+ * Reflects points across the YZ plane (the plane where x=0), effectively flipping
549
+ * the X coordinate while leaving Y and Z unchanged. This is equivalent to scaling
550
+ * by (-1, 1, 1).
551
+ *
552
+ * Matrix form:
553
+ * ```
554
+ * | -1 0 0 0 |
555
+ * | 0 1 0 0 |
556
+ * | 0 0 1 0 |
557
+ * | 0 0 0 1 |
558
+ * ```
559
+ *
560
+ * Transformation: (x, y, z) → (-x, y, z)
561
+ *
562
+ * @returns {Matrix} 4x4 reflection matrix with determinant -1
563
+ *
564
+ * @example
565
+ * // Mirror a point across YZ plane
566
+ * const M = reflectYZ();
567
+ * const [x, y, z] = applyTransform(M, 1, 2, 3);
568
+ * // Result: x=-1, y=2, z=3
569
+ *
570
+ * @example
571
+ * // Create front-back symmetry
572
+ * const M = reflectYZ();
573
+ * // Useful for mirroring left/right halves of models
240
574
  */
241
575
  export function reflectYZ() {
242
576
  return Matrix.from([
@@ -248,8 +582,45 @@ export function reflectYZ() {
248
582
  }
249
583
 
250
584
  /**
251
- * Create a reflection matrix across the origin (flips X, Y, and Z).
252
- * @returns {Matrix} 4x4 reflection matrix
585
+ * Create a reflection matrix through the origin (point inversion).
586
+ *
587
+ * Reflects all points through the origin, effectively flipping all three coordinates.
588
+ * This is equivalent to scaling by (-1, -1, -1), or a 180° rotation around any axis
589
+ * through the origin. Also known as central inversion or point reflection.
590
+ *
591
+ * Matrix form:
592
+ * ```
593
+ * | -1 0 0 0 |
594
+ * | 0 -1 0 0 |
595
+ * | 0 0 -1 0 |
596
+ * | 0 0 0 1 |
597
+ * ```
598
+ *
599
+ * Transformation: (x, y, z) → (-x, -y, -z)
600
+ *
601
+ * This transformation has determinant -1 and is its own inverse (applying it twice
602
+ * returns to the original position).
603
+ *
604
+ * @returns {Matrix} 4x4 reflection matrix with determinant -1
605
+ *
606
+ * @example
607
+ * // Invert a point through origin
608
+ * const M = reflectOrigin();
609
+ * const [x, y, z] = applyTransform(M, 1, 2, 3);
610
+ * // Result: x=-1, y=-2, z=-3
611
+ *
612
+ * @example
613
+ * // Create antipodal symmetry (opposite sides)
614
+ * const M = reflectOrigin();
615
+ * const point = [5, 3, -2];
616
+ * const opposite = applyTransform(M, ...point);
617
+ * // opposite = [-5, -3, 2]
618
+ *
619
+ * @example
620
+ * // Inversion is self-inverse: applying twice returns original
621
+ * const M = reflectOrigin();
622
+ * const M2 = M.mul(M);
623
+ * // M2 equals identity matrix
253
624
  */
254
625
  export function reflectOrigin() {
255
626
  return Matrix.from([