@ifc-lite/renderer 1.41.1 → 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 (120) 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-fit-policy.d.ts.map +1 -1
  14. package/dist/camera-fit-policy.js +18 -7
  15. package/dist/camera-fit-policy.js.map +1 -1
  16. package/dist/camera-framing.d.ts +86 -0
  17. package/dist/camera-framing.d.ts.map +1 -0
  18. package/dist/camera-framing.js +230 -0
  19. package/dist/camera-framing.js.map +1 -0
  20. package/dist/camera-guards.d.ts +148 -0
  21. package/dist/camera-guards.d.ts.map +1 -0
  22. package/dist/camera-guards.js +150 -0
  23. package/dist/camera-guards.js.map +1 -0
  24. package/dist/camera-matrices.d.ts +14 -0
  25. package/dist/camera-matrices.d.ts.map +1 -0
  26. package/dist/camera-matrices.js +146 -0
  27. package/dist/camera-matrices.js.map +1 -0
  28. package/dist/camera-preset-view.d.ts +56 -0
  29. package/dist/camera-preset-view.d.ts.map +1 -0
  30. package/dist/camera-preset-view.js +180 -0
  31. package/dist/camera-preset-view.js.map +1 -0
  32. package/dist/camera-projection.d.ts +1 -1
  33. package/dist/camera-projection.d.ts.map +1 -1
  34. package/dist/camera-projection.js +83 -30
  35. package/dist/camera-projection.js.map +1 -1
  36. package/dist/camera-state.d.ts +62 -0
  37. package/dist/camera-state.d.ts.map +1 -0
  38. package/dist/camera-state.js +5 -0
  39. package/dist/camera-state.js.map +1 -0
  40. package/dist/camera.d.ts +22 -11
  41. package/dist/camera.d.ts.map +1 -1
  42. package/dist/camera.js +110 -85
  43. package/dist/camera.js.map +1 -1
  44. package/dist/deviation/deviation-pipeline.d.ts.map +1 -1
  45. package/dist/deviation/deviation-pipeline.js +21 -4
  46. package/dist/deviation/deviation-pipeline.js.map +1 -1
  47. package/dist/environment.d.ts.map +1 -1
  48. package/dist/environment.js +23 -1
  49. package/dist/environment.js.map +1 -1
  50. package/dist/index.d.ts +333 -43
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +753 -704
  53. package/dist/index.js.map +1 -1
  54. package/dist/math.d.ts +59 -2
  55. package/dist/math.d.ts.map +1 -1
  56. package/dist/math.js +173 -23
  57. package/dist/math.js.map +1 -1
  58. package/dist/model-bounds-tracker.d.ts +96 -0
  59. package/dist/model-bounds-tracker.d.ts.map +1 -0
  60. package/dist/model-bounds-tracker.js +147 -0
  61. package/dist/model-bounds-tracker.js.map +1 -0
  62. package/dist/pointcloud/point-cloud-node.d.ts.map +1 -1
  63. package/dist/pointcloud/point-cloud-node.js +24 -4
  64. package/dist/pointcloud/point-cloud-node.js.map +1 -1
  65. package/dist/render-degradation.d.ts +77 -0
  66. package/dist/render-degradation.d.ts.map +1 -0
  67. package/dist/render-degradation.js +52 -0
  68. package/dist/render-degradation.js.map +1 -0
  69. package/dist/render-section-draw.d.ts +51 -0
  70. package/dist/render-section-draw.d.ts.map +1 -0
  71. package/dist/render-section-draw.js +66 -0
  72. package/dist/render-section-draw.js.map +1 -0
  73. package/dist/render-section-plane.d.ts +128 -0
  74. package/dist/render-section-plane.d.ts.map +1 -0
  75. package/dist/render-section-plane.js +298 -0
  76. package/dist/render-section-plane.js.map +1 -0
  77. package/dist/renderer-overlays.d.ts +144 -0
  78. package/dist/renderer-overlays.d.ts.map +1 -0
  79. package/dist/renderer-overlays.js +257 -0
  80. package/dist/renderer-overlays.js.map +1 -0
  81. package/dist/renderer-symbolic-overlays.d.ts +52 -0
  82. package/dist/renderer-symbolic-overlays.d.ts.map +1 -0
  83. package/dist/renderer-symbolic-overlays.js +115 -0
  84. package/dist/renderer-symbolic-overlays.js.map +1 -0
  85. package/dist/scene-geometry.d.ts +32 -0
  86. package/dist/scene-geometry.d.ts.map +1 -1
  87. package/dist/scene-geometry.js +66 -0
  88. package/dist/scene-geometry.js.map +1 -1
  89. package/dist/scene.d.ts +86 -1
  90. package/dist/scene.d.ts.map +1 -1
  91. package/dist/scene.js +391 -201
  92. package/dist/scene.js.map +1 -1
  93. package/dist/section-2d-lift.d.ts +105 -0
  94. package/dist/section-2d-lift.d.ts.map +1 -0
  95. package/dist/section-2d-lift.js +162 -0
  96. package/dist/section-2d-lift.js.map +1 -0
  97. package/dist/section-2d-line-buffer.d.ts +58 -0
  98. package/dist/section-2d-line-buffer.d.ts.map +1 -0
  99. package/dist/section-2d-line-buffer.js +109 -0
  100. package/dist/section-2d-line-buffer.js.map +1 -0
  101. package/dist/section-2d-overlay.d.ts +43 -75
  102. package/dist/section-2d-overlay.d.ts.map +1 -1
  103. package/dist/section-2d-overlay.js +189 -559
  104. package/dist/section-2d-overlay.js.map +1 -1
  105. package/dist/section-plane-basis.d.ts +22 -3
  106. package/dist/section-plane-basis.d.ts.map +1 -1
  107. package/dist/section-plane-basis.js +51 -10
  108. package/dist/section-plane-basis.js.map +1 -1
  109. package/dist/section-plane.d.ts.map +1 -1
  110. package/dist/section-plane.js +10 -1
  111. package/dist/section-plane.js.map +1 -1
  112. package/dist/shaders/section-2d-overlay.wgsl.d.ts +102 -0
  113. package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -0
  114. package/dist/shaders/section-2d-overlay.wgsl.js +281 -0
  115. package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -0
  116. package/dist/visual-enhancement.d.ts +33 -0
  117. package/dist/visual-enhancement.d.ts.map +1 -0
  118. package/dist/visual-enhancement.js +46 -0
  119. package/dist/visual-enhancement.js.map +1 -0
  120. package/package.json +4 -3
