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.
Files changed (32) hide show
  1. package/dist-kernel/brep-kernel.js +13547 -12034
  2. package/package.json +1 -1
  3. package/src/FeatureRegistry.js +3 -0
  4. package/src/PartHistory.js +141 -0
  5. package/src/UI/CADmaterials.js +2 -2
  6. package/src/UI/MainToolbar.js +39 -2
  7. package/src/UI/SelectionFilter.js +438 -0
  8. package/src/UI/featureDialogWidgets/booleanOperationField.js +33 -52
  9. package/src/UI/featureDialogWidgets/referenceSelectionField.js +10 -0
  10. package/src/UI/featureDialogs.js +841 -8
  11. package/src/UI/history/HistoryCollectionWidget.js +20 -1
  12. package/src/UI/pmi/AnnotationRegistry.js +3 -0
  13. package/src/UI/toolbarButtons/orientToFaceButton.js +3 -0
  14. package/src/UI/toolbarButtons/registerDefaultButtons.js +0 -6
  15. package/src/UI/toolbarButtons/registerSelectionButtons.js +68 -0
  16. package/src/UI/viewer.js +22 -4
  17. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +3 -0
  18. package/src/features/boolean/BooleanFeature.js +15 -0
  19. package/src/features/chamfer/ChamferFeature.js +12 -0
  20. package/src/features/extrude/ExtrudeFeature.js +11 -0
  21. package/src/features/fillet/FilletFeature.js +12 -0
  22. package/src/features/hole/HoleFeature.js +15 -0
  23. package/src/features/loft/LoftFeature.js +17 -0
  24. package/src/features/mirror/MirrorFeature.js +14 -0
  25. package/src/features/patternLinear/PatternLinearFeature.js +9 -0
  26. package/src/features/patternRadial/PatternRadialFeature.js +13 -0
  27. package/src/features/plane/PlaneFeature.js +10 -0
  28. package/src/features/revolve/RevolveFeature.js +15 -0
  29. package/src/features/sketch/SketchFeature.js +11 -0
  30. package/src/features/sweep/SweepFeature.js +17 -0
  31. package/src/features/transform/TransformFeature.js +12 -0
  32. package/src/features/tube/TubeFeature.js +12 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brep-io-kernel",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "scripts": {
5
5
  "dev": "pnpm generateLicenses && pnpm build:kernel && vite --host 0.0.0.0",
6
6
  "build": "pnpm generateLicenses && vite build",
@@ -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
 
@@ -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: [] };
@@ -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: 0,
131
- polygonOffsetUnits: 0,
130
+ polygonOffsetFactor: -2,
131
+ polygonOffsetUnits: 1,
132
132
  emissiveIntensity: 0,
133
133
  })
134
134
  },
@@ -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.addEventListener('click', (e) => { e.stopPropagation(); try { onClick && onClick(); } catch {} });
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?.appendChild(btn);
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;