brep-io-kernel 1.0.17 → 1.0.19
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-kernel/brep-kernel.js +13547 -12034
- package/package.json +1 -1
- package/src/FeatureRegistry.js +3 -0
- package/src/PartHistory.js +141 -0
- package/src/UI/CADmaterials.js +2 -2
- package/src/UI/MainToolbar.js +39 -2
- package/src/UI/SelectionFilter.js +438 -0
- package/src/UI/featureDialogWidgets/booleanOperationField.js +33 -52
- package/src/UI/featureDialogWidgets/referenceSelectionField.js +10 -0
- package/src/UI/featureDialogs.js +841 -8
- package/src/UI/history/HistoryCollectionWidget.js +20 -1
- package/src/UI/pmi/AnnotationRegistry.js +3 -0
- package/src/UI/toolbarButtons/orientToFaceButton.js +3 -0
- package/src/UI/toolbarButtons/registerDefaultButtons.js +0 -6
- package/src/UI/toolbarButtons/registerSelectionButtons.js +68 -0
- package/src/UI/viewer.js +22 -4
- package/src/assemblyConstraints/AssemblyConstraintRegistry.js +3 -0
- package/src/features/boolean/BooleanFeature.js +15 -0
- package/src/features/chamfer/ChamferFeature.js +12 -0
- package/src/features/extrude/ExtrudeFeature.js +11 -0
- package/src/features/fillet/FilletFeature.js +12 -0
- package/src/features/hole/HoleFeature.js +15 -0
- package/src/features/loft/LoftFeature.js +17 -0
- package/src/features/mirror/MirrorFeature.js +14 -0
- package/src/features/patternLinear/PatternLinearFeature.js +9 -0
- package/src/features/patternRadial/PatternRadialFeature.js +13 -0
- package/src/features/plane/PlaneFeature.js +10 -0
- package/src/features/revolve/RevolveFeature.js +15 -0
- package/src/features/sketch/SketchFeature.js +11 -0
- package/src/features/sweep/SweepFeature.js +17 -0
- package/src/features/transform/TransformFeature.js +12 -0
- package/src/features/tube/TubeFeature.js +12 -0
package/package.json
CHANGED
package/src/FeatureRegistry.js
CHANGED
|
@@ -134,6 +134,9 @@ export class FeatureRegistry {
|
|
|
134
134
|
if (!FeatureClass.longName) {
|
|
135
135
|
FeatureClass.longName = FeatureClass.featureName || FeatureClass.name || FeatureClass.shortName || 'Feature';
|
|
136
136
|
}
|
|
137
|
+
if (typeof FeatureClass.showContexButton !== 'function') {
|
|
138
|
+
FeatureClass.showContexButton = () => false;
|
|
139
|
+
}
|
|
137
140
|
this.features.push(FeatureClass);
|
|
138
141
|
}
|
|
139
142
|
|
package/src/PartHistory.js
CHANGED
|
@@ -341,6 +341,7 @@ export class PartHistory {
|
|
|
341
341
|
if (JSON.stringify(feature.inputParams) !== feature.lastRunInputParams) feature.dirty = true;
|
|
342
342
|
|
|
343
343
|
instance.inputParams = await this.sanitizeInputParams(FeatureClass.inputParamsSchema, feature.inputParams);
|
|
344
|
+
try { this._captureReferencePreviewSnapshots(feature, FeatureClass.inputParamsSchema, instance.inputParams, instance.persistentData); } catch { }
|
|
344
345
|
// check the timestamps of any objects referenced by reference_selection inputs; if any are newer than the feature timestamp, mark dirty
|
|
345
346
|
for (const key in FeatureClass.inputParamsSchema) {
|
|
346
347
|
if (Object.prototype.hasOwnProperty.call(FeatureClass.inputParamsSchema, key)) {
|
|
@@ -460,6 +461,146 @@ export class PartHistory {
|
|
|
460
461
|
return this;
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
_captureReferencePreviewSnapshots(feature, schema, resolvedParams, persistentTarget = null) {
|
|
465
|
+
if (!schema || !resolvedParams) return;
|
|
466
|
+
const stores = [];
|
|
467
|
+
if (feature) {
|
|
468
|
+
feature.persistentData = feature.persistentData || {};
|
|
469
|
+
stores.push(feature.persistentData);
|
|
470
|
+
}
|
|
471
|
+
if (persistentTarget && typeof persistentTarget === 'object') {
|
|
472
|
+
stores.push(persistentTarget);
|
|
473
|
+
}
|
|
474
|
+
if (!stores.length) return;
|
|
475
|
+
const ensureBucket = (store, key) => {
|
|
476
|
+
if (!store.__refPreviewSnapshots || typeof store.__refPreviewSnapshots !== 'object') {
|
|
477
|
+
store.__refPreviewSnapshots = {};
|
|
478
|
+
}
|
|
479
|
+
if (!store.__refPreviewSnapshots[key] || typeof store.__refPreviewSnapshots[key] !== 'object') {
|
|
480
|
+
store.__refPreviewSnapshots[key] = {};
|
|
481
|
+
}
|
|
482
|
+
return store.__refPreviewSnapshots[key];
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const normalizeRefName = (obj) => {
|
|
486
|
+
if (!obj) return null;
|
|
487
|
+
const raw = obj.name != null ? String(obj.name).trim() : '';
|
|
488
|
+
if (raw) return raw;
|
|
489
|
+
const type = obj.type || 'OBJECT';
|
|
490
|
+
const pos = obj.position || {};
|
|
491
|
+
const x = Number.isFinite(pos.x) ? pos.x : 0;
|
|
492
|
+
const y = Number.isFinite(pos.y) ? pos.y : 0;
|
|
493
|
+
const z = Number.isFinite(pos.z) ? pos.z : 0;
|
|
494
|
+
return `${type}(${x},${y},${z})`;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const extractEdgeWorldPositions = (obj) => {
|
|
498
|
+
if (!obj) return [];
|
|
499
|
+
try { obj.updateMatrixWorld?.(true); } catch { }
|
|
500
|
+
try {
|
|
501
|
+
if (typeof obj.points === 'function') {
|
|
502
|
+
const pts = obj.points(true);
|
|
503
|
+
if (Array.isArray(pts) && pts.length) {
|
|
504
|
+
const flat = [];
|
|
505
|
+
for (const p of pts) {
|
|
506
|
+
if (!p) continue;
|
|
507
|
+
const x = Number(p.x);
|
|
508
|
+
const y = Number(p.y);
|
|
509
|
+
const z = Number(p.z);
|
|
510
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) continue;
|
|
511
|
+
flat.push(x, y, z);
|
|
512
|
+
}
|
|
513
|
+
if (flat.length >= 6) return flat;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
} catch { /* ignore */ }
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
const geom = obj.geometry;
|
|
520
|
+
const pos = geom && typeof geom.getAttribute === 'function' ? geom.getAttribute('position') : null;
|
|
521
|
+
if (!pos || pos.itemSize !== 3 || pos.count < 2) return [];
|
|
522
|
+
const tmp = new THREE.Vector3();
|
|
523
|
+
const flat = [];
|
|
524
|
+
for (let i = 0; i < pos.count; i++) {
|
|
525
|
+
tmp.set(pos.getX(i), pos.getY(i), pos.getZ(i));
|
|
526
|
+
tmp.applyMatrix4(obj.matrixWorld);
|
|
527
|
+
flat.push(tmp.x, tmp.y, tmp.z);
|
|
528
|
+
}
|
|
529
|
+
return flat.length >= 6 ? flat : [];
|
|
530
|
+
} catch { /* ignore */ }
|
|
531
|
+
return [];
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const extractFaceEdgePositions = (face) => {
|
|
535
|
+
if (!face) return [];
|
|
536
|
+
const out = [];
|
|
537
|
+
const addEdge = (edge) => {
|
|
538
|
+
const positions = extractEdgeWorldPositions(edge);
|
|
539
|
+
if (positions && positions.length >= 6) out.push(positions);
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
if (Array.isArray(face.edges) && face.edges.length) {
|
|
543
|
+
for (const edge of face.edges) addEdge(edge);
|
|
544
|
+
return out;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const faceName = face?.name || face?.userData?.faceName || null;
|
|
548
|
+
const parentSolid = face?.parentSolid || face?.userData?.parentSolid || face?.parent || null;
|
|
549
|
+
if (!faceName || !parentSolid || !Array.isArray(parentSolid.children)) return out;
|
|
550
|
+
for (const child of parentSolid.children) {
|
|
551
|
+
if (!child || child.type !== SelectionFilter.EDGE) continue;
|
|
552
|
+
const faceA = child?.userData?.faceA || null;
|
|
553
|
+
const faceB = child?.userData?.faceB || null;
|
|
554
|
+
if (faceA === faceName || faceB === faceName) {
|
|
555
|
+
addEdge(child);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return out;
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
for (const key in schema) {
|
|
562
|
+
if (!Object.prototype.hasOwnProperty.call(schema, key)) continue;
|
|
563
|
+
const def = schema[key];
|
|
564
|
+
if (!def || def.type !== 'reference_selection') continue;
|
|
565
|
+
const selected = Array.isArray(resolvedParams[key]) ? resolvedParams[key] : [];
|
|
566
|
+
if (!selected.length) continue;
|
|
567
|
+
const buckets = stores.map((store) => ensureBucket(store, key));
|
|
568
|
+
for (const obj of selected) {
|
|
569
|
+
if (!obj || typeof obj !== 'object') continue;
|
|
570
|
+
const refName = normalizeRefName(obj);
|
|
571
|
+
if (!refName) continue;
|
|
572
|
+
const objType = String(obj.type || '').toUpperCase();
|
|
573
|
+
const sourceUuid = obj.uuid || null;
|
|
574
|
+
const sourceFeatureId = obj.owningFeatureID ?? null;
|
|
575
|
+
if (objType === SelectionFilter.EDGE || objType === 'EDGE') {
|
|
576
|
+
const positions = extractEdgeWorldPositions(obj);
|
|
577
|
+
if (positions && positions.length >= 6) {
|
|
578
|
+
for (const bucket of buckets) {
|
|
579
|
+
bucket[refName] = { type: 'EDGE', positions, sourceUuid, sourceFeatureId };
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
} else if (objType === SelectionFilter.FACE || objType === 'FACE' || objType === SelectionFilter.PLANE || objType === 'PLANE') {
|
|
583
|
+
const edgePositions = extractFaceEdgePositions(obj);
|
|
584
|
+
if (edgePositions && edgePositions.length) {
|
|
585
|
+
const snapType = (objType === SelectionFilter.PLANE || objType === 'PLANE') ? 'PLANE' : 'FACE';
|
|
586
|
+
for (const bucket of buckets) {
|
|
587
|
+
bucket[refName] = { type: snapType, edgePositions, sourceUuid, sourceFeatureId };
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
} else if (objType === SelectionFilter.VERTEX || objType === 'VERTEX') {
|
|
591
|
+
const pos = new THREE.Vector3();
|
|
592
|
+
try {
|
|
593
|
+
if (typeof obj.getWorldPosition === 'function') obj.getWorldPosition(pos);
|
|
594
|
+
else pos.set(obj.position?.x || 0, obj.position?.y || 0, obj.position?.z || 0);
|
|
595
|
+
} catch { }
|
|
596
|
+
for (const bucket of buckets) {
|
|
597
|
+
bucket[refName] = { type: 'VERTEX', position: [pos.x, pos.y, pos.z], sourceUuid, sourceFeatureId };
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
463
604
|
|
|
464
605
|
async _coerceRunEffects(result, featureType, featureID) {
|
|
465
606
|
if (result == null) return { added: [], removed: [] };
|
package/src/UI/CADmaterials.js
CHANGED
|
@@ -127,8 +127,8 @@ export const CADmaterials = {
|
|
|
127
127
|
depthWrite: true,
|
|
128
128
|
// Keep selected faces slightly behind edges as well.
|
|
129
129
|
polygonOffset: true,
|
|
130
|
-
polygonOffsetFactor:
|
|
131
|
-
polygonOffsetUnits:
|
|
130
|
+
polygonOffsetFactor: -2,
|
|
131
|
+
polygonOffsetUnits: 1,
|
|
132
132
|
emissiveIntensity: 0,
|
|
133
133
|
})
|
|
134
134
|
},
|
package/src/UI/MainToolbar.js
CHANGED
|
@@ -79,6 +79,14 @@ export class MainToolbar {
|
|
|
79
79
|
color: #e9f0ff;
|
|
80
80
|
box-shadow: 0 0 0 1px rgba(110,168,254,.2) inset;
|
|
81
81
|
}
|
|
82
|
+
.mtb-selection {
|
|
83
|
+
display: none;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 6px;
|
|
86
|
+
padding-left: 6px;
|
|
87
|
+
margin-left: 2px;
|
|
88
|
+
border-left: 1px solid #364053;
|
|
89
|
+
}
|
|
82
90
|
`;
|
|
83
91
|
document.head.appendChild(style);
|
|
84
92
|
}
|
|
@@ -103,7 +111,8 @@ export class MainToolbar {
|
|
|
103
111
|
b.className = 'mtb-btn';
|
|
104
112
|
b.textContent = label;
|
|
105
113
|
b.title = title || label;
|
|
106
|
-
b.
|
|
114
|
+
b.__mtbOnClick = onClick;
|
|
115
|
+
b.addEventListener('click', (e) => { e.stopPropagation(); try { b.__mtbOnClick && b.__mtbOnClick(); } catch {} });
|
|
107
116
|
return b;
|
|
108
117
|
}
|
|
109
118
|
|
|
@@ -111,11 +120,39 @@ export class MainToolbar {
|
|
|
111
120
|
addCustomButton({ label, title, onClick }) {
|
|
112
121
|
try {
|
|
113
122
|
const btn = this._btn(String(label ?? '🔧'), String(title || ''), onClick);
|
|
114
|
-
this._left
|
|
123
|
+
const anchor = this._selectionContainer && this._selectionContainer.parentNode === this._left
|
|
124
|
+
? this._selectionContainer
|
|
125
|
+
: null;
|
|
126
|
+
if (anchor) this._left?.insertBefore(btn, anchor);
|
|
127
|
+
else this._left?.appendChild(btn);
|
|
115
128
|
return btn;
|
|
116
129
|
} catch { return null; }
|
|
117
130
|
}
|
|
118
131
|
|
|
132
|
+
_ensureSelectionContainer() {
|
|
133
|
+
if (this._selectionContainer) return this._selectionContainer;
|
|
134
|
+
const wrap = document.createElement('div');
|
|
135
|
+
wrap.className = 'mtb-selection';
|
|
136
|
+
this._selectionContainer = wrap;
|
|
137
|
+
this._left?.appendChild(wrap);
|
|
138
|
+
return wrap;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Public: allow selection-based buttons in their own cluster
|
|
142
|
+
addSelectionButton({ label, title, onClick }) {
|
|
143
|
+
try {
|
|
144
|
+
const btn = this._btn(String(label ?? '🔧'), String(title || ''), onClick);
|
|
145
|
+
if (label && String(label).length <= 2) btn.classList.add('mtb-icon');
|
|
146
|
+
const wrap = this._ensureSelectionContainer();
|
|
147
|
+
wrap.appendChild(btn);
|
|
148
|
+
return btn;
|
|
149
|
+
} catch { return null; }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getSelectionContainer() {
|
|
153
|
+
return this._ensureSelectionContainer();
|
|
154
|
+
}
|
|
155
|
+
|
|
119
156
|
_positionWithSidebar() {
|
|
120
157
|
try {
|
|
121
158
|
const sb = this.viewer?.sidebar;
|