@itwin/core-geometry 4.0.0-dev.51 → 4.0.0-dev.52

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.
@@ -17,11 +17,15 @@ import { Range3d } from "./Range";
17
17
  * * The math for a Transform `T` consisting of a Matrix3d `M` and a Point3d `o` on a Vector3d `p` is: `Tp = M*p + o`.
18
18
  * In other words, `T` is a combination of two operations on `p`: the action of matrix multiplication, followed by a
19
19
  * translation. `Origin` is a traditional term for `o`, because `T` can be interpreted as a change of basis from the
20
- * global axes centered at the global origin, to a new set of axes centered at `o`.
21
- * * Beware that for common transformations (e.g. scale about point, rotate around an axis, mirror across a
22
- * plane) the "fixed point" that is used when describing the transform is NOT the "origin" stored in the
23
- * transform. Setup methods (e.g createFixedPointAndMatrix, createScaleAboutPoint) take care of determining
24
- * the appropriate origin coordinates.
20
+ * global axes centered at the global origin, to a new set of axes specified by matrix M columns centered at `o`.
21
+ * * Beware that for common transformations (e.g. scale about point, rotate around an axis) the `fixed point` that
22
+ * is used when describing the transform is NOT the `origin` stored in the transform. Setup methods (e.g
23
+ * createFixedPointAndMatrix, createScaleAboutPoint) take care of determining the appropriate origin coordinates.
24
+ * * If `T` is a translation, no point is fixed by `T`.
25
+ * * If `T` is the identity, all points are fixed by `T`.
26
+ * * If `T` is a scale about a point, one point is fixed by `T`.
27
+ * * If `T` is a rotation about an axis, a line is fixed by `T`.
28
+ * * If `T` is a projection to the plane, a plane is fixed by `T`.
25
29
  * @public
26
30
  */
27
31
  export class Transform {
@@ -314,22 +318,32 @@ export class Transform {
314
318
  const origin = Matrix3d.xyzMinusMatrixTimesXYZ(fixedPoint, matrix, fixedPoint);
315
319
  return Transform.createRefs(origin, matrix, result);
316
320
  }
317
- /** Transform the input 2d point. Return as a new point or in the pre-allocated result (if result is given). */
321
+ /**
322
+ * Transform the input 2d point (using `Tp = M*p + o`).
323
+ * Return as a new point or in the pre-allocated result (if result is given).
324
+ */
318
325
  multiplyPoint2d(point, result) {
319
- // Tx = Mx + o so we return Mx + o
320
326
  return Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, point, result);
321
327
  }
322
- /** Transform the input 3d point. Return as a new point or in the pre-allocated result (if result is given). */
328
+ /**
329
+ * Transform the input 3d point (using `Tp = M*p + o`).
330
+ * Return as a new point or in the pre-allocated result (if result is given).
331
+ */
323
332
  multiplyPoint3d(point, result) {
324
333
  // Tx = Mx + o so we return Mx + o
325
334
  return Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, point, result);
326
335
  }
327
- /** Transform the input 3d point in place (override the input point by the transformed point). */
336
+ /**
337
+ * Transform the input 3d point in place (using `Tp = M*p + o`).
338
+ * Return as a new point or in the pre-allocated result (if result is given).
339
+ */
328
340
  multiplyXYAndZInPlace(point) {
329
- // Tx = Mx + o so we override x by Mx + o
330
341
  return Matrix3d.xyzPlusMatrixTimesXYZInPlace(this._origin, this._matrix, point);
331
342
  }
