@ifc-lite/renderer 1.43.0 → 1.44.0

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.
Files changed (85) hide show
  1. package/dist/camera-animation.d.ts +20 -32
  2. package/dist/camera-animation.d.ts.map +1 -1
  3. package/dist/camera-animation.js +71 -358
  4. package/dist/camera-animation.js.map +1 -1
  5. package/dist/camera-controls.d.ts +11 -51
  6. package/dist/camera-controls.d.ts.map +1 -1
  7. package/dist/camera-controls.js +140 -12
  8. package/dist/camera-controls.js.map +1 -1
  9. package/dist/camera-first-person.d.ts +52 -0
  10. package/dist/camera-first-person.d.ts.map +1 -0
  11. package/dist/camera-first-person.js +119 -0
  12. package/dist/camera-first-person.js.map +1 -0
  13. package/dist/camera-framing.d.ts +86 -0
  14. package/dist/camera-framing.d.ts.map +1 -0
  15. package/dist/camera-framing.js +230 -0
  16. package/dist/camera-framing.js.map +1 -0
  17. package/dist/camera-guards.d.ts +148 -0
  18. package/dist/camera-guards.d.ts.map +1 -0
  19. package/dist/camera-guards.js +150 -0
  20. package/dist/camera-guards.js.map +1 -0
  21. package/dist/camera-matrices.d.ts +14 -0
  22. package/dist/camera-matrices.d.ts.map +1 -0
  23. package/dist/camera-matrices.js +146 -0
  24. package/dist/camera-matrices.js.map +1 -0
  25. package/dist/camera-preset-view.d.ts +56 -0
  26. package/dist/camera-preset-view.d.ts.map +1 -0
  27. package/dist/camera-preset-view.js +180 -0
  28. package/dist/camera-preset-view.js.map +1 -0
  29. package/dist/camera-projection.d.ts +1 -1
  30. package/dist/camera-projection.d.ts.map +1 -1
  31. package/dist/camera-projection.js +83 -30
  32. package/dist/camera-projection.js.map +1 -1
  33. package/dist/camera-state.d.ts +62 -0
  34. package/dist/camera-state.d.ts.map +1 -0
  35. package/dist/camera-state.js +5 -0
  36. package/dist/camera-state.js.map +1 -0
  37. package/dist/camera.d.ts +22 -11
  38. package/dist/camera.d.ts.map +1 -1
  39. package/dist/camera.js +75 -120
  40. package/dist/camera.js.map +1 -1
  41. package/dist/environment.d.ts.map +1 -1
  42. package/dist/environment.js +23 -1
  43. package/dist/environment.js.map +1 -1
  44. package/dist/index.d.ts +154 -8
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +294 -45
  47. package/dist/index.js.map +1 -1
  48. package/dist/math.d.ts +49 -1
  49. package/dist/math.d.ts.map +1 -1
  50. package/dist/math.js +122 -67
  51. package/dist/math.js.map +1 -1
  52. package/dist/renderer-symbolic-overlays.d.ts.map +1 -1
  53. package/dist/renderer-symbolic-overlays.js +20 -25
  54. package/dist/renderer-symbolic-overlays.js.map +1 -1
  55. package/dist/scene-geometry.d.ts +32 -0
  56. package/dist/scene-geometry.d.ts.map +1 -1
  57. package/dist/scene-geometry.js +66 -0
  58. package/dist/scene-geometry.js.map +1 -1
  59. package/dist/scene.d.ts.map +1 -1
  60. package/dist/scene.js +22 -97
  61. package/dist/scene.js.map +1 -1
  62. package/dist/section-2d-lift.d.ts +105 -0
  63. package/dist/section-2d-lift.d.ts.map +1 -0
  64. package/dist/section-2d-lift.js +162 -0
  65. package/dist/section-2d-lift.js.map +1 -0
  66. package/dist/section-2d-line-buffer.d.ts +58 -0
  67. package/dist/section-2d-line-buffer.d.ts.map +1 -0
  68. package/dist/section-2d-line-buffer.js +109 -0
  69. package/dist/section-2d-line-buffer.js.map +1 -0
  70. package/dist/section-2d-overlay.d.ts +28 -77
  71. package/dist/section-2d-overlay.d.ts.map +1 -1
  72. package/dist/section-2d-overlay.js +161 -593
  73. package/dist/section-2d-overlay.js.map +1 -1
  74. package/dist/section-plane-basis.d.ts +22 -3
  75. package/dist/section-plane-basis.d.ts.map +1 -1
  76. package/dist/section-plane-basis.js +51 -10
  77. package/dist/section-plane-basis.js.map +1 -1
  78. package/dist/section-plane.d.ts.map +1 -1
  79. package/dist/section-plane.js +10 -1
  80. package/dist/section-plane.js.map +1 -1
  81. package/dist/shaders/section-2d-overlay.wgsl.d.ts +102 -0
  82. package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -0
  83. package/dist/shaders/section-2d-overlay.wgsl.js +281 -0
  84. package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -0
  85. package/package.json +3 -2