@@ -0,0 +1,119 @@
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
+ import { areFiniteNumbers, isUsableDistance } from './camera-guards.js';
5
+ export class FirstPersonNavigator {
6
+ state;
7
+ updateMatrices;
8
+ isFirstPersonMode = false;
9
+ walkVelocity = { x: 0, z: 0 };
10
+ constructor(state, updateMatrices) {
11
+ this.state = state;
12
+ this.updateMatrices = updateMatrices;
13
+ }
14
+ /**
15
+ * Set first-person mode.
16
+ *
17
+ * Load-bearing, not bookkeeping: {@link move} refuses to walk while this is
18
+ * off. The flag was write-only before the extraction — nothing ever read it,
19
+ * so `moveFirstPerson` moved the camera whether or not walk mode had been
20
+ * entered, and the only thing keeping that from being visible was the
21
+ * viewer's *second*, independent check on `activeTool === 'walk'`. Two
22
+ * unrelated gates for one decision means neither is load-bearing on its own,
23
+ * and the renderer is published: an embedder driving `moveFirstPerson` from
24
+ * its own key handler has only this one.
25
+ *
26
+ * Disabling also drops the accumulated walk velocity. It is smoothed in
27
+ * place (`+= (target - current) * 0.15`), so a leftover would be spent on
28
+ * the first frame after walk mode is re-entered — a visible lurch in
29
+ * whatever direction the user happened to be walking when they left.
30
+ */
31
+ setEnabled(enabled) {
32
+ this.isFirstPersonMode = enabled;
33
+ if (!enabled)
34
+ this.stop();
35
+ }
36
+ /**
37
+ * Drop the accumulated walk velocity, keeping the mode as it is.
38
+ *
39
+ * Called on model reset. The mode itself is deliberately *not* cleared:
40
+ * it mirrors the viewer's active tool, which a model load does not change,
41
+ * and clearing it here would leave walk mode silently dead until the user
42
+ * toggled the tool off and back on again.
43
+ */
44
+ stop() {
45
+ this.walkVelocity.x = 0;
46
+ this.walkVelocity.z = 0;
47
+ }
48
+ /**
49
+ * Walk on the horizontal XZ plane (Y-up coordinate system).
50
+ * Forward/backward moves in the camera's horizontal facing direction.
51
+ * Left/right strafes perpendicular. Y position stays fixed (walking on ground).
52
+ * Speed scales with scene size. Movement uses smooth acceleration to avoid
53
+ * abrupt jumps — velocity ramps up over successive frames.
54
+ */
55
+ move(forward, right, _up) {
56
+ // Mode gate, ahead of every other guard: walking is only a thing that
57
+ // exists while walk mode is on. See {@link setEnabled}.
58
+ if (!this.isFirstPersonMode)
59
+ return;
60
+ // Argument-side guard (#2473). `walkVelocity` is accumulated in place, so
61
+ // a non-finite axis latches first-person movement dead for the session
62
+ // exactly the way a non-finite pose did (#2441) — and it does not need an
63
+ // exotic value: both `moveFirstPerson(NaN, 0, 0)` and
64
+ // `moveFirstPerson(Infinity, 0, 0)` came back with a non-finite position
65
+ // on a finite pose. `_up` is unused, so it is deliberately not tested.
66
+ if (!areFiniteNumbers(forward, right))
67
+ return;
68
+ // Camera forward direction projected onto XZ plane
69
+ const dir = {
70
+ x: this.state.camera.target.x - this.state.camera.position.x,
71
+ z: this.state.camera.target.z - this.state.camera.position.z,
72
+ };
73
+ const horizLen = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
74
+ // Finiteness, not just magnitude: `NaN < 1e-10` is false, so a malformed
75
+ // pose fell through here and `dir.x / horizLen` came back NaN. That does
76
+ // not merely produce one bad step — `walkVelocity` is accumulated in place
77
+ // (`+= (target - current) * 0.15`), so a single NaN frame latches it and
78
+ // first-person movement stays dead for the rest of the session even after
79
+ // the pose is corrected (#2441).
80
+ if (!isUsableDistance(horizLen, 1e-10))
81
+ return;
82
+ // Normalized horizontal forward and right vectors
83
+ const fwdX = dir.x / horizLen;
84
+ const fwdZ = dir.z / horizLen;
85
+ const rightX = -fwdZ;
86
+ const rightZ = fwdX;
87
+ // Target velocity from input (forward/right can be -2..2 with sprint)
88
+ const targetVelX = fwdX * forward + rightX * right;
89
+ const targetVelZ = fwdZ * forward + rightZ * right;
90
+ // Smooth acceleration: lerp current walk velocity toward target
91
+ this.walkVelocity.x += (targetVelX - this.walkVelocity.x) * 0.15;
92
+ this.walkVelocity.z += (targetVelZ - this.walkVelocity.z) * 0.15;
93
+ // Speed proportional to scene size (use camera-target distance as proxy)
94
+ const camDir = {
95
+ x: this.state.camera.position.x - this.state.camera.target.x,
96
+ y: this.state.camera.position.y - this.state.camera.target.y,
97
+ z: this.state.camera.position.z - this.state.camera.target.z,
98
+ };
99
+ const distance = Math.sqrt(camDir.x * camDir.x + camDir.y * camDir.y + camDir.z * camDir.z);
100
+ // The horizontal guard above does not cover this one: `camDir` includes Y,
101
+ // so a pose whose *only* bad coordinate is vertical reaches here with a
102
+ // finite `horizLen` and a NaN `distance`. `Math.max` is NaN-transparent —
103
+ // `Math.max(0.02, NaN)` is `NaN`, not the floor — so the clamp would
104
+ // forward it into the offsets written back to position and target.
105
+ if (!isUsableDistance(distance, 0))
106
+ return;
107
+ const speed = Math.max(0.02, distance * 0.004);
108
+ // Apply smoothed velocity
109
+ const offsetX = this.walkVelocity.x * speed;
110
+ const offsetZ = this.walkVelocity.z * speed;
111
+ // Move both position and target by the same offset (preserves view direction)
112
+ this.state.camera.position.x += offsetX;
113
+ this.state.camera.position.z += offsetZ;
114
+ this.state.camera.target.x += offsetX;
115
+ this.state.camera.target.z += offsetZ;
116
+ this.updateMatrices();
117
+ }
118
+ }
119
+ //# sourceMappingURL=camera-first-person.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-first-person.js","sourceRoot":"","sources":["../src/camera-first-person.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"camera-fit-policy.d.ts","sourceRoot":"","sources":["../src/camera-fit-policy.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAoC3C,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,IAAI,EAAE,aAAa,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,EAAE,IAAI,CAAC;IACb,mCAAmC;IACnC,QAAQ,EAAE,IAAI,CAAC;IACf,kEAAkE;IAClE,EAAE,EAAE,IAAI,CAAC;IACT,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,oBAAoB,GAC5B,SAAS,CA8FX"}
1
+ {"version":3,"file":"camera-fit-policy.d.ts","sourceRoot":"","sources":["../src/camera-fit-policy.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAoC3C,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,IAAI,EAAE,aAAa,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,EAAE,IAAI,CAAC;IACb,mCAAmC;IACnC,QAAQ,EAAE,IAAI,CAAC;IACf,kEAAkE;IAClE,EAAE,EAAE,IAAI,CAAC;IACT,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,oBAAoB,GAC5B,SAAS,CAyGX"}
@@ -100,18 +100,29 @@ export function pickFitPolicy(bounds, options) {
100
100
  const maxDistance = longest * 0.3;
101
101
  const distance = clamp(distanceForFeature, Math.min(minDistance, maxDistance), maxDistance);
102
102
  // Build a view direction that looks along +longestAxis (so the
103
- // alignment recedes into the distance) with a 20° downward tilt so the
104
- // ground plane is visible.
103
+ // alignment recedes into the distance) with a 20° tilt off that axis so
104
+ // the model is seen from slightly to one side rather than dead end-on —
105
+ // downward for a horizontal alignment, sideways for a vertical one.
105
106
  const up = { x: 0, y: 1, z: 0 };
106
107
  const along = longestAxis;
107
108
  const tiltCos = Math.cos(LINEAR_TILT_RADIANS);
108
109
  const tiltSin = Math.sin(LINEAR_TILT_RADIANS);
109
- // viewDir = along * cos(tilt) - up * sin(tilt). Camera sits behind the
110
- // target along the *opposite* of viewDir.
110
+ // The axis the view direction is tilted *away from* has to be
111
+ // perpendicular to `along`: rotating a vector away from itself is a no-op.
112
+ // World Y satisfies that for the X- and Z-dominant alignments (roads,
113
+ // railways), but not when the longest axis IS Y — a mast, chimney, shaft,
114
+ // lift core or turbine tower. There the "downward tilt" cancelled out and
115
+ // viewDir came back exactly parallel to `up`, which used to make the whole
116
+ // view-projection matrix NaN and the viewport blank (#2441). Tilt those
117
+ // away from world Z instead, so the camera looks up the axis from off to
118
+ // one side and the pose stays well conditioned.
119
+ const tiltFrom = Math.abs(along.y) > 0.5 ? { x: 0, y: 0, z: 1 } : up;
120
+ // viewDir = along * cos(tilt) - tiltFrom * sin(tilt). Camera sits behind
121
+ // the target along the *opposite* of viewDir.
111
122
  const viewDir = {
112
- x: along.x * tiltCos - up.x * tiltSin,
113
- y: along.y * tiltCos - up.y * tiltSin,
114
- z: along.z * tiltCos - up.z * tiltSin,
123
+ x: along.x * tiltCos - tiltFrom.x * tiltSin,
124
+ y: along.y * tiltCos - tiltFrom.y * tiltSin,
125
+ z: along.z * tiltCos - tiltFrom.z * tiltSin,
115
126
  };
116
127
  const position = {
117
128
  x: center.x - viewDir.x * distance,
@@ -1 +1 @@
1
- {"version":3,"file":"camera-fit-policy.js","sourceRoot":"","sources":["../src/camera-fit-policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"camera-fit-policy.js","sourceRoot":"","sources":["../src/camera-fit-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Framing: "put the camera on this thing, keeping the direction it already
3
+ * has". Given the current camera state and a point or an AABB, work out the
4
+ * pose to end up in.
5
+ *
6
+ * The ViewCube's *preset* directions are the neighbouring subject and live in
7
+ * `camera-preset-view.ts`: there the direction is dictated by a named face and
8
+ * a rotation cycle rather than inherited from the pose, and the box has to pass
9
+ * its own admission check first. They share only the box arithmetic below,
10
+ * which is why `centerOf` / `maxExtentOf` are exported.
11
+ *
12
+ * Pure by construction — every function here reads `CameraInternalState` and
13
+ * returns a target, and none of them writes to it or touches the tween. That
14
+ * is what keeps this module free of a cycle back into `camera-animation.ts`,
15
+ * which is the one that applies the results. Same shape as
16
+ * `camera-fit-policy.ts`, which is already pure-picker + facade-applier.
17
+ *
18
+ * Its failure story is not the tween's. The tween latches a non-finite
19
+ * *gesture delta* into a velocity that never decays (#2441/#2473); framing
20
+ * takes a non-finite *box* — geometry-derived, and every AABB accumulator in
21
+ * this package hands out an inverted `+Inf/-Inf` sentinel for a mesh with no
22
+ * finite vertices — and writes it into position, target AND `orthoSize`, which
23
+ * `getOrthoSize()` then persists into a saved viewpoint (#2461).
24
+ *
25
+ * **Guard placement.** Each input is validated exactly once, here, and a
26
+ * rejection is `null`. Callers null-check and do nothing else: a second,
27
+ * defensive copy of the same guard in the caller would mean neither copy is
28
+ * load-bearing on its own, and the mutation tests that pin these guards would
29
+ * go quiet while still looking green.
30
+ */
31
+ import type { Vec3 } from './types.js';
32
+ import type { CameraInternalState } from './camera-state.js';
33
+ /** A pose to animate to. `orthoSize` is undefined when the fit should not touch zoom. */
34
+ export interface FramingTarget {
35
+ position: Vec3;
36
+ target: Vec3;
37
+ orthoSize?: number;
38
+ }
39
+ /** As {@link FramingTarget}, plus the fit distance `zoomExtent` reports onward. */
40
+ export interface ZoomExtentTarget extends FramingTarget {
41
+ fitDistance: number;
42
+ }
43
+ /** An axis-aligned box. */
44
+ export interface FramingBounds {
45
+ min: Vec3;
46
+ max: Vec3;
47
+ }
48
+ /** Centre of a box. */
49
+ export declare function centerOf(min: Vec3, max: Vec3): Vec3;
50
+ /** Largest edge length of a box. */
51
+ export declare function maxExtentOf(min: Vec3, max: Vec3): number;
52
+ /**
53
+ * Distance at which a box of `maxSize` fits the perspective frustum, with
54
+ * `padding` slack. Shared with `camera-preset-view.ts`, which fits the same
55
+ * way from a dictated direction.
56
+ *
57
+ * Fits **both** screen axes. The vertical half-angle is `fov / 2`; the
58
+ * horizontal one is `atan(tan(fov / 2) * aspect)`, so on a portrait viewport
59
+ * (`aspect < 1`) the horizontal field is the *narrower* of the two and a
60
+ * distance derived from the vertical field alone leaves the box overflowing
61
+ * left and right — the fit silently clips the very thing it was asked to
62
+ * frame. Landscape is unaffected: for `aspect >= 1` the vertical field is
63
+ * already the binding one and this returns the vertical distance exactly,
64
+ * bit for bit.
65
+ *
66
+ * `orthoSizeFor` above has divided by `aspect` for the same reason since it
67
+ * was written; this is the perspective half of the same rule, which had been
68
+ * missing (the three fit-distance formulas in this package all predate it).
69
+ */
70
+ export declare function fitDistanceFor(state: CameraInternalState, maxSize: number, padding: number): number;
71
+ /**
72
+ * Frame/center view on a point (keeps current distance and direction).
73
+ * Standard CAD "Frame Selection" behavior.
74
+ */
75
+ export declare function framePointTarget(state: CameraInternalState, point: Vec3): FramingTarget | null;
76
+ /**
77
+ * Frame selection - zoom to fit bounds while keeping current view direction.
78
+ * This is what "Frame Selection" should do - zoom to fill screen.
79
+ */
80
+ export declare function frameBoundsTarget(state: CameraInternalState, min: Vec3, max: Vec3): FramingTarget | null;
81
+ /**
82
+ * Zoom to extents: same fit, wider padding, and the current view direction is
83
+ * taken from the pose rather than the view matrix.
84
+ */
85
+ export declare function zoomExtentTarget(state: CameraInternalState, min: Vec3, max: Vec3): ZoomExtentTarget | null;
86
+ //# sourceMappingURL=camera-framing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-framing.d.ts","sourceRoot":"","sources":["../src/camera-framing.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,2BAA2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,IAAI,CAAC;CACX;AAED,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAMnD;AAED,oCAAoC;AACpC,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,CAExD;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQnG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,IAAI,GAAG,aAAa,GAAG,IAAI,CAuB9F;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,aAAa,GAAG,IAAI,CA4ExG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,gBAAgB,GAAG,IAAI,CAoE1G"}
@@ -0,0 +1,230 @@
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
+ import { areFiniteNumbers, isUsableBounds, isUsableDistance } from './camera-guards.js';
5
+ /** Centre of a box. */
6
+ export function centerOf(min, max) {
7
+ return {
8
+ x: (min.x + max.x) / 2,
9
+ y: (min.y + max.y) / 2,
10
+ z: (min.z + max.z) / 2,
11
+ };
12
+ }
13
+ /** Largest edge length of a box. */
14
+ export function maxExtentOf(min, max) {
15
+ return Math.max(max.x - min.x, max.y - min.y, max.z - min.z);
16
+ }
17
+ /**
18
+ * Orthographic half-height that shows a box of `maxSize` with `padding` slack,
19
+ * or `undefined` in perspective mode where `orthoSize` is not in play.
20
+ */
21
+ function orthoSizeFor(state, maxSize, padding) {
22
+ if (state.projectionMode !== 'orthographic')
23
+ return undefined;
24
+ const aspect = state.camera.aspect || 1;
25
+ return Math.max(0.01, maxSize / 2, maxSize / 2 / aspect) * padding;
26
+ }
27
+ /**
28
+ * Distance at which a box of `maxSize` fits the perspective frustum, with
29
+ * `padding` slack. Shared with `camera-preset-view.ts`, which fits the same
30
+ * way from a dictated direction.
31
+ *
32
+ * Fits **both** screen axes. The vertical half-angle is `fov / 2`; the
33
+ * horizontal one is `atan(tan(fov / 2) * aspect)`, so on a portrait viewport
34
+ * (`aspect < 1`) the horizontal field is the *narrower* of the two and a
35
+ * distance derived from the vertical field alone leaves the box overflowing
36
+ * left and right — the fit silently clips the very thing it was asked to
37
+ * frame. Landscape is unaffected: for `aspect >= 1` the vertical field is
38
+ * already the binding one and this returns the vertical distance exactly,
39
+ * bit for bit.
40
+ *
41
+ * `orthoSizeFor` above has divided by `aspect` for the same reason since it
42
+ * was written; this is the perspective half of the same rule, which had been
43
+ * missing (the three fit-distance formulas in this package all predate it).
44
+ */
45
+ export function fitDistanceFor(state, maxSize, padding) {
46
+ const fovFactor = Math.tan(state.camera.fov / 2);
47
+ const vertical = (maxSize / 2) / fovFactor * padding;
48
+ const aspect = state.camera.aspect;
49
+ // `setAspect` is the only writer and already rejects a non-positive or
50
+ // non-finite ratio, so this only ever narrows the *portrait* case.
51
+ if (!Number.isFinite(aspect) || aspect <= 0 || aspect >= 1)
52
+ return vertical;
53
+ return vertical / aspect;
54
+ }
55
+ /**
56
+ * Frame/center view on a point (keeps current distance and direction).
57
+ * Standard CAD "Frame Selection" behavior.
58
+ */
59
+ export function framePointTarget(state, point) {
60
+ // The point is added to the current offset and animated into both
61
+ // `position` and `target`, so a non-finite one destroys the pose. It is
62
+ // externally derived: `zoomToTopic` frames a BCF marker position, which is
63
+ // computed from a file-supplied viewpoint direction (#2461/#2466).
64
+ if (!areFiniteNumbers(point.x, point.y, point.z))
65
+ return null;
66
+ // Keep current viewing direction and distance
67
+ const dir = {
68
+ x: state.camera.position.x - state.camera.target.x,
69
+ y: state.camera.position.y - state.camera.target.y,
70
+ z: state.camera.position.z - state.camera.target.z,
71
+ };
72
+ // New position: point + current offset
73
+ return {
74
+ position: {
75
+ x: point.x + dir.x,
76
+ y: point.y + dir.y,
77
+ z: point.z + dir.z,
78
+ },
79
+ target: point,
80
+ };
81
+ }
82
+ /**
83
+ * Frame selection - zoom to fit bounds while keeping current view direction.
84
+ * This is what "Frame Selection" should do - zoom to fill screen.
85
+ */
86
+ export function frameBoundsTarget(state, min, max) {
87
+ // Bounds are the upstream input #2450 stopped short of (#2461). They are
88
+ // not caller-authored constants: they come from geometry, and every AABB
89
+ // accumulator in this package starts from `min = +Infinity, max =
90
+ // -Infinity` and only narrows on a comparison — which is false for a
91
+ // non-finite vertex — so a mesh with no finite vertices hands out that
92
+ // inverted sentinel as if it were a real box. `Math.max` picking the
93
+ // largest extent is NaN-transparent, so it reaches `position`, `target`
94
+ // AND `orthoSize`, and `getOrthoSize()` is what a saved viewpoint persists.
95
+ if (!isUsableBounds(min, max))
96
+ return null;
97
+ const center = centerOf(min, max);
98
+ const maxSize = maxExtentOf(min, max);
99
+ if (maxSize < 1e-6) {
100
+ // Very small or zero size - just center on it
101
+ return framePointTarget(state, center);
102
+ }
103
+ // Calculate required distance based on FOV to fit bounds
104
+ const distance = fitDistanceFor(state, maxSize, 1.2); // 1.2x padding for nice framing
105
+ // Get current viewing direction from view matrix (more reliable than position-target)
106
+ // View matrix forward is -Z axis in view space
107
+ const viewMatrix = state.viewMatrix.m;
108
+ // Extract forward direction from view matrix (negative Z column, normalized)
109
+ let dir = {
110
+ x: -viewMatrix[8], // -m[2][0] (forward X)
111
+ y: -viewMatrix[9], // -m[2][1] (forward Y)
112
+ z: -viewMatrix[10], // -m[2][2] (forward Z)
113
+ };
114
+ const dirLen = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
115
+ // Normalize direction
116
+ if (dirLen > 1e-6) {
117
+ dir.x /= dirLen;
118
+ dir.y /= dirLen;
119
+ dir.z /= dirLen;
120
+ }
121
+ else {
122
+ // Fallback: use position-target if view matrix is invalid
123
+ dir = {
124
+ x: state.camera.position.x - state.camera.target.x,
125
+ y: state.camera.position.y - state.camera.target.y,
126
+ z: state.camera.position.z - state.camera.target.z,
127
+ };
128
+ const fallbackLen = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
129
+ // Finiteness, not just magnitude — see `zoomExtentTarget`. `dirLen` above
130
+ // is safe (`MathUtils.lookAt` guarantees a finite view matrix), but this
131
+ // fallback reads the raw pose, where an overflowed coordinate is reachable.
132
+ if (isUsableDistance(fallbackLen, 1e-6)) {
133
+ dir.x /= fallbackLen;
134
+ dir.y /= fallbackLen;
135
+ dir.z /= fallbackLen;
136
+ }
137
+ else {
138
+ // Last resort: southeast isometric
139
+ dir.x = 0.6;
140
+ dir.y = 0.5;
141
+ dir.z = 0.6;
142
+ const len = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
143
+ dir.x /= len;
144
+ dir.y /= len;
145
+ dir.z /= len;
146
+ }
147
+ }
148
+ return {
149
+ // New position: center + direction * distance
150
+ position: {
151
+ x: center.x + dir.x * distance,
152
+ y: center.y + dir.y * distance,
153
+ z: center.z + dir.z * distance,
154
+ },
155
+ target: center,
156
+ // Calculate orthoSize for orthographic mode so zoom level resets properly
157
+ orthoSize: orthoSizeFor(state, maxSize, 1.2),
158
+ };
159
+ }
160
+ /**
161
+ * Zoom to extents: same fit, wider padding, and the current view direction is
162
+ * taken from the pose rather than the view matrix.
163
+ */
164
+ export function zoomExtentTarget(state, min, max) {
165
+ // Same input class and same reasoning as `frameBoundsTarget` (#2461).
166
+ if (!isUsableBounds(min, max))
167
+ return null;
168
+ const center = centerOf(min, max);
169
+ const maxSize = maxExtentOf(min, max);
170
+ // Keep current viewing direction
171
+ const dir = {
172
+ x: state.camera.position.x - state.camera.target.x,
173
+ y: state.camera.position.y - state.camera.target.y,
174
+ z: state.camera.position.z - state.camera.target.z,
175
+ };
176
+ const currentDistance = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
177
+ // The degenerate box `frameBoundsTarget` has always special-cased and this
178
+ // one did not. `isUsableBounds` deliberately admits `max === min` (a flat
179
+ // wall, a single picked point, a one-element model), and for such a box the
180
+ // fit distance below is *zero* — so `position` is written equal to `target`,
181
+ // a pose that carries no view direction at all and that `MathUtils.lookAt`
182
+ // then has to substitute a whole basis for. Centre on the point at the
183
+ // distance the camera already has, exactly as `frameBounds` does, and report
184
+ // that distance rather than zero.
185
+ //
186
+ // Only when the current offset is usable. When it is not, the pose already
187
+ // has position === target (or is non-finite), so there is no offset to keep
188
+ // and nothing is gained by keeping it; fall through to the isometric
189
+ // fallback below, which is what this path did before.
190
+ if (maxSize < 1e-6 && isUsableDistance(currentDistance, 1e-10)) {
191
+ const framed = framePointTarget(state, center);
192
+ if (framed)
193
+ return { ...framed, fitDistance: currentDistance };
194
+ }
195
+ // Calculate required distance based on FOV
196
+ const distance = fitDistanceFor(state, maxSize, 1.5); // 1.5x for padding
197
+ // Normalize direction. Finiteness, not just magnitude: `len > 1e-10` is
198
+ // *true* for Infinity, and `Infinity / Infinity` is NaN, so a pose whose
199
+ // position has overflowed walks past a bare floor and writes a NaN position
200
+ // from a perfectly usable box (#2441).
201
+ if (isUsableDistance(currentDistance, 1e-10)) {
202
+ dir.x /= currentDistance;
203
+ dir.y /= currentDistance;
204
+ dir.z /= currentDistance;
205
+ }
206
+ else {
207
+ // Fallback direction
208
+ dir.x = 0.6;
209
+ dir.y = 0.5;
210
+ dir.z = 0.6;
211
+ const len = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
212
+ dir.x /= len;
213
+ dir.y /= len;
214
+ dir.z /= len;
215
+ }
216
+ return {
217
+ // New position: center + direction * distance
218
+ position: {
219
+ x: center.x + dir.x * distance,
220
+ y: center.y + dir.y * distance,
221
+ z: center.z + dir.z * distance,
222
+ },
223
+ target: center,
224
+ // Calculate orthoSize for orthographic mode so zoom level resets properly
225
+ orthoSize: orthoSizeFor(state, maxSize, 1.5),
226
+ // The caller hands this to `CameraProjection.updateNearFarPlanes`.
227
+ fitDistance: distance,
228
+ };
229
+ }
230
+ //# sourceMappingURL=camera-framing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-framing.js","sourceRoot":"","sources":["../src/camera-framing.ts"],"names":[],"mappings":"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"}