@preference-sl/pref-viewer 2.18.0 → 3.0.0-beta.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.
- package/README.md +267 -31
- package/index.d.ts +75 -6
- package/package.json +8 -2
- package/src/babylonjs-animation-controller.js +18 -10
- package/src/babylonjs-animation-opening-menu.js +80 -14
- package/src/babylonjs-animation-opening.js +3 -3
- package/src/babylonjs-ar-floor-bounds.js +169 -0
- package/src/babylonjs-ar-glass.js +128 -0
- package/src/babylonjs-ar-placement-feedback.js +449 -0
- package/src/babylonjs-ar-placement.js +1038 -0
- package/src/babylonjs-camera-wheel-motion.js +35 -2
- package/src/babylonjs-controller.js +507 -125
- package/src/babylonjs-orbital-pointers-input.js +186 -0
- package/src/{render-math.js → babylonjs-render-math.js} +11 -11
- package/src/babylonjs-selection-controller.js +4 -4
- package/src/babylonjs-view-cube-canvas-controller.js +1287 -0
- package/src/babylonjs-view-cube-canvas-orbit-math.js +220 -0
- package/src/babylonjs-view-cube-canvas-renderer.js +775 -0
- package/src/babylonjs-view-cube-controller.js +74 -1103
- package/src/babylonjs-view-cube-svg-controller.js +1012 -0
- package/src/file-storage.js +7 -4
- package/src/gltf-resolver.js +13 -8
- package/src/localization/translations.js +12 -0
- package/src/panzoom-controller.js +4 -4
- package/src/pref-viewer-2d.js +2 -2
- package/src/pref-viewer-3d.js +55 -9
- package/src/pref-viewer-controls-presentation.js +292 -0
- package/src/pref-viewer-dialog.js +86 -7
- package/src/pref-viewer-menu-3d.js +134 -17
- package/src/pref-viewer-style-controller.js +320 -0
- package/src/pref-viewer.js +133 -109
- package/src/quick-look-action.js +280 -0
- package/src/styles.js +702 -98
- package/src/babylonjs-view-cube-renderer.js +0 -548
- package/src/view-cube-orbit-math.js +0 -199
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure orbit and view math for the draggable view cube.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Babylon.js ArcRotateCamera convention (Y-up): the camera sits at
|
|
5
|
+
* target + radius * (sin(beta) * cos(alpha), cos(beta), sin(beta) * sin(alpha)).
|
|
6
|
+
* A camera on +Z therefore has alpha = PI / 2 and beta = PI / 2. The module has no Babylon or DOM
|
|
7
|
+
* dependency, so it is safe to import from any environment.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Orbit pose of a camera around its target, in radians.
|
|
12
|
+
* @typedef {Object} OrbitAngles
|
|
13
|
+
* @property {number} alpha - Longitude around the Y axis, atan2(z, x) convention.
|
|
14
|
+
* @property {number} beta - Polar angle measured from +Y, in [0, PI].
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Plain three-component vector.
|
|
19
|
+
* @typedef {Object} Vec3
|
|
20
|
+
* @property {number} x
|
|
21
|
+
* @property {number} y
|
|
22
|
+
* @property {number} z
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* One of the 26 cube views.
|
|
27
|
+
* @typedef {Object} ViewCubeDirection
|
|
28
|
+
* @property {string} id - Signed-axis triple such as "0,0,1" (face), "1,0,1" (edge) or "1,1,1" (corner).
|
|
29
|
+
* @property {Vec3} direction - Normalized direction from the target towards the camera.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Stateless orbit calculations and sizing policy for the Canvas ViewCube.
|
|
34
|
+
* All operations are static and pure; callers need no instance or disposal.
|
|
35
|
+
* Private constants own numerical tolerances and sizing; read-only getters expose
|
|
36
|
+
* the polar margin and immutable region directions required by consumers.
|
|
37
|
+
* This class has no Babylon.js or DOM dependency and never modifies input poses.
|
|
38
|
+
*/
|
|
39
|
+
export default class BabylonJSViewCubeCanvasOrbitMath {
|
|
40
|
+
/** Full revolution in radians, used for angle normalization. */
|
|
41
|
+
static #TWO_PI = Math.PI * 2;
|
|
42
|
+
/** Relative horizontal-length threshold below which longitude is undefined. */
|
|
43
|
+
static #POLE_EPSILON = 1e-9;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Safety margin, in radians, kept between beta and the poles to avoid gimbal flips.
|
|
47
|
+
* @type {number}
|
|
48
|
+
*/
|
|
49
|
+
static #BETA_MARGIN = 0.05;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The 26 cube views: 6 faces, 12 edges and 8 corners.
|
|
53
|
+
* @type {ReadonlyArray<ViewCubeDirection>}
|
|
54
|
+
*/
|
|
55
|
+
static #VIEW_CUBE_DIRECTIONS = Object.freeze(BabylonJSViewCubeCanvasOrbitMath.#createViewCubeDirections());
|
|
56
|
+
|
|
57
|
+
// Cube sizing, from an ergonomic evaluation of the 26-region cube. WCAG 2.2 sets a 24x24 CSS px minimum target
|
|
58
|
+
// size, and at the former fixed 100 px the corner spheres measure about 11 px and the edge bars about 4 px
|
|
59
|
+
// (0.26 and 0.09 over the +-1.15 orthographic extent). Finger-sized 44-48 px targets are unreachable with 26
|
|
60
|
+
// regions on one cube, so the cube grows with the viewer within a bounded range and the renderer adds a pick
|
|
61
|
+
// tolerance for small regions.
|
|
62
|
+
/** Share of the smaller viewer dimension used as the cube side before clamping. */
|
|
63
|
+
static #VIEW_CUBE_SIZE_RATIO = 0.22;
|
|
64
|
+
/** Smallest preferred cube side, in CSS pixels, applied before the viewer-share cap. */
|
|
65
|
+
static #VIEW_CUBE_MIN_SIDE = 96;
|
|
66
|
+
/** Largest cube side, in CSS pixels. */
|
|
67
|
+
static #VIEW_CUBE_MAX_SIDE = 180;
|
|
68
|
+
/** Hard cap: the cube never takes more than this share of the smaller viewer dimension. */
|
|
69
|
+
static #VIEW_CUBE_MAX_VIEWPORT_SHARE = 0.4;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Wraps an angle into the half-open interval (-PI, PI].
|
|
73
|
+
* @param {number} angle - Angle in radians; any magnitude.
|
|
74
|
+
* @returns {number} Equivalent angle in (-PI, PI].
|
|
75
|
+
*/
|
|
76
|
+
static normalizeAngle(angle) {
|
|
77
|
+
return angle - BabylonJSViewCubeCanvasOrbitMath.#TWO_PI * Math.ceil((angle - Math.PI) / BabylonJSViewCubeCanvasOrbitMath.#TWO_PI);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Computes the signed shortest angular difference between two angles.
|
|
82
|
+
* @param {number} from - Start angle in radians.
|
|
83
|
+
* @param {number} to - End angle in radians.
|
|
84
|
+
* @returns {number} Signed difference in (-PI, PI]; positive means counter-clockwise growth of the angle.
|
|
85
|
+
*/
|
|
86
|
+
static shortestDelta(from, to) {
|
|
87
|
+
return BabylonJSViewCubeCanvasOrbitMath.normalizeAngle(to - from);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clamps beta to [BETA_MARGIN, PI - BETA_MARGIN].
|
|
92
|
+
* @param {number} beta - Polar angle in radians.
|
|
93
|
+
* @returns {number} Clamped polar angle.
|
|
94
|
+
*/
|
|
95
|
+
static clampBeta(beta) {
|
|
96
|
+
return Math.min(Math.max(beta, BabylonJSViewCubeCanvasOrbitMath.#BETA_MARGIN), Math.PI - BabylonJSViewCubeCanvasOrbitMath.#BETA_MARGIN);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Converts orbit angles into the unit vector pointing from the target towards the camera.
|
|
101
|
+
* @param {OrbitAngles} pose - Orbit angles in radians.
|
|
102
|
+
* @returns {Vec3} Unit direction (sin(beta) * cos(alpha), cos(beta), sin(beta) * sin(alpha)).
|
|
103
|
+
*/
|
|
104
|
+
static directionFromAngles({ alpha, beta }) {
|
|
105
|
+
const sinBeta = Math.sin(beta);
|
|
106
|
+
return { x: sinBeta * Math.cos(alpha), y: Math.cos(beta), z: sinBeta * Math.sin(alpha) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a direction from the target towards the camera into orbit angles.
|
|
111
|
+
* The vector need not be normalized. Beta retains the full [0, PI] range so camera synchronization
|
|
112
|
+
* can represent top and bottom without the drag safety margin. When the horizontal component vanishes alpha is undefined and
|
|
113
|
+
* fallbackAlpha is returned unchanged to avoid an arbitrary spin.
|
|
114
|
+
* A zero-length or non-finite vector has no direction: the function returns null instead of throwing.
|
|
115
|
+
* @param {Vec3} direction - Direction from the target towards the camera.
|
|
116
|
+
* @param {number} [fallbackAlpha=0] - Alpha to keep at the poles.
|
|
117
|
+
* @returns {OrbitAngles|null} Orbit angles, or null when the vector has no usable direction.
|
|
118
|
+
*/
|
|
119
|
+
static anglesFromDirection({ x, y, z }, fallbackAlpha = 0) {
|
|
120
|
+
const length = Math.hypot(x, y, z);
|
|
121
|
+
if (!Number.isFinite(length) || length === 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const beta = Math.acos(Math.min(Math.max(y / length, -1), 1));
|
|
125
|
+
const alpha = Math.hypot(x, z) / length < BabylonJSViewCubeCanvasOrbitMath.#POLE_EPSILON ? fallbackAlpha : Math.atan2(z, x);
|
|
126
|
+
return { alpha, beta };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Interpolates two orbit poses; alpha follows the shortest angular path, beta is linear.
|
|
131
|
+
* @param {OrbitAngles} a - Start pose.
|
|
132
|
+
* @param {OrbitAngles} b - End pose.
|
|
133
|
+
* @param {number} t - Interpolation factor, 0 gives a and 1 gives b (equivalent modulo 2*PI for alpha).
|
|
134
|
+
* @returns {OrbitAngles} New pose; inputs are not modified.
|
|
135
|
+
*/
|
|
136
|
+
static lerpAngles(a, b, t) {
|
|
137
|
+
return {
|
|
138
|
+
alpha: a.alpha + BabylonJSViewCubeCanvasOrbitMath.shortestDelta(a.alpha, b.alpha) * t,
|
|
139
|
+
beta: a.beta + (b.beta - a.beta) * t,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Applies a drag increment to a pose. Alpha is not wrapped; beta is clamped away from the poles.
|
|
145
|
+
* @param {OrbitAngles} pose - Current pose.
|
|
146
|
+
* @param {number} dAlpha - Alpha increment in radians.
|
|
147
|
+
* @param {number} dBeta - Beta increment in radians.
|
|
148
|
+
* @returns {OrbitAngles} New pose; the input is not modified.
|
|
149
|
+
*/
|
|
150
|
+
static applyDragDelta({ alpha, beta }, dAlpha, dBeta) {
|
|
151
|
+
return { alpha: alpha + dAlpha, beta: BabylonJSViewCubeCanvasOrbitMath.clampBeta(beta + dBeta) };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Builds the 26 signed-axis views in the same order as the cube controller: faces, edges, corners.
|
|
156
|
+
* Ids and directions equal those of BabylonJSViewCubeController.createViewCubeRegions(); a test
|
|
157
|
+
* guards against drift.
|
|
158
|
+
* @returns {ViewCubeDirection[]}
|
|
159
|
+
*/
|
|
160
|
+
static #createViewCubeDirections() {
|
|
161
|
+
const axes = [];
|
|
162
|
+
for (let i = 0; i < 3; i++) {
|
|
163
|
+
for (const s of [-1, 1]) {
|
|
164
|
+
const axis = [0, 0, 0];
|
|
165
|
+
axis[i] = s;
|
|
166
|
+
axes.push(axis);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i < 3; i++) {
|
|
170
|
+
for (let j = i + 1; j < 3; j++) {
|
|
171
|
+
for (const s of [-1, 1]) {
|
|
172
|
+
for (const t of [-1, 1]) {
|
|
173
|
+
const axis = [0, 0, 0];
|
|
174
|
+
axis[i] = s;
|
|
175
|
+
axis[j] = t;
|
|
176
|
+
axes.push(axis);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const x of [-1, 1]) {
|
|
182
|
+
for (const y of [-1, 1]) {
|
|
183
|
+
for (const z of [-1, 1]) {
|
|
184
|
+
axes.push([x, y, z]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return axes.map((axis) => {
|
|
189
|
+
const length = Math.hypot(...axis);
|
|
190
|
+
const [x, y, z] = axis.map((c) => c / length);
|
|
191
|
+
return Object.freeze({ id: axis.join(","), direction: Object.freeze({ x, y, z }) });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Computes the side of the square view cube for a viewer.
|
|
197
|
+
* side = min(clamp(0.22 * m, 96, 180), 0.4 * m) where m is the smaller viewport dimension, rounded to an integer.
|
|
198
|
+
* @param {number} viewportWidth - Viewer width in CSS pixels.
|
|
199
|
+
* @param {number} viewportHeight - Viewer height in CSS pixels.
|
|
200
|
+
* @returns {number} Integer side in CSS pixels, or 0 when the viewport is empty or not finite.
|
|
201
|
+
*/
|
|
202
|
+
static computeViewCubeSize(viewportWidth, viewportHeight) {
|
|
203
|
+
const smaller = Math.min(viewportWidth, viewportHeight);
|
|
204
|
+
if (!Number.isFinite(viewportWidth) || !Number.isFinite(viewportHeight) || smaller <= 0) {
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
const preferred = Math.min(Math.max(BabylonJSViewCubeCanvasOrbitMath.#VIEW_CUBE_SIZE_RATIO * smaller, BabylonJSViewCubeCanvasOrbitMath.#VIEW_CUBE_MIN_SIDE), BabylonJSViewCubeCanvasOrbitMath.#VIEW_CUBE_MAX_SIDE);
|
|
208
|
+
return Math.round(Math.min(preferred, BabylonJSViewCubeCanvasOrbitMath.#VIEW_CUBE_MAX_VIEWPORT_SHARE * smaller));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** @returns {number} Read-only polar safety margin in radians. */
|
|
212
|
+
static get BETA_MARGIN() {
|
|
213
|
+
return BabylonJSViewCubeCanvasOrbitMath.#BETA_MARGIN;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** @returns {ReadonlyArray<ViewCubeDirection>} Shared deeply frozen face, edge and corner directions. */
|
|
217
|
+
static get VIEW_CUBE_DIRECTIONS() {
|
|
218
|
+
return BabylonJSViewCubeCanvasOrbitMath.#VIEW_CUBE_DIRECTIONS;
|
|
219
|
+
}
|
|
220
|
+
}
|