@@ -1,7 +1,22 @@
1
1
  /* This Source Code Form is subject to the terms of the Mozilla Public
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
- import { MathUtils } from './math.js';
4
+ import { MathUtils, viewBasis } from './math.js';
5
+ import { DEFAULT_ORTHO_SIZE, isUsableBounds } from './camera-guards.js';
6
+ /**
7
+ * One NDC axis: `screen / extent * scale + offset`, degrading to the centre
8
+ * of the axis when the extent is unusable.
9
+ *
10
+ * `extent` is a drawing-buffer dimension. It is zero before the canvas is
11
+ * first sized and while its container is collapsed, and the division would
12
+ * then hand back ±Infinity — or NaN, when the cursor coordinate is zero too,
13
+ * since `0 / 0` is NaN. Neither is a position on screen.
14
+ */
15
+ function usableNdc(screen, extent, scale, offset) {
16
+ if (!(Number.isFinite(extent) && extent > 0) || !Number.isFinite(screen))
17
+ return offset + scale / 2;
18
+ return (screen / extent) * scale + offset;
19
+ }
5
20
  /**
6
21
  * Handles projection math: screen-to-world and world-to-screen conversions,
7
22
  * bounding box fitting, and near/far plane management.
@@ -56,30 +71,43 @@ export class CameraProjection {
56
71
  * @returns Ray origin and direction in world space
57
72
  */