332
- /** Transform the input point. Return as a new point or in the pre-allocated result (if result is given). */
343
+ /**
344
+ * Transform the input 3d point (using `Tp = M*p + o`).
345
+ * Return as a new point or in the pre-allocated result (if result is given).
346
+ */
333
347
  multiplyXYZ(x, y, z = 0, result) {
334
348
  // Tx = Mx + o so we return Mx + o
335
349
  return Matrix3d.xyzPlusMatrixTimesCoordinates(this._origin, this._matrix, x, y, z, result);
@@ -375,7 +389,7 @@ export class Transform {
375
389
  return Matrix3d.xyzPlusMatrixTimesCoordinatesToFloat64Array(this._origin, this._matrix, x, y, z, result);
376
390
  }
377
391
  /**
378
- * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row. Now multiply
392
+ * Treat the 3x3 `matrix` and `origin` as upper 3x4 part of a 4x4 matrix, with 0001 as the final row. Now multiply
379
393
  * the transposed of this 4x4 matrix by Point4d given as xyzw. Return as a new point4d (`M*p` as first 3 elements
380
394
  * and `o*p + w` as last element where `p = (x,y,z)`) or in the pre-allocated result (if result is given).
381
395
  */
@@ -384,13 +398,13 @@ export class Transform {
384
398
  const origin = this._origin;
385
399
  return Point4d.create((x * coffs[0]) + (y * coffs[3]) + (z * coffs[6]), (x * coffs[1]) + (y * coffs[4]) + (z * coffs[7]), (x * coffs[2]) + (y * coffs[5]) + (z * coffs[8]), (x * origin.x) + (y * origin.y) + (z * origin.z) + w, result);
386
400
  }
387
- /** For each point in the array, replace point by the transformed point (by `Tp = M*p + o`) */
401
+ /** For each point in the array, replace point by the transformed point (using `Tp = M*p + o`) */
388
402
  multiplyPoint3dArrayInPlace(points) {
389
403
  let point;
390
404
  for (point of points)
391
405
  Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, point, point);
392
406
  }
