@preference-sl/pref-viewer 2.16.2 → 2.17.0-beta.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/README.md +184 -22
- package/index.d.ts +11 -1
- package/package.json +1 -1
- package/src/babylonjs-animation-controller.js +12 -163
- package/src/babylonjs-animation-opening-menu.js +8 -1
- package/src/babylonjs-animation-opening.js +21 -1
- package/src/babylonjs-animation-overlay-controller.js +145 -0
- package/src/babylonjs-camera-framing.js +171 -0
- package/src/babylonjs-camera-zoom-controller.js +272 -0
- package/src/babylonjs-controller.js +640 -378
- package/src/babylonjs-selection-controller.js +215 -0
- package/src/babylonjs-view-cube-controller.js +810 -0
- package/src/file-storage.js +13 -0
- package/src/gltf-storage.js +97 -0
- package/src/localization/i18n.js +147 -74
- package/src/localization/translations.js +18 -0
- package/src/material-utils.js +67 -60
- package/src/panzoom-controller.js +14 -1
- package/src/pref-viewer-2d.js +16 -4
- package/src/pref-viewer-3d-data.js +166 -0
- package/src/pref-viewer-3d.js +40 -5
- package/src/pref-viewer-dialog.js +7 -1
- package/src/pref-viewer-menu-3d.js +57 -37
- package/src/pref-viewer.js +155 -44
- package/src/render-math.js +130 -117
- package/src/styles.js +94 -3
- package/src/svg-resolver.js +5 -4
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies semantic selection state to Babylon model meshes using non-destructive overlays.
|
|
3
|
+
* Retains the semantic snapshot across model replacements and restores original overlays on cleanup.
|
|
4
|
+
* @class BabylonJSSelectionController
|
|
5
|
+
*/
|
|
6
|
+
export default class BabylonJSSelectionController {
|
|
7
|
+
#getModelMeshes;
|
|
8
|
+
#requestRender;
|
|
9
|
+
#selectableColor;
|
|
10
|
+
#selectedColor;
|
|
11
|
+
#overlayAlpha;
|
|
12
|
+
#originalOverlays = new Map();
|
|
13
|
+
#state = {};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Removes the glTF loader suffix from a mesh id while preserving every other id shape.
|
|
17
|
+
* @public
|
|
18
|
+
* @static
|
|
19
|
+
* @param {unknown} value - Mesh id candidate.
|
|
20
|
+
* @returns {string} Normalized semantic id, or an empty string when unusable.
|
|
21
|
+
*/
|
|
22
|
+
static normalizeMeshId(value) {
|
|
23
|
+
return typeof value === "string" ? value.replace(/_primitive\d+$/, "") : "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Determines whether a completed pointer gesture represents a primary click.
|
|
28
|
+
* @public
|
|
29
|
+
* @static
|
|
30
|
+
* @param {{x:number,y:number,button:number,pointerId?:number,dragged?:boolean}|null} start - Pointer-down snapshot.
|
|
31
|
+
* @param {{x:number,y:number,button:number,pointerId?:number}|null} end - Pointer-up snapshot.
|
|
32
|
+
* @param {number} maxDistancePx - Maximum Euclidean movement in CSS pixels.
|
|
33
|
+
* @returns {boolean} Whether the gesture is a click.
|
|
34
|
+
*/
|
|
35
|
+
static isPrimaryClickGesture(start, end, maxDistancePx) {
|
|
36
|
+
if (!start || !end || start.button !== 0 || end.button !== 0 || start.dragged || start.pointerId !== end.pointerId) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return Math.hypot(end.x - start.x, end.y - start.y) <= maxDistancePx;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Checks that a model mesh has geometry and can participate in semantic picking.
|
|
44
|
+
* Explicit ray predicates bypass Babylon's default visibility and pickability checks.
|
|
45
|
+
* @public
|
|
46
|
+
* @static
|
|
47
|
+
* @param {object} mesh - Babylon mesh considered by the picking predicate.
|
|
48
|
+
* @param {Set<object>} modelMeshes - Mesh references belonging to the model container.
|
|
49
|
+
* @returns {boolean} Whether the mesh is visible, enabled, pickable and has vertices.
|
|
50
|
+
*/
|
|
51
|
+
static isPickableModelMesh(mesh, modelMeshes) {
|
|
52
|
+
return modelMeshes.has(mesh) && mesh.isVisible !== false && mesh.visibility !== 0
|
|
53
|
+
&& mesh.isPickable !== false && mesh.isEnabled?.() !== false
|
|
54
|
+
&& (mesh.getTotalVertices?.() ?? 0) > 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Builds the set of model meshes accepted by picking, including Babylon instances owned by them.
|
|
59
|
+
* @public
|
|
60
|
+
* @static
|
|
61
|
+
* @param {Array<object>} meshes - Meshes from the model AssetContainer.
|
|
62
|
+
* @returns {Set<object>} Accepted model mesh references.
|
|
63
|
+
*/
|
|
64
|
+
static getModelMeshSet(meshes = []) {
|
|
65
|
+
const result = new Set();
|
|
66
|
+
for (const mesh of meshes ?? []) {
|
|
67
|
+
if (!mesh) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
result.add(mesh);
|
|
71
|
+
for (const instance of mesh.instances ?? []) {
|
|
72
|
+
result.add(instance);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Converts ordered Babylon ray hits into the public semantic mesh-id list.
|
|
80
|
+
* @public
|
|
81
|
+
* @static
|
|
82
|
+
* @param {Array<object>} picks - Babylon PickingInfo results.
|
|
83
|
+
* @param {Array<object>} modelMeshes - Meshes belonging to the model container.
|
|
84
|
+
* @returns {string[]} Stable, distance-ordered, deduplicated semantic ids.
|
|
85
|
+
*/
|
|
86
|
+
static collectPickedMeshIds(picks = [], modelMeshes = []) {
|
|
87
|
+
const acceptedMeshes = BabylonJSSelectionController.getModelMeshSet(modelMeshes);
|
|
88
|
+
const ids = [];
|
|
89
|
+
const seen = new Set();
|
|
90
|
+
[...(picks ?? [])]
|
|
91
|
+
.filter((pick) => pick?.hit === true && acceptedMeshes.has(pick.pickedMesh))
|
|
92
|
+
.sort((left, right) => (left.distance ?? Number.POSITIVE_INFINITY) - (right.distance ?? Number.POSITIVE_INFINITY))
|
|
93
|
+
.forEach((pick) => {
|
|
94
|
+
const id = BabylonJSSelectionController.normalizeMeshId(pick.pickedMesh?.id);
|
|
95
|
+
if (id && !seen.has(id)) {
|
|
96
|
+
seen.add(id);
|
|
97
|
+
ids.push(id);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return ids;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a controller with a live model-mesh provider and selection presentation settings.
|
|
105
|
+
* @public
|
|
106
|
+
* @param {function(): Array<object>} getModelMeshes - Returns current model meshes.
|
|
107
|
+
* @param {{selectableColor:object,selectedColor:object,overlayAlpha:number,requestRender?:function():void}} options - Overlay configuration.
|
|
108
|
+
*/
|
|
109
|
+
constructor(getModelMeshes, options) {
|
|
110
|
+
this.#getModelMeshes = getModelMeshes;
|
|
111
|
+
this.#selectableColor = options.selectableColor;
|
|
112
|
+
this.#selectedColor = options.selectedColor;
|
|
113
|
+
this.#overlayAlpha = options.overlayAlpha;
|
|
114
|
+
this.#requestRender = options.requestRender ?? (() => {});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Applies a complete selection snapshot to every matching model mesh.
|
|
119
|
+
* @public
|
|
120
|
+
* @param {{selectableIds?:string[],selectedIds?:string[],selectedItems?:Array<{id:string,highlights?:string[]}>}} state - Canonical selection state.
|
|
121
|
+
* @returns {void}
|
|
122
|
+
*/
|
|
123
|
+
applySelectionState(state = {}) {
|
|
124
|
+
this.#state = state;
|
|
125
|
+
this.refresh();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Reapplies the retained snapshot after model meshes are replaced.
|
|
130
|
+
* Restores removed meshes, gives selected ids priority and requests rendering only on changes.
|
|
131
|
+
* @public
|
|
132
|
+
* @returns {void}
|
|
133
|
+
*/
|
|
134
|
+
refresh() {
|
|
135
|
+
const selectableIds = new Set(this.#state.selectableIds ?? []);
|
|
136
|
+
const selectedIds = new Set(this.#state.selectedIds ?? []);
|
|
137
|
+
for (const item of this.#state.selectedItems ?? []) {
|
|
138
|
+
if (item?.id) {
|
|
139
|
+
selectedIds.add(item.id);
|
|
140
|
+
}
|
|
141
|
+
for (const id of item?.highlights ?? []) {
|
|
142
|
+
selectedIds.add(id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const currentMeshes = new Set(BabylonJSSelectionController.getModelMeshSet(this.#getModelMeshes?.() ?? []));
|
|
147
|
+
let changed = false;
|
|
148
|
+
for (const mesh of this.#originalOverlays.keys()) {
|
|
149
|
+
if (!currentMeshes.has(mesh)) {
|
|
150
|
+
changed = this.#restoreMesh(mesh) || changed;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const mesh of currentMeshes) {
|
|
155
|
+
const id = BabylonJSSelectionController.normalizeMeshId(mesh?.id);
|
|
156
|
+
const color = selectedIds.has(id)
|
|
157
|
+
? this.#selectedColor
|
|
158
|
+
: (selectableIds.has(id) ? this.#selectableColor : null);
|
|
159
|
+
if (color) {
|
|
160
|
+
if (!this.#originalOverlays.has(mesh)) {
|
|
161
|
+
this.#originalOverlays.set(mesh, {
|
|
162
|
+
renderOverlay: mesh.renderOverlay,
|
|
163
|
+
overlayColor: mesh.overlayColor,
|
|
164
|
+
overlayAlpha: mesh.overlayAlpha,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (mesh.renderOverlay !== true || mesh.overlayColor !== color || mesh.overlayAlpha !== this.#overlayAlpha) {
|
|
168
|
+
mesh.renderOverlay = true;
|
|
169
|
+
mesh.overlayColor = color;
|
|
170
|
+
mesh.overlayAlpha = this.#overlayAlpha;
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
changed = this.#restoreMesh(mesh) || changed;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (changed) {
|
|
179
|
+
this.#requestRender();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Restores all meshes changed by this controller and releases their saved overlay properties.
|
|
185
|
+
* @public
|
|
186
|
+
* @returns {void}
|
|
187
|
+
*/
|
|
188
|
+
dispose() {
|
|
189
|
+
let changed = false;
|
|
190
|
+
for (const mesh of [...this.#originalOverlays.keys()]) {
|
|
191
|
+
changed = this.#restoreMesh(mesh) || changed;
|
|
192
|
+
}
|
|
193
|
+
if (changed) {
|
|
194
|
+
this.#requestRender();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Restores a mesh's saved overlay properties and removes its restoration snapshot.
|
|
200
|
+
* @private
|
|
201
|
+
* @param {object} mesh - Mesh previously updated by this controller.
|
|
202
|
+
* @returns {boolean} Whether a saved overlay was restored.
|
|
203
|
+
*/
|
|
204
|
+
#restoreMesh(mesh) {
|
|
205
|
+
const original = this.#originalOverlays.get(mesh);
|
|
206
|
+
if (!original) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
mesh.renderOverlay = original.renderOverlay;
|
|
210
|
+
mesh.overlayColor = original.overlayColor;
|
|
211
|
+
mesh.overlayAlpha = original.overlayAlpha;
|
|
212
|
+
this.#originalOverlays.delete(mesh);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
}
|