brep-io-kernel 1.0.20 → 1.0.22
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 +4 -1
- package/dist-kernel/brep-kernel.js +10858 -9938
- package/package.json +3 -2
- package/src/BREP/Edge.js +2 -0
- package/src/BREP/Face.js +2 -0
- package/src/BREP/SolidMethods/visualize.js +372 -365
- package/src/BREP/Vertex.js +2 -17
- package/src/PartHistory.js +4 -25
- package/src/SketchSolver2D.js +3 -0
- package/src/UI/AccordionWidget.js +1 -1
- package/src/UI/EnvMonacoEditor.js +0 -3
- package/src/UI/HistoryWidget.js +12 -4
- package/src/UI/SceneListing.js +45 -7
- package/src/UI/SelectionFilter.js +903 -438
- package/src/UI/SelectionState.js +464 -0
- package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +41 -1
- package/src/UI/assembly/AssemblyConstraintsWidget.js +21 -3
- package/src/UI/assembly/constraintSelectionUtils.js +3 -182
- package/src/UI/{assembly/constraintFaceUtils.js → faceUtils.js} +30 -5
- package/src/UI/featureDialogs.js +154 -69
- package/src/UI/history/HistoryCollectionWidget.js +65 -0
- package/src/UI/pmi/AnnotationCollectionWidget.js +1 -0
- package/src/UI/pmi/BaseAnnotation.js +37 -0
- package/src/UI/pmi/LabelOverlay.js +32 -0
- package/src/UI/pmi/PMIMode.js +27 -0
- package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +5 -0
- package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +5 -0
- package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +57 -0
- package/src/UI/pmi/dimensions/LeaderAnnotation.js +5 -0
- package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +22 -16
- package/src/UI/pmi/dimensions/NoteAnnotation.js +9 -0
- package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +81 -16
- package/src/UI/toolbarButtons/orientToFaceButton.js +3 -36
- package/src/UI/toolbarButtons/registerDefaultButtons.js +2 -0
- package/src/UI/toolbarButtons/selectionStateButton.js +206 -0
- package/src/UI/viewer.js +34 -13
- package/src/assemblyConstraints/AssemblyConstraintHistory.js +18 -42
- package/src/assemblyConstraints/constraints/AngleConstraint.js +1 -0
- package/src/assemblyConstraints/constraints/DistanceConstraint.js +1 -0
- package/src/features/selectionUtils.js +21 -5
- package/src/features/sketch/SketchFeature.js +2 -2
- package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +3 -2
- package/src/utils/selectionResolver.js +258 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { CADmaterials } from "./CADmaterials.js";
|
|
2
|
+
|
|
3
|
+
const debugMode = false;
|
|
4
|
+
|
|
5
|
+
export class SelectionState {
|
|
6
|
+
static hoverColor = '#fbff00';
|
|
7
|
+
|
|
8
|
+
static attach(obj, { deep = false } = {}) {
|
|
9
|
+
if (!obj || typeof obj !== 'object') return false;
|
|
10
|
+
let changed = false;
|
|
11
|
+
|
|
12
|
+
const attachOne = (target) => {
|
|
13
|
+
if (!target || typeof target !== 'object') return;
|
|
14
|
+
const state = SelectionState._ensureState(target);
|
|
15
|
+
if (!state) return;
|
|
16
|
+
const wasAttached = !!state._attached;
|
|
17
|
+
|
|
18
|
+
const selectedDesc = Object.getOwnPropertyDescriptor(target, 'selected');
|
|
19
|
+
if (!selectedDesc || !selectedDesc.get || !selectedDesc.get.__selectionState) {
|
|
20
|
+
const getter = function () { return state.selected; };
|
|
21
|
+
getter.__selectionState = true;
|
|
22
|
+
const setter = function (v) {
|
|
23
|
+
const nv = !!v;
|
|
24
|
+
if (state.selected === nv) return;
|
|
25
|
+
if (debugMode) {
|
|
26
|
+
try {
|
|
27
|
+
console.log('[SelectionState] selected changed', {
|
|
28
|
+
name: target?.name,
|
|
29
|
+
type: target?.type,
|
|
30
|
+
prev: state.selected,
|
|
31
|
+
next: nv,
|
|
32
|
+
target,
|
|
33
|
+
});
|
|
34
|
+
console.trace('[SelectionState] selected stack');
|
|
35
|
+
} catch { }
|
|
36
|
+
}
|
|
37
|
+
state.selected = nv;
|
|
38
|
+
SelectionState.apply(target);
|
|
39
|
+
};
|
|
40
|
+
setter.__selectionState = true;
|
|
41
|
+
Object.defineProperty(target, 'selected', {
|
|
42
|
+
get: getter,
|
|
43
|
+
set: setter,
|
|
44
|
+
configurable: true,
|
|
45
|
+
enumerable: true,
|
|
46
|
+
});
|
|
47
|
+
changed = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const hoveredDesc = Object.getOwnPropertyDescriptor(target, 'hovered');
|
|
51
|
+
if (!hoveredDesc || !hoveredDesc.get || !hoveredDesc.get.__selectionState) {
|
|
52
|
+
const getter = function () { return state.hovered; };
|
|
53
|
+
getter.__selectionState = true;
|
|
54
|
+
const setter = function (v) {
|
|
55
|
+
const nv = !!v;
|
|
56
|
+
if (state.hovered === nv) return;
|
|
57
|
+
if (debugMode) {
|
|
58
|
+
try {
|
|
59
|
+
console.log('[SelectionState] hovered changed', {
|
|
60
|
+
name: target?.name,
|
|
61
|
+
type: target?.type,
|
|
62
|
+
prev: state.hovered,
|
|
63
|
+
next: nv,
|
|
64
|
+
target,
|
|
65
|
+
});
|
|
66
|
+
console.trace('[SelectionState] hovered stack');
|
|
67
|
+
} catch { }
|
|
68
|
+
}
|
|
69
|
+
state.hovered = nv;
|
|
70
|
+
SelectionState.apply(target);
|
|
71
|
+
};
|
|
72
|
+
setter.__selectionState = true;
|
|
73
|
+
Object.defineProperty(target, 'hovered', {
|
|
74
|
+
get: getter,
|
|
75
|
+
set: setter,
|
|
76
|
+
configurable: true,
|
|
77
|
+
enumerable: true,
|
|
78
|
+
});
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
SelectionState._seedBaseMaterials(target);
|
|
83
|
+
if (!wasAttached) state._attached = true;
|
|
84
|
+
|
|
85
|
+
if (!wasAttached && (state.selected || state.hovered)) {
|
|
86
|
+
SelectionState.apply(target);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (!deep) {
|
|
91
|
+
attachOne(obj);
|
|
92
|
+
return changed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const stack = [obj];
|
|
96
|
+
while (stack.length) {
|
|
97
|
+
const current = stack.pop();
|
|
98
|
+
attachOne(current);
|
|
99
|
+
const kids = Array.isArray(current?.children) ? current.children : [];
|
|
100
|
+
for (const child of kids) {
|
|
101
|
+
if (child) stack.push(child);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return changed;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static setHoverColor(color) {
|
|
108
|
+
if (!color) return;
|
|
109
|
+
try { SelectionState.hoverColor = String(color); } catch { }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static apply(obj, { force = false } = {}) {
|
|
113
|
+
if (!obj || typeof obj !== 'object') return;
|
|
114
|
+
SelectionState.attach(obj);
|
|
115
|
+
const state = SelectionState._getState(obj);
|
|
116
|
+
if (!state) return;
|
|
117
|
+
|
|
118
|
+
const type = obj.type || '';
|
|
119
|
+
if (type === 'SOLID' || type === 'COMPONENT') {
|
|
120
|
+
SelectionState._applyToSolid(obj, state, { force });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
SelectionState._applyForObject(obj, {
|
|
125
|
+
selected: state.selected,
|
|
126
|
+
hovered: state.hovered,
|
|
127
|
+
force,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static setBaseMaterial(obj, material, { force = true } = {}) {
|
|
132
|
+
if (!obj || !material) return;
|
|
133
|
+
SelectionState.attach(obj);
|
|
134
|
+
const targets = SelectionState._getDrawableTargets(obj);
|
|
135
|
+
for (const target of targets) {
|
|
136
|
+
if (!target) continue;
|
|
137
|
+
target.userData = target.userData || {};
|
|
138
|
+
target.userData.__baseMaterial = material;
|
|
139
|
+
}
|
|
140
|
+
SelectionState.apply(obj, { force: !!force });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static getBaseMaterial(obj, rootType = obj?.type) {
|
|
144
|
+
if (!obj) return null;
|
|
145
|
+
const ud = obj.userData || {};
|
|
146
|
+
if (ud.__baseMaterial) return ud.__baseMaterial;
|
|
147
|
+
if (ud.__defaultMaterial) return ud.__defaultMaterial;
|
|
148
|
+
|
|
149
|
+
const type = rootType || obj.type || '';
|
|
150
|
+
if (type === 'FACE') return CADmaterials.FACE?.BASE ?? obj.material;
|
|
151
|
+
if (type === 'PLANE') return CADmaterials.PLANE?.BASE ?? CADmaterials.FACE?.BASE ?? obj.material;
|
|
152
|
+
if (type === 'EDGE') return CADmaterials.EDGE?.BASE ?? obj.material;
|
|
153
|
+
if (type === 'VERTEX') return CADmaterials.VERTEX?.BASE ?? obj.material;
|
|
154
|
+
return obj.material ?? null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static getHoverTargets(target) {
|
|
158
|
+
if (!target) return [];
|
|
159
|
+
const type = target.type || '';
|
|
160
|
+
if (type === 'SOLID' || type === 'COMPONENT') {
|
|
161
|
+
const out = [];
|
|
162
|
+
const kids = Array.isArray(target.children) ? target.children : [];
|
|
163
|
+
for (const child of kids) {
|
|
164
|
+
if (!child) continue;
|
|
165
|
+
if (child.type === 'SOLID' || child.type === 'COMPONENT') {
|
|
166
|
+
const nested = Array.isArray(child.children) ? child.children : [];
|
|
167
|
+
for (const nestedChild of nested) {
|
|
168
|
+
if (nestedChild && (nestedChild.type === 'FACE' || nestedChild.type === 'EDGE')) out.push(nestedChild);
|
|
169
|
+
}
|
|
170
|
+
} else if (child.type === 'FACE' || child.type === 'EDGE') {
|
|
171
|
+
out.push(child);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
return [target];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static _ensureState(obj) {
|
|
180
|
+
if (!obj || typeof obj !== 'object') return null;
|
|
181
|
+
let state = obj.__selectionState;
|
|
182
|
+
if (!state) {
|
|
183
|
+
const existingSelected = !!obj.selected;
|
|
184
|
+
const existingHovered = !!obj.hovered;
|
|
185
|
+
state = { selected: existingSelected, hovered: existingHovered };
|
|
186
|
+
try {
|
|
187
|
+
Object.defineProperty(obj, '__selectionState', {
|
|
188
|
+
value: state,
|
|
189
|
+
writable: true,
|
|
190
|
+
configurable: true,
|
|
191
|
+
enumerable: false,
|
|
192
|
+
});
|
|
193
|
+
} catch {
|
|
194
|
+
obj.__selectionState = state;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return state;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static _getState(obj) {
|
|
201
|
+
return obj?.__selectionState || null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static _seedBaseMaterials(obj) {
|
|
205
|
+
const targets = SelectionState._getDrawableTargets(obj);
|
|
206
|
+
for (const target of targets) {
|
|
207
|
+
if (!target) continue;
|
|
208
|
+
target.userData = target.userData || {};
|
|
209
|
+
if (!target.userData.__baseMaterial) {
|
|
210
|
+
if (target.userData.__defaultMaterial) {
|
|
211
|
+
target.userData.__baseMaterial = target.userData.__defaultMaterial;
|
|
212
|
+
} else if (target.material) {
|
|
213
|
+
target.userData.__baseMaterial = target.material;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static _getDrawableTargets(obj) {
|
|
220
|
+
if (!obj) return [];
|
|
221
|
+
if (obj.type === 'VERTEX') {
|
|
222
|
+
const targets = [];
|
|
223
|
+
if (obj._point && obj._point.material) targets.push(obj._point);
|
|
224
|
+
const kids = Array.isArray(obj.children) ? obj.children : [];
|
|
225
|
+
for (const child of kids) {
|
|
226
|
+
if (child && child.material && !targets.includes(child)) targets.push(child);
|
|
227
|
+
}
|
|
228
|
+
return targets.length ? targets : [obj];
|
|
229
|
+
}
|
|
230
|
+
if (obj.material) return [obj];
|
|
231
|
+
return [obj];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static _applyToSolid(obj, state, { force = false } = {}) {
|
|
235
|
+
const children = Array.isArray(obj.children) ? obj.children : [];
|
|
236
|
+
if (state.hovered) {
|
|
237
|
+
for (const child of children) {
|
|
238
|
+
if (!child) continue;
|
|
239
|
+
if (child.type === 'SOLID' || child.type === 'COMPONENT') {
|
|
240
|
+
const nested = Array.isArray(child.children) ? child.children : [];
|
|
241
|
+
for (const nestedChild of nested) {
|
|
242
|
+
if (nestedChild && (nestedChild.type === 'FACE' || nestedChild.type === 'EDGE')) {
|
|
243
|
+
SelectionState._applyForObject(nestedChild, { hovered: true, force });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} else if (child.type === 'FACE' || child.type === 'EDGE') {
|
|
247
|
+
SelectionState._applyForObject(child, { hovered: true, force });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (state.selected) {
|
|
254
|
+
for (const child of children) {
|
|
255
|
+
if (!child) continue;
|
|
256
|
+
if (child.type === 'FACE' || child.type === 'EDGE' || child.type === 'PLANE') {
|
|
257
|
+
SelectionState._applyForObject(child, { selected: true, force });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for (const child of children) {
|
|
264
|
+
if (!child) continue;
|
|
265
|
+
if (child.type === 'FACE' || child.type === 'EDGE' || child.type === 'PLANE') {
|
|
266
|
+
SelectionState.attach(child);
|
|
267
|
+
const childState = SelectionState._getState(child);
|
|
268
|
+
SelectionState._applyForObject(child, {
|
|
269
|
+
selected: !!childState?.selected,
|
|
270
|
+
hovered: !!childState?.hovered,
|
|
271
|
+
force,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
static _applyForObject(obj, { selected = false, hovered = false, force = false } = {}) {
|
|
278
|
+
if (!obj) return;
|
|
279
|
+
SelectionState._seedBaseMaterials(obj);
|
|
280
|
+
const rootType = obj.type || '';
|
|
281
|
+
const targets = SelectionState._getDrawableTargets(obj);
|
|
282
|
+
for (const target of targets) {
|
|
283
|
+
if (!target) continue;
|
|
284
|
+
if (hovered) {
|
|
285
|
+
SelectionState._applyHover(target, rootType, { force });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
SelectionState._clearHover(target);
|
|
289
|
+
if (selected) {
|
|
290
|
+
SelectionState._applySelected(target, rootType);
|
|
291
|
+
} else {
|
|
292
|
+
SelectionState._applyBase(target, rootType);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static _applyBase(target, rootType) {
|
|
298
|
+
const base = SelectionState.getBaseMaterial(target, rootType);
|
|
299
|
+
try {
|
|
300
|
+
const ud = target.userData || {};
|
|
301
|
+
if (ud.__selectedMat && ud.__selectedMat !== base) {
|
|
302
|
+
if (Array.isArray(ud.__selectedMat)) {
|
|
303
|
+
for (const m of ud.__selectedMat) {
|
|
304
|
+
try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
|
|
305
|
+
}
|
|
306
|
+
} else if (typeof ud.__selectedMat.dispose === 'function') {
|
|
307
|
+
ud.__selectedMat.dispose();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
try { delete ud.__selectedMat; } catch { }
|
|
311
|
+
try { delete ud.__selectedMatBase; } catch { }
|
|
312
|
+
try { delete ud.__selectedColor; } catch { }
|
|
313
|
+
} catch { }
|
|
314
|
+
if (base) SelectionState._assignMaterial(target, base);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static _applySelected(target, rootType) {
|
|
318
|
+
const base = SelectionState.getBaseMaterial(target, rootType);
|
|
319
|
+
let mat = base;
|
|
320
|
+
if (rootType === 'FACE') {
|
|
321
|
+
mat = CADmaterials.FACE?.SELECTED ?? base;
|
|
322
|
+
} else if (rootType === 'PLANE') {
|
|
323
|
+
mat = CADmaterials.PLANE?.SELECTED ?? CADmaterials.FACE?.SELECTED ?? base;
|
|
324
|
+
} else if (rootType === 'EDGE') {
|
|
325
|
+
const selMat = CADmaterials.EDGE?.SELECTED ?? CADmaterials.EDGE?.BASE ?? base;
|
|
326
|
+
const selColor = SelectionState._resolveColor(selMat?.color ?? selMat?.color?.getHexString?.());
|
|
327
|
+
if (selColor && base) {
|
|
328
|
+
const ud = target.userData || (target.userData = {});
|
|
329
|
+
const prevMat = ud.__selectedMat;
|
|
330
|
+
const sameBase = ud.__selectedMatBase === base;
|
|
331
|
+
const sameColor = ud.__selectedColor === selColor;
|
|
332
|
+
if (prevMat && sameBase && sameColor) {
|
|
333
|
+
mat = prevMat;
|
|
334
|
+
} else {
|
|
335
|
+
if (prevMat && prevMat !== base) {
|
|
336
|
+
try {
|
|
337
|
+
if (Array.isArray(prevMat)) {
|
|
338
|
+
for (const m of prevMat) {
|
|
339
|
+
try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
|
|
340
|
+
}
|
|
341
|
+
} else if (typeof prevMat.dispose === 'function') {
|
|
342
|
+
prevMat.dispose();
|
|
343
|
+
}
|
|
344
|
+
} catch { }
|
|
345
|
+
}
|
|
346
|
+
mat = SelectionState._cloneMaterialWithColor(base, selColor);
|
|
347
|
+
ud.__selectedMat = mat;
|
|
348
|
+
ud.__selectedMatBase = base;
|
|
349
|
+
ud.__selectedColor = selColor;
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
mat = selMat ?? base;
|
|
353
|
+
}
|
|
354
|
+
} else if (rootType === 'VERTEX') {
|
|
355
|
+
mat = CADmaterials.VERTEX?.SELECTED ?? base;
|
|
356
|
+
}
|
|
357
|
+
if (mat) SelectionState._assignMaterial(target, mat);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
static _applyHover(target, rootType, { force = false } = {}) {
|
|
361
|
+
const ud = target.userData || (target.userData = {});
|
|
362
|
+
if (ud.__hoverMatApplied) {
|
|
363
|
+
if (!force) return;
|
|
364
|
+
SelectionState._clearHover(target);
|
|
365
|
+
}
|
|
366
|
+
const base = SelectionState.getBaseMaterial(target, rootType);
|
|
367
|
+
if (!base) return;
|
|
368
|
+
const hoverColor = SelectionState.hoverColor;
|
|
369
|
+
const hoverMat = SelectionState._cloneMaterialWithColor(base, hoverColor);
|
|
370
|
+
ud.__hoverOrigMat = base;
|
|
371
|
+
ud.__hoverMatApplied = true;
|
|
372
|
+
ud.__hoverMat = hoverMat;
|
|
373
|
+
if (hoverMat) SelectionState._assignMaterial(target, hoverMat);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static _clearHover(target) {
|
|
377
|
+
const ud = target.userData || {};
|
|
378
|
+
if (!ud.__hoverMatApplied) return;
|
|
379
|
+
try {
|
|
380
|
+
if (ud.__hoverMat && ud.__hoverMat !== ud.__hoverOrigMat) {
|
|
381
|
+
if (Array.isArray(ud.__hoverMat)) {
|
|
382
|
+
for (const m of ud.__hoverMat) {
|
|
383
|
+
try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
|
|
384
|
+
}
|
|
385
|
+
} else if (typeof ud.__hoverMat.dispose === 'function') {
|
|
386
|
+
ud.__hoverMat.dispose();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch { }
|
|
390
|
+
try { delete ud.__hoverMatApplied; } catch { }
|
|
391
|
+
try { delete ud.__hoverOrigMat; } catch { }
|
|
392
|
+
try { delete ud.__hoverMat; } catch { }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
static _assignMaterial(target, material) {
|
|
396
|
+
try {
|
|
397
|
+
const prev = target?.material;
|
|
398
|
+
if (prev !== material && debugMode) {
|
|
399
|
+
console.log('[SelectionState] material changed', {
|
|
400
|
+
name: target?.name,
|
|
401
|
+
type: target?.type,
|
|
402
|
+
prev,
|
|
403
|
+
next: material,
|
|
404
|
+
target,
|
|
405
|
+
});
|
|
406
|
+
console.trace('[SelectionState] material stack');
|
|
407
|
+
}
|
|
408
|
+
} catch { }
|
|
409
|
+
try { target.material = material; } catch { }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
static _cloneMaterialWithColor(material, color) {
|
|
413
|
+
if (!material) return material;
|
|
414
|
+
if (Array.isArray(material)) {
|
|
415
|
+
return material.map((m) => SelectionState._cloneMaterialWithColor(m, color));
|
|
416
|
+
}
|
|
417
|
+
let clone = material;
|
|
418
|
+
try {
|
|
419
|
+
if (material && typeof material.clone === 'function') clone = material.clone();
|
|
420
|
+
} catch {
|
|
421
|
+
clone = material;
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
if (clone && clone.color && typeof clone.color.set === 'function' && color) {
|
|
425
|
+
clone.color.set(color);
|
|
426
|
+
}
|
|
427
|
+
} catch { }
|
|
428
|
+
try {
|
|
429
|
+
if (material && clone && material.resolution && clone.resolution && typeof clone.resolution.copy === 'function') {
|
|
430
|
+
clone.resolution.copy(material.resolution);
|
|
431
|
+
}
|
|
432
|
+
} catch { }
|
|
433
|
+
try {
|
|
434
|
+
if (material && clone && typeof material.dashed !== 'undefined' && typeof clone.dashed !== 'undefined') {
|
|
435
|
+
clone.dashed = material.dashed;
|
|
436
|
+
}
|
|
437
|
+
if (material && clone && typeof material.dashSize !== 'undefined' && typeof clone.dashSize !== 'undefined') {
|
|
438
|
+
clone.dashSize = material.dashSize;
|
|
439
|
+
}
|
|
440
|
+
if (material && clone && typeof material.gapSize !== 'undefined' && typeof clone.gapSize !== 'undefined') {
|
|
441
|
+
clone.gapSize = material.gapSize;
|
|
442
|
+
}
|
|
443
|
+
if (material && clone && typeof material.dashScale !== 'undefined' && typeof clone.dashScale !== 'undefined') {
|
|
444
|
+
clone.dashScale = material.dashScale;
|
|
445
|
+
}
|
|
446
|
+
} catch { }
|
|
447
|
+
return clone;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
static _resolveColor(value) {
|
|
451
|
+
if (!value) return null;
|
|
452
|
+
if (value?.isColor) return value;
|
|
453
|
+
if (typeof value === 'string') {
|
|
454
|
+
const v = value.trim();
|
|
455
|
+
if (/^[0-9a-fA-F]{6}$/.test(v)) return `#${v}`;
|
|
456
|
+
return v;
|
|
457
|
+
}
|
|
458
|
+
if (typeof value === 'number') return value;
|
|
459
|
+
if (typeof value?.getHexString === 'function') {
|
|
460
|
+
try { return `#${value.getHexString()}`; } catch { }
|
|
461
|
+
}
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
@@ -164,6 +164,8 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
164
164
|
|
|
165
165
|
this.partHistory = partHistory || null;
|
|
166
166
|
this.viewer = viewer || null;
|
|
167
|
+
this._pendingFocusField = null;
|
|
168
|
+
this._autoFocusOnExpand = true;
|
|
167
169
|
this._highlightCallback = typeof onHighlightRequest === 'function' ? onHighlightRequest : null;
|
|
168
170
|
this._clearHighlightCallback = typeof onClearHighlight === 'function' ? onClearHighlight : null;
|
|
169
171
|
this._beforeConstraintChangeHandler = callBeforeChange;
|
|
@@ -174,11 +176,15 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
174
176
|
this.partHistory = partHistory || null;
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
focusEntryById(targetId, { behavior = 'smooth' } = {}) {
|
|
179
|
+
focusEntryById(targetId, { behavior = 'smooth', focusField = null } = {}) {
|
|
178
180
|
if (targetId == null) return;
|
|
179
181
|
const id = String(targetId);
|
|
180
182
|
if (!id) return;
|
|
181
183
|
this._expandedId = id;
|
|
184
|
+
if (this._autoFocusOnExpand) {
|
|
185
|
+
this._pendingFocusEntryId = id;
|
|
186
|
+
}
|
|
187
|
+
this._pendingFocusField = focusField != null ? String(focusField) : null;
|
|
182
188
|
this.render();
|
|
183
189
|
const root = this._shadow || null;
|
|
184
190
|
if (!root) return;
|
|
@@ -189,6 +195,40 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
|
|
198
|
+
_applyPendingFocus() {
|
|
199
|
+
if (!this._autoFocusOnExpand) return;
|
|
200
|
+
const targetId = this._pendingFocusEntryId;
|
|
201
|
+
if (!targetId) return;
|
|
202
|
+
if (!this._expandedId || String(this._expandedId) !== String(targetId)) {
|
|
203
|
+
this._pendingFocusEntryId = null;
|
|
204
|
+
this._pendingFocusField = null;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const form = this.getFormForEntry(targetId);
|
|
208
|
+
if (!form) {
|
|
209
|
+
this._pendingFocusEntryId = null;
|
|
210
|
+
this._pendingFocusField = null;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const focusField = this._pendingFocusField;
|
|
214
|
+
const focus = () => {
|
|
215
|
+
let handled = false;
|
|
216
|
+
if (focusField && typeof form.activateField === 'function') {
|
|
217
|
+
try { handled = form.activateField(focusField) === true; } catch { /* ignore */ }
|
|
218
|
+
}
|
|
219
|
+
if (!handled) {
|
|
220
|
+
try {
|
|
221
|
+
if (typeof form.focusFirstField === 'function') form.focusFirstField();
|
|
222
|
+
else if (typeof form.activateFirstReferenceSelection === 'function') form.activateFirstReferenceSelection();
|
|
223
|
+
} catch { /* ignore */ }
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => focus());
|
|
227
|
+
else setTimeout(focus, 0);
|
|
228
|
+
this._pendingFocusEntryId = null;
|
|
229
|
+
this._pendingFocusField = null;
|
|
230
|
+
}
|
|
231
|
+
|
|
192
232
|
#decorateEntry(context = {}) {
|
|
193
233
|
const { entry = null, elements = {}, index = 0 } = context;
|
|
194
234
|
if (!elements || !elements.item) return;
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
computeFaceOrigin,
|
|
8
8
|
computeFaceNormal,
|
|
9
9
|
estimateArrowLength,
|
|
10
|
-
} from '
|
|
10
|
+
} from '../faceUtils.js';
|
|
11
11
|
import { applyHighlightMaterial, restoreHighlightRecords } from './constraintHighlightUtils.js';
|
|
12
12
|
import { extractWorldPoint } from './constraintPointUtils.js';
|
|
13
13
|
import { constraintStatusInfo } from './constraintStatusUtils.js';
|
|
@@ -209,6 +209,10 @@ export class AssemblyConstraintsWidget {
|
|
|
209
209
|
this._scheduleSync();
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
collapseExpandedDialogs() {
|
|
213
|
+
try { this._constraintList?.collapseExpandedEntries?.({ clearOpenState: true }); } catch { /* ignore */ }
|
|
214
|
+
}
|
|
215
|
+
|
|
212
216
|
#handleHistoryChange() {
|
|
213
217
|
this._scheduleSync();
|
|
214
218
|
if (this._ignoreFullSolveChangeCount > 0) {
|
|
@@ -1247,6 +1251,17 @@ export class AssemblyConstraintsWidget {
|
|
|
1247
1251
|
return null;
|
|
1248
1252
|
}
|
|
1249
1253
|
|
|
1254
|
+
#resolveFocusFieldForConstraint(entry) {
|
|
1255
|
+
const constraintClass = this._resolveConstraintClass(entry);
|
|
1256
|
+
const field = constraintClass?.focusField;
|
|
1257
|
+
if (field) return field;
|
|
1258
|
+
const type = constraintClass?.constraintType || entry?.type || entry?.inputParams?.type;
|
|
1259
|
+
const normalized = typeof type === 'string' ? type.toLowerCase() : '';
|
|
1260
|
+
if (normalized === 'distance') return 'distance';
|
|
1261
|
+
if (normalized === 'angle') return 'angle';
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1250
1265
|
#handleLabelClick(idx, _ann, ev) {
|
|
1251
1266
|
if (idx == null) return;
|
|
1252
1267
|
const id = String(idx);
|
|
@@ -1255,12 +1270,14 @@ export class AssemblyConstraintsWidget {
|
|
|
1255
1270
|
try { ev.preventDefault(); } catch { }
|
|
1256
1271
|
try { ev.stopPropagation(); } catch { }
|
|
1257
1272
|
}
|
|
1273
|
+
const entries = this.history?.list?.() || [];
|
|
1274
|
+
const targetEntry = entries.find((entry) => resolveConstraintId(entry) === id) || null;
|
|
1275
|
+
|
|
1258
1276
|
let changed = false;
|
|
1259
1277
|
if (typeof this.history?.setExclusiveOpen === 'function') {
|
|
1260
1278
|
changed = this.history.setExclusiveOpen(id);
|
|
1261
1279
|
}
|
|
1262
1280
|
if (!changed) {
|
|
1263
|
-
const entries = this.history?.list?.() || [];
|
|
1264
1281
|
for (const entry of entries) {
|
|
1265
1282
|
const entryId = resolveConstraintId(entry);
|
|
1266
1283
|
const shouldOpen = entryId === id;
|
|
@@ -1272,7 +1289,8 @@ export class AssemblyConstraintsWidget {
|
|
|
1272
1289
|
this.history?.setOpenState?.(id, true);
|
|
1273
1290
|
}
|
|
1274
1291
|
|
|
1275
|
-
this
|
|
1292
|
+
const focusField = this.#resolveFocusFieldForConstraint(targetEntry);
|
|
1293
|
+
this._constraintList?.focusEntryById?.(id, { focusField });
|
|
1276
1294
|
}
|
|
1277
1295
|
|
|
1278
1296
|
_createNormalArrow(object, color, label) {
|