393
- /** For each point in the 2d array, replace point by the transformed point (by `Tp = M*p + o`) */
407
+ /** For each point in the 2d array, replace point by the transformed point (using `Tp = M*p + o`) */
394
408
  multiplyPoint3dArrayArrayInPlace(chains) {
395
409
  for (const chain of chains)
396
410
  this.multiplyPoint3dArrayInPlace(chain);
@@ -427,58 +441,23 @@ export class Transform {
427
441
  return this._matrix.multiplyInverseXYZAsPoint3d(x - this._origin.x, y - this._origin.y, z - this._origin.z, result);
428
442
  }
429
443
  /**
430
- * * for each point: multiply transform * point
431
- * * if result is given, resize to match source and replace each corresponding pi
432
- * * if result is not given, return a new array.
433
- */
434
- multiplyInversePoint3dArray(source, result) {
435
- if (!this._matrix.computeCachedInverse(true))
436
- return undefined;
437
- const originX = this.origin.x;
438
- const originY = this.origin.y;
439
- const originZ = this.origin.z;
440
- if (result) {
441
- const n = Transform.matchArrayLengths(source, result, Point3d.createZero);
442
- for (let i = 0; i < n; i++)
443
- this._matrix.multiplyInverseXYZAsPoint3d(source[i].x - originX, source[i].y - originY, source[i].z - originZ, result[i]);
444
- }
445
- result = [];
446
- for (const p of source)
447
- result.push(this._matrix.multiplyInverseXYZAsPoint3d(p.x - originX, p.y - originY, p.z - originZ));
448
- return result;
449
- }
450
- /**
451
- * * For each point in source: multiply transformInverse * point in place in the point.
452
- * * Return false if not invertible.
453
- */
454
- multiplyInversePoint3dArrayInPlace(source) {
455
- if (!this._matrix.computeCachedInverse(true))
456
- return false;
457
- const originX = this.origin.x;
458
- const originY = this.origin.y;
459
- const originZ = this.origin.z;
460
- const n = source.length;
461
- for (let i = 0; i < n; i++)
462
- this._matrix.multiplyInverseXYZAsPoint3d(source[i].x - originX, source[i].y - originY, source[i].z - originZ, source[i]);
463
- return true;
464
- }
465
- /**
466
- * * Compute (if needed) the inverse of the matrix part, thereby ensuring inverse operations can complete.
467
- * * Return true if matrix inverse completes.
444
+ * * Compute (if needed) the inverse of the `matrix` part of the Transform, thereby ensuring inverse
445
+ * operations can complete.
468
446
  * @param useCached If true, accept prior cached inverse if available.
447
+ * @returns `true` if matrix inverse completes, `false` otherwise.
469
448
  */
470
449
  computeCachedInverse(useCached = true) {
471
450
  return this._matrix.computeCachedInverse(useCached);
472
451
  }
473
452
  /**
474
- * * If destination has more values than source, remove the extras.
475
- * * If destination has fewer values, use the constructionFunction to create new ones.
476
- * @param source array
477
- * @param dest destination array, to be modified to match source length
478
- * @param constructionFunction function to call to create new entries.
453
+ * Match the length of destination array with the length of source array
454
+ * * If destination has more elements than source, remove the extra elements.
455
+ * * If destination has fewer elements than source, use `constructionFunction` to create new elements.
456
+ * *
457
+ * @param source the source array
458
+ * @param dest the destination array
459
+ * @param constructionFunction function to call to create new elements.
479
460
  */
480
- // modify destination so it has non-null points for the same length as the source.
481
- // (ASSUME existing elements of dest are non-null, and that parameters are given as either Point2d or Point3d arrays)
482
461
  static matchArrayLengths(source, dest, constructionFunction) {
483
462
  const numSource = source.length;
484
463
  const numDest = dest.length;
@@ -493,72 +472,125 @@ export class Transform {
493
472
  return numSource;
494
473
  }
495
474
  /**
496
- * * For each point: multiply transform * point
497
- * * If result is given, resize to match source and replace each corresponding pi
498
- * * If result is not given, return a new array.
475
+ * If for each point `p` we have `Tp = M*p + o = point` (where `point` is the transformed point), then
476
+ * `p = MInverse * (point - o)`. This function returns the array of original points `p[]` if `points`
477
+ * is the array of transformed point (`Tp = point` for each `p` and `point`).
478
+ * * If `results` is given, resize it to match the input `points` array and update it with original points `p[]`.
479
+ * * If `results` is not given, return a new array.
480
+ * * Returns `undefined` if the `matrix` part if this Transform is singular.
499
481
  */
500
- multiplyPoint2dArray(source, result) {
482
+ multiplyInversePoint3dArray(points, results) {
483
+ if (!this._matrix.computeCachedInverse(true))
484
+ return undefined;
485
+ const originX = this.origin.x;
486
+ const originY = this.origin.y;
487
+ const originZ = this.origin.z;
488
+ if (results) {
489
+ const n = Transform.matchArrayLengths(points, results, Point3d.createZero);
490
+ for (let i = 0; i < n; i++)
491
+ this._matrix.multiplyInverseXYZAsPoint3d(points[i].x - originX, points[i].y - originY, points[i].z - originZ, results[i]);
492
+ }
493
+ results = [];
494
+ for (const point of points)
495
+ results.push(this._matrix.multiplyInverseXYZAsPoint3d(point.x - originX, point.y - originY, point.z - originZ));
496
+ return results;
497
+ }
498
+ /**
499
+ * If for each point `p` we have `Tp = M*p + o = point` (where `point` is the transformed point), then
500
+ * `p = MInverse * (point - o)`. This function calculates the array of original points `p[]` if `points`
501
+ * is the array of transformed point (`Tp = point` for each `p` and `point`) and replaces `points`
502
+ * with the array of original points.
503
+ * * Returns `true` if the `matrix` part if this Transform is invertible and `false if singular.
504
+ */
505
+ multiplyInversePoint3dArrayInPlace(points) {
506
+ if (!this._matrix.computeCachedInverse(true))
507
+ return false;
508
+ for (const point of points)
509
+ this._matrix.multiplyInverseXYZAsPoint3d(point.x - this.origin.x, point.y - this.origin.y, point.z - this.origin.z, point);
510
+ return true;
511
+ }
512
+ /**
513
+ * Transform the input 2d point array (using `Tp = M*p + o`).
514
+ * * If `result` is given, resize it to match the input `points` array and update it with transformed points.
515
+ * * If `result` is not given, return a new array.
516
+ */
517
+ multiplyPoint2dArray(points, result) {
501
518
  if (result) {
502
- const n = Transform.matchArrayLengths(source, result, Point2d.createZero);
519
+ const n = Transform.matchArrayLengths(points, result, Point2d.createZero);
503
520
  for (let i = 0; i < n; i++)
504
- Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, source[i], result[i]);
521
+ Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, points[i], result[i]);
505
522
  return result;
506
523
  }
507
524
  result = [];
508
- for (const p of source)
525
+ for (const p of points)
509
526
  result.push(Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, p));
510
527
  return result;
511
528
  }
