@ifc-lite/renderer 1.43.0 → 1.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/camera-animation.d.ts +20 -32
- package/dist/camera-animation.d.ts.map +1 -1
- package/dist/camera-animation.js +71 -358
- package/dist/camera-animation.js.map +1 -1
- package/dist/camera-controls.d.ts +11 -51
- package/dist/camera-controls.d.ts.map +1 -1
- package/dist/camera-controls.js +140 -12
- package/dist/camera-controls.js.map +1 -1
- package/dist/camera-first-person.d.ts +52 -0
- package/dist/camera-first-person.d.ts.map +1 -0
- package/dist/camera-first-person.js +119 -0
- package/dist/camera-first-person.js.map +1 -0
- package/dist/camera-framing.d.ts +86 -0
- package/dist/camera-framing.d.ts.map +1 -0
- package/dist/camera-framing.js +230 -0
- package/dist/camera-framing.js.map +1 -0
- package/dist/camera-guards.d.ts +148 -0
- package/dist/camera-guards.d.ts.map +1 -0
- package/dist/camera-guards.js +150 -0
- package/dist/camera-guards.js.map +1 -0
- package/dist/camera-matrices.d.ts +14 -0
- package/dist/camera-matrices.d.ts.map +1 -0
- package/dist/camera-matrices.js +146 -0
- package/dist/camera-matrices.js.map +1 -0
- package/dist/camera-preset-view.d.ts +56 -0
- package/dist/camera-preset-view.d.ts.map +1 -0
- package/dist/camera-preset-view.js +180 -0
- package/dist/camera-preset-view.js.map +1 -0
- package/dist/camera-projection.d.ts +1 -1
- package/dist/camera-projection.d.ts.map +1 -1
- package/dist/camera-projection.js +83 -30
- package/dist/camera-projection.js.map +1 -1
- package/dist/camera-state.d.ts +62 -0
- package/dist/camera-state.d.ts.map +1 -0
- package/dist/camera-state.js +5 -0
- package/dist/camera-state.js.map +1 -0
- package/dist/camera.d.ts +22 -11
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +75 -120
- package/dist/camera.js.map +1 -1
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +23 -1
- package/dist/environment.js.map +1 -1
- package/dist/fill-bridge-anchor.d.ts +32 -0
- package/dist/fill-bridge-anchor.d.ts.map +1 -0
- package/dist/fill-bridge-anchor.js +82 -0
- package/dist/fill-bridge-anchor.js.map +1 -0
- package/dist/fill-predicates.d.ts +44 -0
- package/dist/fill-predicates.d.ts.map +1 -0
- package/dist/fill-predicates.js +65 -0
- package/dist/fill-predicates.js.map +1 -0
- package/dist/fill-triangulate.d.ts +102 -0
- package/dist/fill-triangulate.d.ts.map +1 -0
- package/dist/fill-triangulate.js +333 -0
- package/dist/fill-triangulate.js.map +1 -0
- package/dist/index.d.ts +154 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +294 -45
- package/dist/index.js.map +1 -1
- package/dist/math.d.ts +49 -1
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +122 -67
- package/dist/math.js.map +1 -1
- package/dist/renderer-symbolic-overlays.d.ts.map +1 -1
- package/dist/renderer-symbolic-overlays.js +20 -25
- package/dist/renderer-symbolic-overlays.js.map +1 -1
- package/dist/scene-geometry.d.ts +32 -0
- package/dist/scene-geometry.d.ts.map +1 -1
- package/dist/scene-geometry.js +66 -0
- package/dist/scene-geometry.js.map +1 -1
- package/dist/scene.d.ts +25 -0
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +91 -109
- package/dist/scene.js.map +1 -1
- package/dist/section-2d-lift.d.ts +109 -0
- package/dist/section-2d-lift.d.ts.map +1 -0
- package/dist/section-2d-lift.js +168 -0
- package/dist/section-2d-lift.js.map +1 -0
- package/dist/section-2d-line-buffer.d.ts +58 -0
- package/dist/section-2d-line-buffer.d.ts.map +1 -0
- package/dist/section-2d-line-buffer.js +109 -0
- package/dist/section-2d-line-buffer.js.map +1 -0
- package/dist/section-2d-overlay.d.ts +28 -77
- package/dist/section-2d-overlay.d.ts.map +1 -1
- package/dist/section-2d-overlay.js +161 -593
- package/dist/section-2d-overlay.js.map +1 -1
- package/dist/section-plane-basis.d.ts +22 -3
- package/dist/section-plane-basis.d.ts.map +1 -1
- package/dist/section-plane-basis.js +51 -10
- package/dist/section-plane-basis.js.map +1 -1
- package/dist/section-plane.d.ts.map +1 -1
- package/dist/section-plane.js +10 -1
- package/dist/section-plane.js.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.d.ts +102 -0
- package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -0
- package/dist/shaders/section-2d-overlay.wgsl.js +281 -0
- package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -0
- package/dist/symbolic-overlay-pipelines.d.ts +10 -39
- package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
- package/dist/symbolic-overlay-pipelines.js +20 -163
- package/dist/symbolic-overlay-pipelines.js.map +1 -1
- 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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
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:
|
|
79
|
-
y:
|
|
80
|
-
z:
|
|
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
|
-
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
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":"AAAA;;+DAE+D;AAS/D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
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 @@
|
|
|
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 {
|
|
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
|
package/dist/camera.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"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"}
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|