58
73
  unprojectToRay(screenX, screenY, canvasWidth, canvasHeight) {
59
- // Convert screen coords to NDC (-1 to 1)
60
- const ndcX = (screenX / canvasWidth) * 2 - 1;
61
- const ndcY = 1 - (screenY / canvasHeight) * 2; // Flip Y
74
+ // Convert screen coords to NDC (-1 to 1).
75
+ //
76
+ // The divisors are the drawing-buffer dimensions, and a canvas can report
77
+ // zero before its first resize or while its container is collapsed — the
78
+ // division then yields ±Infinity and the ortho branch below writes that
79
+ // into the ray origin. A viewport with no extent has no meaningful cursor
80
+ // position, so the honest reading of "where is the cursor, in NDC" is
81
+ // dead centre; that is also the answer this returns for a cursor at the
82
+ // exact centre of a healthy canvas, so it introduces no new behaviour.
83
+ const ndcX = usableNdc(screenX, canvasWidth, 2, -1);
84
+ const ndcY = usableNdc(screenY, canvasHeight, -2, 1);
85
+ // One basis for both branches, and the same one the *view matrix* is built
86
+ // from (#2467). The orthographic branch used to recompute
87
+ // `normalize(cross(forward, up))` itself, which has no degeneracy handling
88
+ // at all: `MathUtils.normalize` floored on `len < 1e-10` back then, and
89
+ // that comparison is false for NaN, so it divided by NaN rather than
90
+ // falling back — a non-finite `camera.up` (a BCF `CameraUpVector` of `1e999`
91
+ // arrives as `Infinity` through a bare `parseFloat`) returned
92
+ // `origin = (NaN, NaN, NaN)` for BOTH NaN and Infinity, while the rendered
93
+ // frame stayed perfectly finite. Picking and measurement live outside this
94
+ // package and test hits with comparisons, every one of which is false
95
+ // against NaN, so the click read as "empty space".
96
+ const basis = viewBasis(this.state.camera.position, this.state.camera.target, this.state.camera.up);
62
97
  if (this.state.projectionMode === 'orthographic') {
63
98
  // Orthographic: rays are parallel. Origin varies with screen position.
64
- const halfH = this.state.orthoSize;
65
- const halfW = halfH * this.state.camera.aspect;
66
- // Forward direction (camera towards target)
67
- const forward = MathUtils.normalize({
68
- x: this.state.camera.target.x - this.state.camera.position.x,
69
- y: this.state.camera.target.y - this.state.camera.position.y,
70
- z: this.state.camera.target.z - this.state.camera.position.z,
71
- });
72
- // Right = forward x up
73
- const right = MathUtils.normalize(MathUtils.cross(forward, this.state.camera.up));
74
- // Actual up = right x forward
75
- const actualUp = MathUtils.cross(right, forward);
99
+ // `orthoSize` has a read-site backstop in `Camera.updateMatrices` for
100
+ // the projection matrix; this is the other reader, so it needs its own.
101
+ const halfH = Number.isFinite(this.state.orthoSize) ? this.state.orthoSize : DEFAULT_ORTHO_SIZE;
102
+ const aspect = Number.isFinite(this.state.camera.aspect) ? this.state.camera.aspect : 1;
103
+ const halfW = halfH * aspect;
76
104
  // Ray origin: camera position offset by NDC * view extents
77
105
  const origin = {
78
- x: this.state.camera.position.x + right.x * ndcX * halfW + actualUp.x * ndcY * halfH,
79
- y: this.state.camera.position.y + right.y * ndcX * halfW + actualUp.y * ndcY * halfH,
80
- z: this.state.camera.position.z + right.z * ndcX * halfW + actualUp.z * ndcY * halfH,
106
+ x: basis.eye.x + basis.right.x * ndcX * halfW + basis.up.x * ndcY * halfH,
107
+ y: basis.eye.y + basis.right.y * ndcX * halfW + basis.up.y * ndcY * halfH,
108
+ z: basis.eye.z + basis.right.z * ndcX * halfW + basis.up.z * ndcY * halfH,
81
109
  };
82
- return { origin, direction: forward };
110
+ return { origin, direction: basis.forward };
83
111
  }
84
112
  // Perspective: ray origin is always the camera position
85
113
  // Direction is computed through the screen point
@@ -87,23 +115,43 @@ export class CameraProjection {
87
115
  const invViewProj = MathUtils.invert(this.state.viewProjMatrix);
88
116
  if (!invViewProj) {
89
117
  // Fallback: return ray from camera position towards target
90
- const dir = MathUtils.normalize({
91
- x: this.state.camera.target.x - this.state.camera.position.x,
92
- y: this.state.camera.target.y - this.state.camera.position.y,
93
- z: this.state.camera.target.z - this.state.camera.position.z,
94
- });
95
- return { origin: { ...this.state.camera.position }, direction: dir };
118
+ return { origin: { ...basis.eye }, direction: basis.forward };
96
119
  }
97
120
  // Unproject a point at some depth to get a point on the ray
98
121
  // Using z=0.5 (midpoint in Reverse-Z: 1.0=near, 0.0=far) to get a finite point
99
122
  const worldPoint = MathUtils.transformPoint(invViewProj, { x: ndcX, y: ndcY, z: 0.5 });
100
- // Ray origin is camera position, direction is towards unprojected point
101
- const origin = { ...this.state.camera.position };
102
- const direction = MathUtils.normalize({
123
+ // Ray origin is the scrubbed camera position the same one the view
124
+ // matrix's translation row uses, so the ray belongs to the frame on
125
+ // screen rather than to a pose that was never rendered.
126
+ const origin = { ...basis.eye };
127
+ const raw = {
103
128
  x: worldPoint.x - origin.x,
104
129
  y: worldPoint.y - origin.y,
105
130
  z: worldPoint.z - origin.z,
106
- });
131
+ };
132
+ const direction = MathUtils.normalize(raw);
133
+ // `normalize` is total — it returns the zero vector for everything it
134
+ // cannot normalize — and a zero-length direction is a ray that hits
135
+ // nothing, the same silent miss the NaN origin produced. Two inputs land
136
+ // here, and the second is why the floor inside `normalize` had to be a
137
+ // finiteness test rather than the `len < 1e-10` lower bound it was:
138
+ //
139
+ // - the inverted matrix is ill-conditioned enough that the unprojected
140
+ // point lands on the eye, leaving nothing to normalize;
141
+ // - `raw` is non-finite. `MathUtils.invert` stores its result in a
142
+ // `Float32Array`, so an inverse component past 3.4e38 saturates to
143
+ // `Infinity` and `transformPoint` carries that into `worldPoint`. A
144
+ // lower bound admits `Infinity` (it is not *below* the floor), and
145
+ // `Infinity / Infinity` is NaN while the finite components divide to
146
+ // `0` — so the direction came back `{NaN, 0, -0}`, which is neither
147
+ // finite nor the zero vector this branch tests for. Measured on a
148
+ // camera at `(1e155, 0, 0)` with a wide viewport (#2479).
149
+ //
150
+ // Look along the view direction instead: that is the ray through the
151
+ // centre of the frame, which is where a cursor with no usable offset is.
152
+ if (direction.x === 0 && direction.y === 0 && direction.z === 0) {
153
+ return { origin, direction: basis.forward };
154
+ }
107
155
  return { origin, direction };
108
156
  }
109
157
  /**
@@ -112,6 +160,11 @@ export class CameraProjection {
112
160
  * Y-up coordinate system: Y is vertical
113
161
  */
114
162
  fitToBounds(min, max) {
163
+ // Same input class, same policy as the animator's `frameBounds`/
164
+ // `zoomExtent` (#2461): an infinite or inverted AABB has no centre and no
165
+ // extent to fit to, and both go straight into `position` and `target`.
166
+ if (!isUsableBounds(min, max))
167
+ return;
115
168
  const center = {
116
169
  x: (min.x + max.x) / 2,
117
170
  y: (min.y + max.y) / 2,
@@ -1 +1 @@
1
- {"version":3,"file":"camera-projection.js","sourceRoot":"","sources":["../src/camera-projection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"camera-projection.js","sourceRoot":"","sources":["../src/camera-projection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The state object every camera sub-system shares.
3
+ *
4
+ * This lives on its own rather than inside one of the consumers: `camera.ts`,
5
+ * `camera-controls.ts`, `camera-animation.ts`, `camera-projection.ts`,
6
+ * `camera-matrices.ts`, `camera-framing.ts`, `camera-preset-view.ts` and
7
+ * `camera-first-person.ts` all
8
+ * need the type, and declaring it inside the gesture module made four of them
9
+ * import their core type from a sibling that is otherwise unrelated to them.
10
+ * Owning it here turns that chain into a star.
11
+ */
12
+ import type { Camera as CameraType, Mat4 } from './types.js';
13
+ /** Projection mode for the camera */
14
+ export type ProjectionMode = 'perspective' | 'orthographic';
15
+ /**
16
+ * Shared mutable state for camera sub-systems.
17
+ * All sub-systems reference the same state object so changes are visible across them.
18
+ */
19
+ export interface CameraInternalState {
20
+ camera: CameraType;
21
+ viewMatrix: Mat4;
22
+ projMatrix: Mat4;
23
+ viewProjMatrix: Mat4;
24
+ /** Current projection mode */
25
+ projectionMode: ProjectionMode;
26
+ /** Orthographic half-height in world units (controls zoom level in ortho mode) */
27
+ orthoSize: number;
28
+ /** Scene bounding box for tight orthographic near/far computation */
29
+ sceneBounds: {
30
+ min: {
31
+ x: number;
32
+ y: number;
33
+ z: number;
34
+ };
35
+ max: {
36
+ x: number;
37
+ y: number;
38
+ z: number;
39
+ };
40
+ } | null;
41
+ /**
42
+ * Optional outlier-robust bounds for anchoring the orbit pivot (issue #1394).
43
+ * Distinct from `sceneBounds`: the renderer keeps `sceneBounds` synced to the
44
+ * FULL model AABB (needed for near/far clipping and section ranges), but a
45
+ * handful of far-flung meshes can push that AABB's centre into empty space,
46
+ * which the orbit-pivot fallback would then rotate around. When set, the pivot
47
+ * uses this tighter centre instead. `null` ⇒ fall back to `sceneBounds`.
48
+ */
49
+ orbitAnchorBounds: {
50
+ min: {
51
+ x: number;
52
+ y: number;
53
+ z: number;
54
+ };
55
+ max: {
56
+ x: number;
57
+ y: number;
58
+ z: number;
59
+ };
60
+ } | null;
61
+ }
62
+ //# sourceMappingURL=camera-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-state.d.ts","sourceRoot":"","sources":["../src/camera-state.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE7D,qCAAqC;AACrC,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,cAAc,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;IACjB,cAAc,EAAE,IAAI,CAAC;IACrB,8BAA8B;IAC9B,cAAc,EAAE,cAAc,CAAC;IAC/B,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,WAAW,EAAE;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3G;;;;;;;OAOG;IACH,iBAAiB,EAAE;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CAClH"}
@@ -0,0 +1,5 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ export {};
5
+ //# sourceMappingURL=camera-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-state.js","sourceRoot":"","sources":["../src/camera-state.ts"],"names":[],"mappings":"AAAA;;+DAE+D"}
package/dist/camera.d.ts CHANGED
@@ -6,14 +6,20 @@
6
6
  * (screen-world conversion/bounds fitting).
7
7
  */
8
8
  import type { Vec3, Mat4 } from './types.js';
9
- import { type ProjectionMode } from './camera-controls.js';
9
+ import type { ProjectionMode } from './camera-state.js';
10
10
  import { type Bounds3, type FitPolicy } from './camera-fit-policy.js';
11
11
  export declare class Camera {
12
12
  private state;
13
13
  private controls;
14
14
  private animator;
15
15
  private projection;
16
+ private firstPerson;
16
17
  constructor();
18
+ /**
19
+ * Cold-path convenience for this class's own setters. The hot path uses the
20
+ * closure built in the constructor; see the note there.
21
+ */
22
+ private updateMatrices;
17
23
  /**
18
24
  * Set camera aspect ratio
19
25
  */
@@ -149,7 +155,21 @@ export declare class Camera {
149
155
  */
150
156
  getFOV(): number;
151
157
  /**
152
- * Get distance from camera position to target
158
+ * Get distance from camera position to target.
159
+ *
160
+ * Deliberately **unsanitized**: it reports the pose as it actually is, so a
161
+ * malformed one (a BCF viewpoint restored from a file reaches the public
162
+ * setters unvalidated) yields NaN rather than a substituted number. This is
163
+ * a measurement, not a control input: it is what `useBCF` reads back when
164
+ * restoring a viewpoint and what the BCF overlay scales markers by. There is
165
+ * no substitute that is right for every reader — a plausible-looking `1`
166
+ * would place every restored target one unit from the eye — and returning a
167
+ * number here would contradict `getPosition()`/`getTarget()`, which are raw,
168
+ * leaving callers no way to tell that the pose is broken. Gesture code inside
169
+ * this package guards with {@link isUsableDistance} instead; each gesture
170
+ * needs a different fallback. Callers OUTSIDE the package cannot use that
171
+ * predicate (it is package-internal) and are not all guarded — see #2466 for
172
+ * the viewpoint-restore path (#2441).
153
173
  */
154
174
  getDistance(): number;
155
175
  /**
@@ -275,14 +295,5 @@ export declare class Camera {
275
295
  z: number;
276
296
  };
277
297
  } | null;
278
- private updateMatrices;
279
- /**
280
- * Compute tight near/far for orthographic mode by projecting the scene
281
- * bounding sphere onto the camera view direction.
282
- *
283
- * This gives optimal depth precision (minimizing z-fighting) while ensuring
284
- * no geometry is clipped regardless of camera position or view angle.
285
- */
286
- private computeOrthoNearFar;
287
298
  }
288
299
  //# sourceMappingURL=camera.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,KAAK,EAAuB,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAK7E,OAAO,EAAiB,KAAK,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,wBAAwB,CAAC;AAQhH,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,WAAW,CAAuB;;IAoC1C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAa/B;;OAEG;IACH,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAKlD;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAKhD;;OAEG;IACH,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAK5C;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAYzB;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IAIzC;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,IAAI;IAQhE;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,IAAI;IAe9D;;;;;;;;OAQG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,UAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI;IAQjJ;;;;OAIG;IACH,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAIvC;;;OAGG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlC;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAOxC;;;OAGG;IACG,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAStE;;;;OAIG;IACH,iBAAiB,CACf,MAAM,EAAE,OAAO,EACf,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3E,SAAS;IAgCZ;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhG;;OAEG;IACH,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI7C;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjE;;;;OAIG;IACH,aAAa,CACX,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC5D,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,EACjC,gBAAgB,CAAC,EAAE,MAAM,GACxB,IAAI;IAIP;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;;OAGG;IACH,KAAK,IAAI,IAAI;IAcb,iBAAiB,IAAI,IAAI;IAIzB,WAAW,IAAI,IAAI;IAInB,SAAS,IAAI,IAAI;IAIjB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,IAAI,MAAM;IASrB;;;;;OAKG;IACH,WAAW,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IA6CrD;;;;;;;OAOG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE;IAI9H;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAI3G;;;OAGG;IACH,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAkB7C;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAI5B;;OAEG;IACH,iBAAiB,IAAI,cAAc;IAInC;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAWhC;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,GAAG,IAAI;IAK3H;;;;;OAKG;IACH,cAAc,IAAI;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI;IAI/G;;;;;OAKG;IACH,oBAAoB,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,GAAG,IAAI;IAIjI;;;;OAIG;IACH,oBAAoB,IAAI;QAAE,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI;CAGtH"}
package/dist/camera.js CHANGED
@@ -5,34 +5,16 @@ import { MathUtils } from './math.js';
5
5
  import { CameraControls } from './camera-controls.js';
6
6
  import { CameraAnimator } from './camera-animation.js';
7
7
  import { CameraProjection } from './camera-projection.js';
8
+ import { FirstPersonNavigator } from './camera-first-person.js';
9
+ import { updateCameraMatrices } from './camera-matrices.js';
8
10
  import { pickFitPolicy } from './camera-fit-policy.js';
9
- /**
10
- * Smallest orthographic half-height the projection matrix is ever built from.
11
- * Matches the floor the orthographic zoom clamps to in `CameraControls`.
12
- */
13
- const MIN_ORTHO_SIZE = 0.01;
14
- /** Default orthographic half-height, in world units, and the neutral fallback. */
15
- const DEFAULT_ORTHO_SIZE = 50;
16
- /**
17
- * Clamp an orthographic half-height, rejecting a non-finite one.
18
- *
19
- * `Math.max`/`Math.min` are NaN-transparent — `Math.max(0.01, NaN)` is `NaN`,
20
- * not the floor — so a clamp alone does not stop a malformed value reaching
21
- * the projection matrix (#2441). `orthoSize` is the other value feeding the
22
- * orthographic projection alongside the near/far guarded in `updateMatrices`,
23
- * and it is derived from the live pose when the mode is switched.
24
- *
25
- * Returns `null` when there is no usable size, so the caller can keep the
26
- * value it already had rather than substitute an arbitrary one.
27
- */
28
- function usableOrthoSize(size) {
29
- return Number.isFinite(size) ? Math.max(MIN_ORTHO_SIZE, size) : null;
30
- }
11
+ import { DEFAULT_ORTHO_SIZE, isUsableBounds, isUsableDistance, usableOrthoSize, } from './camera-guards.js';
31
12
  export class Camera {
32
13
  state;
33
14
  controls;
34
15
  animator;
35
16
  projection;
17
+ firstPerson;
36
18
  constructor() {
37
19
  // Geometry is converted from IFC Z-up to WebGL Y-up during import
38
20
  this.state = {
@@ -53,12 +35,25 @@ export class Camera {
53
35
  sceneBounds: null,
54
36
  orbitAnchorBounds: null,
55
37
  };
56
- const updateMatrices = () => this.updateMatrices();
38
+ // Bound straight to the module-level function rather than to
39
+ // `this.updateMatrices()`: this closure is the per-frame path every
40
+ // sub-system drives the matrices through, and routing it via the private
41
+ // method would add a hop to it. Depth is therefore unchanged from before
42
+ // the extraction (closure → one call), and the callee is monomorphic.
43
+ const updateMatrices = () => updateCameraMatrices(this.state);
57
44
  this.controls = new CameraControls(this.state, updateMatrices);
58
45
  this.projection = new CameraProjection(this.state, updateMatrices);
59
46
  this.animator = new CameraAnimator(this.state, updateMatrices, this.controls, this.projection);
47
+ this.firstPerson = new FirstPersonNavigator(this.state, updateMatrices);
60
48
  this.updateMatrices();
61
49
  }
50
+ /**
51
+ * Cold-path convenience for this class's own setters. The hot path uses the
52
+ * closure built in the constructor; see the note there.
53
+ */
54
+ updateMatrices() {
55
+ updateCameraMatrices(this.state);
56
+ }
62
57
  /**
63
58
  * Set camera aspect ratio
64
59
  */
@@ -135,11 +130,17 @@ export class Camera {
135
130
  * Pan camera (Y-up coordinate system)
136
131
  */
137
132
  pan(deltaX, deltaY, addVelocity = false) {
138
- // Pan speed depends on distance; compute before pan (pan preserves distance)
139
- const panSpeed = this.getDistance() * 0.001;
133
+ // Pan speed depends on distance; compute before pan (pan preserves distance).
134
+ // `getDistance()` reports the pose verbatim, so a malformed one makes this
135
+ // NaN — and the inertia loop *latches* it: it spends velocity only while
136
+ // `Math.abs(velocity) > minVelocity`, which is false for NaN, so a NaN pan
137
+ // velocity is never applied and never decays. Pan inertia would stay dead
138
+ // for the rest of the session even after the pose is corrected (#2441).
139
+ // Skip the velocity rather than seed it with an invented speed.
140
+ const distance = this.getDistance();
140
141
  this.controls.pan(deltaX, deltaY);
141
- if (addVelocity) {
142
- this.animator.addPanVelocity(deltaX, deltaY, panSpeed);
142
+ if (addVelocity && isUsableDistance(distance, 0)) {
143
+ this.animator.addPanVelocity(deltaX, deltaY, distance * 0.001);
143
144
  }
144
145
  }
145
146
  /**
@@ -214,6 +215,23 @@ export class Camera {
214
215
  * callers don't have to thread the FOV through themselves.
215
216
  */
216
217
  fitBoundsAdaptive(bounds, options) {
218
+ // `pickFitPolicy` is pure and would hand back a non-finite pose for an
219
+ // infinite or inverted box, which `snapToFitPolicy` writes verbatim into
220
+ // position, target AND up — the widest single write in the class. This is
221
+ // the auto-fit that runs as geometry streams, so the box comes straight
222
+ // from the model (#2461). Report the pose the camera already has: applying
223
+ // it is a no-op, which is exactly the intended outcome, and callers only
224
+ // read `policy.kind`.
225
+ if (!isUsableBounds(bounds.min, bounds.max)) {
226
+ return {
227
+ kind: 'compact',
228
+ aspect: 1,
229
+ target: { ...this.state.camera.target },
230
+ position: { ...this.state.camera.position },
231
+ up: { ...this.state.camera.up },
232
+ distance: this.getDistance(),
233
+ };
234
+ }
217
235
  const fitOpts = {
218
236
  fovY: this.state.camera.fov,
219
237
  viewportShortPx: options?.viewportShortPx,
@@ -243,13 +261,13 @@ export class Camera {
243
261
  * Set first-person mode
244
262
  */
245
263
  enableFirstPersonMode(enabled) {
246
- this.animator.enableFirstPersonMode(enabled);
264
+ this.firstPerson.setEnabled(enabled);
247
265
  }
248
266
  /**
249
267
  * Move in first-person mode (Y-up coordinate system)
250
268
  */
251
269
  moveFirstPerson(forward, right, up) {
252
- this.animator.moveFirstPerson(forward, right, up);
270
+ this.firstPerson.move(forward, right, up);
253
271
  }
254
272
  /**
255
273
  * Set preset view with explicit bounds (Y-up coordinate system)
@@ -272,6 +290,11 @@ export class Camera {
272
290
  reset() {
273
291
  this.controls.setOrbitCenter(null);
274
292
  this.animator.reset();
293
+ // The walk velocity is smoothed in place, so it survives a model swap and
294
+ // would be spent on the new model's first walk frame. It used to live on
295
+ // the animator, where `animator.reset()` did not clear it either — the
296
+ // extraction is what made the omission visible.
297
+ this.firstPerson.stop();
275
298
  // Drop the previous model's outlier-robust orbit anchor (issue #1394) — it
276
299
  // survives setSceneBounds() syncs, so without this a model swap would orbit
277
300
  // the new model around the old one's centre until the first fit clears it.
@@ -299,7 +322,21 @@ export class Camera {
299
322
  return this.state.camera.fov;
300
323
  }
301
324
  /**
302
- * Get distance from camera position to target
325
+ * Get distance from camera position to target.
326
+ *
327
+ * Deliberately **unsanitized**: it reports the pose as it actually is, so a
328
+ * malformed one (a BCF viewpoint restored from a file reaches the public
329
+ * setters unvalidated) yields NaN rather than a substituted number. This is
330
+ * a measurement, not a control input: it is what `useBCF` reads back when
331
+ * restoring a viewpoint and what the BCF overlay scales markers by. There is
332
+ * no substitute that is right for every reader — a plausible-looking `1`
333
+ * would place every restored target one unit from the eye — and returning a
334
+ * number here would contradict `getPosition()`/`getTarget()`, which are raw,
335
+ * leaving callers no way to tell that the pose is broken. Gesture code inside
336
+ * this package guards with {@link isUsableDistance} instead; each gesture
337
+ * needs a different fallback. Callers OUTSIDE the package cannot use that
338
+ * predicate (it is package-internal) and are not all guarded — see #2466 for
339
+ * the viewpoint-restore path (#2441).
303
340
  */
304
341
  getDistance() {
305
342
  const dir = {
@@ -322,7 +359,14 @@ export class Camera {
322
359
  z: this.state.camera.position.z - this.state.camera.target.z,
323
360
  };
324
361
  const distance = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
325
- if (distance < 1e-6)
362
+ // `distance < 1e-6` alone is a magnitude test, not a finiteness one: the
363
+ // comparison is false for NaN, so a malformed pose fell through the guard
364
+ // that looked like it was catching it, and `Math.asin(Math.max(-1,
365
+ // Math.min(1, NaN)))` is NaN — a NaN elevation that leaves the renderer
366
+ // entirely, into the viewer's rotation readout and the measurement
367
+ // handlers (#2441). Same neutral answer as the degenerate pose, which is
368
+ // preserved verbatim.
369
+ if (!isUsableDistance(distance, 1e-6))
326
370
  return { azimuth: 0, elevation: 0 };
327
371
  // Elevation: angle from horizontal plane
328
372
  const elevation = Math.asin(Math.max(-1, Math.min(1, dir.y / distance))) * 180 / Math.PI;
@@ -456,94 +500,5 @@ export class Camera {
456
500
  getOrbitAnchorBounds() {
457
501
  return this.state.orbitAnchorBounds;
458
502
  }
459
- updateMatrices() {
460
- const dx = this.state.camera.position.x - this.state.camera.target.x;
461
- const dy = this.state.camera.position.y - this.state.camera.target.y;
462
- const dz = this.state.camera.position.z - this.state.camera.target.z;
463
- const rawDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
464
- // A non-finite position or target — a malformed BCF viewpoint reaches the
465
- // public setters unvalidated — would put NaN into near/far and from there
466
- // into the projection matrix, leaving the viewport dead even though
467
- // `MathUtils.lookAt` scrubs the view matrix (#2441). Fall back to a
468
- // neutral distance so the projection stays finite; the camera state itself
469
- // is left as the caller set it.
470
- const distance = Number.isFinite(rawDistance) ? rawDistance : 1;
471
- this.state.viewMatrix = MathUtils.lookAt(this.state.camera.position, this.state.camera.target, this.state.camera.up);
472
- if (this.state.projectionMode === 'orthographic') {
473
- // Orthographic: project scene bounding sphere onto view direction for tight near/far.
474
- // Tight range maximizes depth precision (less z-fighting) and prevents clipping.
475
- const nf = this.computeOrthoNearFar(distance);
476
- this.state.camera.near = nf.near;
477
- this.state.camera.far = nf.far;
478
- // Backstop for the writers that never see `setOrthoSize`'s clamp: the
479
- // orthographic zoom scales `orthoSize` in place and the animator
480
- // interpolates it, so a NaN reaching either (from a malformed pose, or
481
- // from bounds that were themselves non-finite) would land straight in
482
- // the projection matrix. Same fallback shape as the distance above.
483
- const h = Number.isFinite(this.state.orthoSize) ? this.state.orthoSize : DEFAULT_ORTHO_SIZE;
484
- const w = h * this.state.camera.aspect;
485
- this.state.projMatrix = MathUtils.orthographicReverseZ(-w, w, -h, h, this.state.camera.near, this.state.camera.far);
486
- }
487
- else {
488
- // Perspective: adapt near/far based on camera-to-target distance
489
- this.state.camera.near = Math.max(0.01, distance * 0.001);
490
- this.state.camera.far = Math.max(distance * 10, 1000);
491
- this.state.projMatrix = MathUtils.perspectiveReverseZ(this.state.camera.fov, this.state.camera.aspect, this.state.camera.near, this.state.camera.far);
492
- }
493
- this.state.viewProjMatrix = MathUtils.multiply(this.state.projMatrix, this.state.viewMatrix);
494
- }
495
- /**
496
- * Compute tight near/far for orthographic mode by projecting the scene
497
- * bounding sphere onto the camera view direction.
498
- *
499
- * This gives optimal depth precision (minimizing z-fighting) while ensuring
500
- * no geometry is clipped regardless of camera position or view angle.
501
- */
502
- computeOrthoNearFar(distance) {
503
- const bounds = this.state.sceneBounds;
504
- if (!bounds) {
505
- // Fallback: generous range centered on camera
506
- return { near: -Math.max(distance, 500), far: Math.max(distance, 500) };
507
- }
508
- // Scene bounding sphere center and radius
509
- const cx = (bounds.min.x + bounds.max.x) / 2;
510
- const cy = (bounds.min.y + bounds.max.y) / 2;
511
- const cz = (bounds.min.z + bounds.max.z) / 2;
512
- const ex = bounds.max.x - bounds.min.x;
513
- const ey = bounds.max.y - bounds.min.y;
514
- const ez = bounds.max.z - bounds.min.z;
515
- const radius = Math.sqrt(ex * ex + ey * ey + ez * ez) / 2;
516
- // View direction (camera looks from position toward target)
517
- const pos = this.state.camera.position;
518
- let vx = this.state.camera.target.x - pos.x;
519
- let vy = this.state.camera.target.y - pos.y;
520
- let vz = this.state.camera.target.z - pos.z;
521
- const vLen = Math.sqrt(vx * vx + vy * vy + vz * vz);
522
- if (vLen > 1e-8) {
523
- vx /= vLen;
524
- vy /= vLen;
525
- vz /= vLen;
526
- }
527
- // Signed distance from camera to scene center along view direction
528
- const toCenter = (cx - pos.x) * vx + (cy - pos.y) * vy + (cz - pos.z) * vz;
529
- // Near/far as distances from camera along view direction.
530
- // The sphere spans [toCenter - radius, toCenter + radius] along view dir.
531
- // Add 10% padding for safety.
532
- const pad = radius * 0.1 + 1;
533
- let near = toCenter - radius - pad;
534
- let far = toCenter + radius + pad;
535
- // Ensure minimum range for depth precision
536
- if (far - near < 1) {
537
- near -= 0.5;
538
- far += 0.5;
539
- }
540
- // Same contract as the perspective branch: a non-finite camera position or
541
- // scene bound must not reach the projection matrix (#2441).
542
- if (!Number.isFinite(near) || !Number.isFinite(far)) {
543
- const fallback = Math.max(Number.isFinite(distance) ? distance : 0, 500);
544
- return { near: -fallback, far: fallback };
545
- }
546
- return { near, far };
547
- }
548
503
  }
549
504
  //# sourceMappingURL=camera.js.map