512
529
  /**
513
- * * For each point: multiply transform * point
514
- * * If result is given, resize to match source and replace each corresponding pi
515
- * * If result is not given, return a new array.
530
+ * Transform the input 3d point array (using `Tp = M*p + o`).
531
+ * * If `result` is given, resize it to match the input `points` array and update it with transformed points.
532
+ * * If `result` is not given, return a new array.
516
533
  */
517
- multiplyPoint3dArray(source, result) {
534
+ multiplyPoint3dArray(points, result) {
518
535
  if (result) {
519
- const n = Transform.matchArrayLengths(source, result, Point3d.createZero);
536
+ const n = Transform.matchArrayLengths(points, result, Point3d.createZero);
520
537
  for (let i = 0; i < n; i++)
521
- Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, source[i], result[i]);
538
+ Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, points[i], result[i]);
522
539
  return result;
523
540
  }
524
541
  result = [];
525
- for (const p of source)
542
+ for (const p of points)
526
543
  result.push(Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, p));
527
544
  return result;
528
545
  }
529
546
  /**
530
- * Multiply the vector by the Matrix3d part of the transform.
531
- * * The transform's origin is not used.
532
- * * Return as new or result by usual optional result convention
547
+ * Multiply the vector by the `matrix` part of the Transform.
548
+ * * The `origin` part of Transform is not used.
549
+ * * If `result` is given, update it with the multiplication. Otherwise, create a new Vector3d.
533
550
  */
534
551
  multiplyVector(vector, result) {
535
552
  return this._matrix.multiplyVector(vector, result);
536
553
  }
537
554
  /**
538
- * Multiply the vector in place by the Matrix3d part of the transform.
539
- * * The transform's origin is not used.
555
+ * Multiply the vector by the `matrix` part of the Transform in place.
556
+ * * The `origin` part of Transform is not used.
540
557
  */
541
558
  multiplyVectorInPlace(vector) {
542
559
  this._matrix.multiplyVectorInPlace(vector);
543
560
  }
544
561
  /**
545
- * Multiply the vector (x,y,z) by the Matrix3d part of the transform.
546
- * * The transform's origin is not used.
547
- * * Return as new or result by usual optional result convention
562
+ * Multiply the vector (x,y,z) by the `matrix` part of the Transform.
563
+ * * The `origin` part of Transform is not used.
564
+ * * If `result` is given, update it with the multiplication. Otherwise, create a new Vector3d.
548
565
  */
549
566
  multiplyVectorXYZ(x, y, z, result) {
550
567
  return this._matrix.multiplyXYZ(x, y, z, result);
551
568
  }
552
- /** Multiply this Transform times other Transform.
569
+ /**
570
+ * Calculate `transformA * transformB` and store it into the calling instance (`this`).
571
+ * * **Note:** If `transformA = [A a]` and `transformB = [B b]` then `transformA * transformB` is defined as
572
+ * `[A*B Ab+a]`. See `multiplyTransformTransform` doc for math details.
573
+ * @param transformA first operand
574
+ * @param transformB second operand
575
+ */
576
+ setMultiplyTransformTransform(transformA, transformB) {
577
+ Matrix3d.xyzPlusMatrixTimesXYZ(transformA._origin, transformA._matrix, transformB._origin, this._origin);
578
+ transformA._matrix.multiplyMatrixMatrix(transformB._matrix, this._matrix);
579
+ }
580
+ /**
581
+ * Multiply `this` Transform times `other` Transform.
582
+ * **Note:** If `this = [A a]` and `other = [B b]` then `this * other` is defined as [A*B Ab+a].
583
+ * That's because we create a 4x4 matrix for each Transform with the 3x3 `matrix` and `origin`
584
+ * as upper 3x4 part of a 4x4 matrix and 0001 as the final row. Then we multiply those two 4x4 matrixes:
553
585
  * ```
554
586
  * equation
555
587
  * \begin{matrix}
556
- * \text{`this` transform with matrix part }\bold{A}\text{ and translation }\bold{a} & \blockTransform{A}{a}\\
557
- * \text{`other` transform with matrix part }\bold{B}\text{ and translation part }\bold{b}\text{ promoted to block transform} & \blockTransform{B}{b} \\
588
+ * \text{`this` Transform with `matrix` part }\bold{A}\text{ and `origin` part }\bold{a} & \blockTransform{A}{a}\\
589
+ * \text{`other` Transform with `matrix` part }\bold{B}\text{ and `origin` part }\bold{b} & \blockTransform{B}{b} \\
558
590
  * \text{product}& \blockTransform{A}{a}\blockTransform{B}{b}=\blockTransform{AB}{Ab + a}
559
591
  * \end{matrix}
560
592
  * ```
561
- * @param other right hand transform for multiplication.
593
+ * @param other the 'other` Transform to be multiplied to `this` Transform.
562
594
  * @param result optional preallocated result to reuse.
563
595
  */
