brep-io-kernel 1.0.21 → 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 +11065 -10512
- 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 +3 -0
- package/src/UI/SceneListing.js +45 -7
- package/src/UI/SelectionFilter.js +469 -442
- package/src/UI/SelectionState.js +464 -0
- package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +40 -1
- package/src/UI/assembly/AssemblyConstraintsWidget.js +17 -3
- package/src/UI/assembly/constraintSelectionUtils.js +3 -182
- package/src/UI/{assembly/constraintFaceUtils.js → faceUtils.js} +30 -5
- package/src/UI/featureDialogs.js +99 -69
- package/src/UI/pmi/LabelOverlay.js +32 -0
- package/src/UI/pmi/PMIMode.js +23 -0
- package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +7 -1
- 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 +16 -16
- 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,7 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
164
164
|
|
|
165
165
|
this.partHistory = partHistory || null;
|
|
166
166
|
this.viewer = viewer || null;
|
|
167
|
+
this._pendingFocusField = null;
|
|
167
168
|
this._autoFocusOnExpand = true;
|
|
168
169
|
this._highlightCallback = typeof onHighlightRequest === 'function' ? onHighlightRequest : null;
|
|
169
170
|
this._clearHighlightCallback = typeof onClearHighlight === 'function' ? onClearHighlight : null;
|
|
@@ -175,11 +176,15 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
175
176
|
this.partHistory = partHistory || null;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
focusEntryById(targetId, { behavior = 'smooth' } = {}) {
|
|
179
|
+
focusEntryById(targetId, { behavior = 'smooth', focusField = null } = {}) {
|
|
179
180
|
if (targetId == null) return;
|
|
180
181
|
const id = String(targetId);
|
|
181
182
|
if (!id) return;
|
|
182
183
|
this._expandedId = id;
|
|
184
|
+
if (this._autoFocusOnExpand) {
|
|
185
|
+
this._pendingFocusEntryId = id;
|
|
186
|
+
}
|
|
187
|
+
this._pendingFocusField = focusField != null ? String(focusField) : null;
|
|
183
188
|
this.render();
|
|
184
189
|
const root = this._shadow || null;
|
|
185
190
|
if (!root) return;
|
|
@@ -190,6 +195,40 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
|
|
|
190
195
|
}
|
|
191
196
|
}
|
|
192
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
|
+
|
|
193
232
|
#decorateEntry(context = {}) {
|
|
194
233
|
const { entry = null, elements = {}, index = 0 } = context;
|
|
195
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';
|
|
@@ -1251,6 +1251,17 @@ export class AssemblyConstraintsWidget {
|
|
|
1251
1251
|
return null;
|
|
1252
1252
|
}
|
|
1253
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
|
+
|
|
1254
1265
|
#handleLabelClick(idx, _ann, ev) {
|
|
1255
1266
|
if (idx == null) return;
|
|
1256
1267
|
const id = String(idx);
|
|
@@ -1259,12 +1270,14 @@ export class AssemblyConstraintsWidget {
|
|
|
1259
1270
|
try { ev.preventDefault(); } catch { }
|
|
1260
1271
|
try { ev.stopPropagation(); } catch { }
|
|
1261
1272
|
}
|
|
1273
|
+
const entries = this.history?.list?.() || [];
|
|
1274
|
+
const targetEntry = entries.find((entry) => resolveConstraintId(entry) === id) || null;
|
|
1275
|
+
|
|
1262
1276
|
let changed = false;
|
|
1263
1277
|
if (typeof this.history?.setExclusiveOpen === 'function') {
|
|
1264
1278
|
changed = this.history.setExclusiveOpen(id);
|
|
1265
1279
|
}
|
|
1266
1280
|
if (!changed) {
|
|
1267
|
-
const entries = this.history?.list?.() || [];
|
|
1268
1281
|
for (const entry of entries) {
|
|
1269
1282
|
const entryId = resolveConstraintId(entry);
|
|
1270
1283
|
const shouldOpen = entryId === id;
|
|
@@ -1276,7 +1289,8 @@ export class AssemblyConstraintsWidget {
|
|
|
1276
1289
|
this.history?.setOpenState?.(id, true);
|
|
1277
1290
|
}
|
|
1278
1291
|
|
|
1279
|
-
this
|
|
1292
|
+
const focusField = this.#resolveFocusFieldForConstraint(targetEntry);
|
|
1293
|
+
this._constraintList?.focusEntryById?.(id, { focusField });
|
|
1280
1294
|
}
|
|
1281
1295
|
|
|
1282
1296
|
_createNormalArrow(object, color, label) {
|