564
596
  multiplyTransformTransform(other, result) {
@@ -568,30 +600,20 @@ export class Transform {
568
600
  return result;
569
601
  }
570
602
  /**
571
- * Multiply transformA * transformB, store to calling instance.
572
- * @param transformA left operand
573
- * @param transformB right operand
574
- */
575
- setMultiplyTransformTransform(transformA, transformB) {
576
- if (Transform._scratchPoint === undefined)
577
- Transform._scratchPoint = Point3d.create();
578
- Matrix3d.xyzPlusMatrixTimesXYZ(transformA._origin, transformA._matrix, transformB._origin, Transform._scratchPoint);
579
- this._origin.setFrom(Transform._scratchPoint);
580
- transformA._matrix.multiplyMatrixMatrix(transformB._matrix, this._matrix);
581
- }
582
- // [Q A][R 0] = [QR A]
583
- // [0 1][0 1] [0 1]
584
- /**
585
- * Multiply this Transform times other Matrix3d, with other considered to be a Transform with 0 translation.
603
+ * Multiply `this` Transform times `other` Matrix3d (considered to be a Transform with 0 `origin`).
604
+ * **Note:** If `this = [A a]`, then we promote `other` matrix to be a Transform [B 0].
605
+ * Then `this * other` is defined as [A*B a]. That's because we create a 4x4 matrix for each Transform
606
+ * with the 3x3 `matrix` and `origin` as upper 3x4 part of a 4x4 matrix and 0001 as the final row. Then we
607
+ * multiply those two 4x4 matrixes:
586
608
  * ```
587
609
  * equation
588
610
  * \begin{matrix}
589
- * \text{`this` transform with matrix part }\bold{A}\text{ and translation }\bold{b} & \blockTransform{B}{b}\\
590
- * \text{`other` matrix }\bold{B}\text{ promoted to block transform} & \blockTransform{B}{0} \\
611
+ * \text{`this` Transform with `matrix` part }\bold{A}\text{ and `origin` part }\bold{a} & \blockTransform{A}{a}\\
612
+ * \text{`other` matrix }\bold{B}\text{ promoted to block Transform} & \blockTransform{B}{0} \\
591
613
  * \text{product}& \blockTransform{A}{a}\blockTransform{B}{0}=\blockTransform{AB}{a}
592
614
  * \end{matrix}
593
615
  * ```
594
- * @param other right hand Matrix3d for multiplication.
616
+ * @param other the `other` Matrix3d to be multiplied to `this` Transform.
595
617
  * @param result optional preallocated result to reuse.
596
618
  */
597
619
  multiplyTransformMatrix3d(other, result) {
@@ -602,15 +624,17 @@ export class Transform {
602
624
  return result;
603
625
  }
604
626
  /**
605
- * Return the range of the transformed corners.
627
+ * Return the Range of the transformed corners.
606
628
  * * The 8 corners are transformed individually.
607
- * * Note that if there is anything other than translation and principal axis scaling in the transform, the volume of the range rotation will increase.
608
- * * Hence to get a "tight" range on rotated geometry, a range computation must be made on the rotated geometry itself.
629
+ * * **Note:** Suppose you have a geometry, a range box around that geometry, and your Transform is a rotation.
630
+ * If you rotate the range box and recompute a new range box around the rotated range box, then the new range
631
+ * box will have a larger volume than the original range box. However, if you rotate the geometry itself and
632
+ * then recompute the range box, it will be a tighter range box around the rotated geometry. `multiplyRange`
633
+ * function creates the larger range box because it only has access to the range box and the geometry itself.
609
634
  */
610
635
  multiplyRange(range, result) {
611
636
  if (range.isNull)
612
637
  return range.clone(result);
613
- // snag current values to allow aliasing.
614
638
  const lowX = range.low.x;
615
639
  const lowY = range.low.y;
616
640
  const lowZ = range.low.z;
@@ -629,9 +653,9 @@ export class Transform {
629
653
  return result;
630
654
  }
631
655
  /**
632
- * * Return a Transform which is the inverse of this transform.
633
- * @param result optional pre-allocated result
634
- * @return the inverse Transform, or undefined if the matrix is singular
656
+ * Return a Transform which is the inverse of `this` Transform.
657
+ * * If `transform = [M o]` then `transformInverse = [MInverse MInverse*-o]`
658
+ * * Return `undefined` if this Transform's matrix is singular.
635
659
  */
636
660
  inverse(result) {
637
661
  const matrixInverse = this._matrix.inverse(result ? result._matrix : undefined);
@@ -645,15 +669,15 @@ export class Transform {
645
669
  return Transform.createRefs(matrixInverse.multiplyXYZ(-this._origin.x, -this._origin.y, -this._origin.z), matrixInverse);
646
670
  }
647
671
  /**
648
- * Initialize transforms that map each direction of a box (axis aligned) to `[0,1]`.
649
- * * The corner coordinates do _not_ need to be in order in any of the x,y,z directions.
650
- * * The npcToGlobalTransform (if supplied) maps 000 to the point named point000.
651
- * * The npcToGlobalTransform (if supplied) maps 11 to the point named point000.
652
- * * The globalToNpc transform is the inverse.
653
- * @param min the "000" corner of the box
654
- * @param max the "111" corner of the box
655
- * @param npcToGlobal (object created by caller, re-initialized here) transform that carries 01 coordinates into the min,max box.
656
- * @param globalToNpc (object created by caller, re-initialized here) transform that carries world coordinates into 01
672
+ * Initialize 2 Transforms: First Transform maps a box (axis aligned) specified by `min` and `max` to
673
+ * the unit box specified by 000 and 111 and inverse of it. Second Transform is the reverse of first.
674
+ * @param min the min corner of the box
675
+ * @param max the max corner of the box
676
+ * @param npcToGlobal maps global (the unit box specified by 000 and 111) to NPC (a box specified by `min`
677
+ * and `max`). Object created by caller, re-initialized here.
678
+ * @param globalToNpc maps NPC (a box specified by `min` and `max`) to global (the unit box specified by
679
+ * 000 and 111). Object created by caller, re-initialized here.
680
+ * * NPC stands for `Normalized Projection Coordinate`
657
681
  */
658
682
  static initFromRange(min, max, npcToGlobal, globalToNpc) {
659
683
  const diag = max.minus(min);
@@ -664,10 +688,26 @@ export class Transform {
664
688
  if (diag.z === 0.0)
665
689
  diag.z = 1.0;
666
690
  const rMatrix = new Matrix3d();
691
+ /**
692
+ * [diag.x 0 0 min.x]
693
+ * npcToGlobal = [ 0 diag.y 0 min.y]
694
+ * [ 0 0 diag.y min.z]
695
+ *
696
+ * npcToGlobal * 0 = min
697
+ * npcToGlobal * 1 = diag + min = max
698
+ */
667
699
  if (npcToGlobal) {
668
700
  Matrix3d.createScale(diag.x, diag.y, diag.z, rMatrix);
669
701
  Transform.createOriginAndMatrix(min, rMatrix, npcToGlobal);
670
702
  }
703
+ /**
704
+ * [1/diag.x 0 0 -min.x/diag.x]
705
+ * globalToNpc = [ 0 1/diag.y 0 -min.y/diag.y]
706
+ * [ 0 0 1/diag.y -min.z/diag.z]
707
+ *
708
+ * globalToNpc * min = min/diag - min/diag = 0
709
+ * globalToNpc * max = max/diag - min/diag = diag/diag = 1
710
+ */
671
711
  if (globalToNpc) {
672
712
  const origin = new Point3d(-min.x / diag.x, -min.y / diag.y, -min.z / diag.z);
673
713
  Matrix3d.createScale(1.0 / diag.x, 1.0 / diag.y, 1.0 / diag.z, rMatrix);