brep-io-kernel 1.0.0-ci.10

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 (271) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +157 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/package.json +58 -0
  5. package/src/BREP/AssemblyComponent.js +42 -0
  6. package/src/BREP/BREP.js +43 -0
  7. package/src/BREP/BetterSolid.js +805 -0
  8. package/src/BREP/Edge.js +103 -0
  9. package/src/BREP/Extrude.js +403 -0
  10. package/src/BREP/Face.js +187 -0
  11. package/src/BREP/MeshRepairer.js +634 -0
  12. package/src/BREP/OffsetShellSolid.js +614 -0
  13. package/src/BREP/PointCloudWrap.js +302 -0
  14. package/src/BREP/Revolve.js +345 -0
  15. package/src/BREP/SolidMethods/authoring.js +112 -0
  16. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  17. package/src/BREP/SolidMethods/chamfer.js +122 -0
  18. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  19. package/src/BREP/SolidMethods/fillet.js +792 -0
  20. package/src/BREP/SolidMethods/index.js +72 -0
  21. package/src/BREP/SolidMethods/io.js +105 -0
  22. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  23. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  24. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  25. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  26. package/src/BREP/SolidMethods/metadata.js +106 -0
  27. package/src/BREP/SolidMethods/metrics.js +51 -0
  28. package/src/BREP/SolidMethods/transforms.js +361 -0
  29. package/src/BREP/SolidMethods/visualize.js +508 -0
  30. package/src/BREP/SolidShared.js +26 -0
  31. package/src/BREP/Sweep.js +1596 -0
  32. package/src/BREP/Tube.js +857 -0
  33. package/src/BREP/Vertex.js +43 -0
  34. package/src/BREP/applyBooleanOperation.js +704 -0
  35. package/src/BREP/boundsUtils.js +48 -0
  36. package/src/BREP/chamfer.js +551 -0
  37. package/src/BREP/edgePolylineUtils.js +85 -0
  38. package/src/BREP/fillets/common.js +388 -0
  39. package/src/BREP/fillets/fillet.js +1422 -0
  40. package/src/BREP/fillets/filletGeometry.js +15 -0
  41. package/src/BREP/fillets/inset.js +389 -0
  42. package/src/BREP/fillets/offsetHelper.js +143 -0
  43. package/src/BREP/fillets/outset.js +88 -0
  44. package/src/BREP/helix.js +193 -0
  45. package/src/BREP/meshToBrep.js +234 -0
  46. package/src/BREP/primitives.js +279 -0
  47. package/src/BREP/setupManifold.js +71 -0
  48. package/src/BREP/threadGeometry.js +1120 -0
  49. package/src/BREP/triangleUtils.js +8 -0
  50. package/src/BREP/triangulate.js +608 -0
  51. package/src/FeatureRegistry.js +183 -0
  52. package/src/PartHistory.js +1132 -0
  53. package/src/UI/AccordionWidget.js +292 -0
  54. package/src/UI/CADmaterials.js +850 -0
  55. package/src/UI/EnvMonacoEditor.js +522 -0
  56. package/src/UI/FloatingWindow.js +396 -0
  57. package/src/UI/HistoryWidget.js +457 -0
  58. package/src/UI/MainToolbar.js +131 -0
  59. package/src/UI/ModelLibraryView.js +194 -0
  60. package/src/UI/OrthoCameraIdle.js +206 -0
  61. package/src/UI/PluginsWidget.js +280 -0
  62. package/src/UI/SceneListing.js +606 -0
  63. package/src/UI/SelectionFilter.js +629 -0
  64. package/src/UI/ViewCube.js +389 -0
  65. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  66. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  67. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  68. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  69. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  70. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  71. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  72. package/src/UI/assembly/constraintPointUtils.js +64 -0
  73. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  74. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  75. package/src/UI/componentSelectorModal.js +240 -0
  76. package/src/UI/controls/CombinedTransformControls.js +386 -0
  77. package/src/UI/dialogs.js +351 -0
  78. package/src/UI/expressionsManager.js +100 -0
  79. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  80. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  81. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  82. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  83. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  84. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  85. package/src/UI/featureDialogWidgets/index.js +34 -0
  86. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  87. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  88. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  89. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  90. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  91. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  92. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  93. package/src/UI/featureDialogWidgets/utils.js +43 -0
  94. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  95. package/src/UI/featureDialogs.js +1414 -0
  96. package/src/UI/fileManagerWidget.js +615 -0
  97. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  98. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  99. package/src/UI/history/historyDisplayInfo.js +133 -0
  100. package/src/UI/mobile.js +28 -0
  101. package/src/UI/objectDump.js +442 -0
  102. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  103. package/src/UI/pmi/AnnotationHistory.js +353 -0
  104. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  105. package/src/UI/pmi/BaseAnnotation.js +269 -0
  106. package/src/UI/pmi/LabelOverlay.css +102 -0
  107. package/src/UI/pmi/LabelOverlay.js +191 -0
  108. package/src/UI/pmi/PMIMode.js +1550 -0
  109. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  110. package/src/UI/pmi/annUtils.js +729 -0
  111. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  112. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  113. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  114. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  115. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  116. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  117. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  118. package/src/UI/pmi/pmiStyle.js +44 -0
  119. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  120. package/src/UI/sketcher/dimensions.js +674 -0
  121. package/src/UI/sketcher/glyphs.js +236 -0
  122. package/src/UI/sketcher/highlights.js +60 -0
  123. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  124. package/src/UI/toolbarButtons/exportButton.js +609 -0
  125. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  126. package/src/UI/toolbarButtons/importButton.js +160 -0
  127. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  128. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  129. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  130. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  131. package/src/UI/toolbarButtons/saveButton.js +99 -0
  132. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  133. package/src/UI/toolbarButtons/testsButton.js +26 -0
  134. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  135. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  136. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  137. package/src/UI/triangleDebuggerWindow.js +945 -0
  138. package/src/UI/viewer.js +4228 -0
  139. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  140. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  141. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  142. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  143. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  144. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  145. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  146. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  147. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  148. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  149. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  150. package/src/core/entities/HistoryCollectionBase.js +72 -0
  151. package/src/core/entities/ListEntityBase.js +109 -0
  152. package/src/core/entities/schemaProcesser.js +121 -0
  153. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  154. package/src/exporters/sheetMetalUnfold.js +862 -0
  155. package/src/exporters/step.js +1135 -0
  156. package/src/exporters/threeMF.js +575 -0
  157. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  158. package/src/features/boolean/BooleanFeature.js +94 -0
  159. package/src/features/chamfer/ChamferFeature.js +116 -0
  160. package/src/features/datium/DatiumFeature.js +80 -0
  161. package/src/features/edgeFeatureUtils.js +41 -0
  162. package/src/features/extrude/ExtrudeFeature.js +143 -0
  163. package/src/features/fillet/FilletFeature.js +197 -0
  164. package/src/features/helix/HelixFeature.js +405 -0
  165. package/src/features/hole/HoleFeature.js +1050 -0
  166. package/src/features/hole/screwClearance.js +86 -0
  167. package/src/features/hole/threadDesignationCatalog.js +149 -0
  168. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  169. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  170. package/src/features/imageToFace/imageEditor.js +1270 -0
  171. package/src/features/imageToFace/traceUtils.js +971 -0
  172. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  173. package/src/features/loft/LoftFeature.js +605 -0
  174. package/src/features/mirror/MirrorFeature.js +151 -0
  175. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  176. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  177. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  178. package/src/features/pattern/PatternFeature.js +275 -0
  179. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  180. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  181. package/src/features/plane/PlaneFeature.js +154 -0
  182. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  183. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  184. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  185. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  186. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  187. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  188. package/src/features/remesh/RemeshFeature.js +97 -0
  189. package/src/features/revolve/RevolveFeature.js +111 -0
  190. package/src/features/selectionUtils.js +118 -0
  191. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  192. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  193. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  194. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  195. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  196. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  197. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  198. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  199. package/src/features/sheetMetal/profileUtils.js +25 -0
  200. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  201. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  202. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  203. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  204. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  205. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  206. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  207. package/src/features/sketch/SketchFeature.js +955 -0
  208. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  209. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  210. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  211. package/src/features/spline/SplineEditorSession.js +988 -0
  212. package/src/features/spline/SplineFeature.js +1388 -0
  213. package/src/features/spline/splineUtils.js +218 -0
  214. package/src/features/sweep/SweepFeature.js +110 -0
  215. package/src/features/transform/TransformFeature.js +152 -0
  216. package/src/features/tube/TubeFeature.js +635 -0
  217. package/src/fs.proxy.js +625 -0
  218. package/src/idbStorage.js +254 -0
  219. package/src/index.js +12 -0
  220. package/src/main.js +15 -0
  221. package/src/metadataManager.js +64 -0
  222. package/src/path.proxy.js +277 -0
  223. package/src/plugins/ghLoader.worker.js +151 -0
  224. package/src/plugins/pluginManager.js +286 -0
  225. package/src/pmi/PMIViewsManager.js +134 -0
  226. package/src/services/componentLibrary.js +198 -0
  227. package/src/tests/ConsoleCapture.js +189 -0
  228. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  229. package/src/tests/browserTests.js +597 -0
  230. package/src/tests/debugBoolean.js +225 -0
  231. package/src/tests/partFiles/badBoolean.json +957 -0
  232. package/src/tests/partFiles/extrudeTest.json +88 -0
  233. package/src/tests/partFiles/filletFail.json +58 -0
  234. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  235. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  236. package/src/tests/test_boolean_subtract.js +27 -0
  237. package/src/tests/test_chamfer.js +17 -0
  238. package/src/tests/test_extrudeFace.js +24 -0
  239. package/src/tests/test_fillet.js +17 -0
  240. package/src/tests/test_fillet_nonClosed.js +45 -0
  241. package/src/tests/test_filletsMoreDifficult.js +46 -0
  242. package/src/tests/test_history_features_basic.js +149 -0
  243. package/src/tests/test_hole.js +282 -0
  244. package/src/tests/test_mirror.js +16 -0
  245. package/src/tests/test_offsetShellGrouping.js +85 -0
  246. package/src/tests/test_plane.js +4 -0
  247. package/src/tests/test_primitiveCone.js +11 -0
  248. package/src/tests/test_primitiveCube.js +7 -0
  249. package/src/tests/test_primitiveCylinder.js +8 -0
  250. package/src/tests/test_primitivePyramid.js +9 -0
  251. package/src/tests/test_primitiveSphere.js +17 -0
  252. package/src/tests/test_primitiveTorus.js +21 -0
  253. package/src/tests/test_pushFace.js +126 -0
  254. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  255. package/src/tests/test_sheetMetal_features.js +80 -0
  256. package/src/tests/test_sketch_openLoop.js +45 -0
  257. package/src/tests/test_solidMetrics.js +58 -0
  258. package/src/tests/test_stlLoader.js +1889 -0
  259. package/src/tests/test_sweepFace.js +55 -0
  260. package/src/tests/test_tube.js +45 -0
  261. package/src/tests/test_tube_closedLoop.js +67 -0
  262. package/src/tests/tests.js +493 -0
  263. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  264. package/src/tools/dialogCapturePageFactory.js +227 -0
  265. package/src/tools/featureDialogCapturePage.js +47 -0
  266. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  267. package/src/utils/axisHelpers.js +99 -0
  268. package/src/utils/deepClone.js +69 -0
  269. package/src/utils/geometryTolerance.js +37 -0
  270. package/src/utils/normalizeTypeString.js +8 -0
  271. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,115 @@
1
+ import * as THREE from 'three';
2
+ import { objectRepresentativePoint } from '../pmi/annUtils.js';
3
+
4
+ export function isFaceObject(object) {
5
+ if (!object) return false;
6
+ const type = object.userData?.type || object.userData?.brepType || object.type;
7
+ return String(type).toUpperCase() === 'FACE';
8
+ }
9
+
10
+ export function computeFaceOrigin(object) {
11
+ if (!object) return null;
12
+ try {
13
+ const pt = objectRepresentativePoint?.(null, object);
14
+ if (pt && typeof pt.clone === 'function') return pt.clone();
15
+ } catch { /* ignore */ }
16
+
17
+ const geom = object.geometry;
18
+ if (geom?.computeBoundingBox) {
19
+ try {
20
+ geom.computeBoundingBox();
21
+ const center = geom.boundingBox?.getCenter(new THREE.Vector3());
22
+ if (center) {
23
+ object.updateMatrixWorld?.(true);
24
+ return center.applyMatrix4(object.matrixWorld);
25
+ }
26
+ } catch { /* ignore */ }
27
+ }
28
+
29
+ if (typeof object.getWorldPosition === 'function') {
30
+ try {
31
+ return object.getWorldPosition(new THREE.Vector3());
32
+ } catch { /* ignore */ }
33
+ }
34
+
35
+ return null;
36
+ }
37
+
38
+ export function computeFaceNormal(object) {
39
+ if (!object) return null;
40
+ try {
41
+ if (typeof object.getAverageNormal === 'function') {
42
+ const avg = object.getAverageNormal();
43
+ if (avg && avg.lengthSq() > 1e-10) return avg.clone().normalize();
44
+ }
45
+ } catch { /* ignore */ }
46
+
47
+ const geom = object.geometry;
48
+ if (!geom?.isBufferGeometry) return null;
49
+ const pos = geom.getAttribute?.('position');
50
+ if (!pos || pos.itemSize !== 3 || pos.count < 3) return null;
51
+ const index = geom.getIndex?.();
52
+
53
+ const v0 = new THREE.Vector3();
54
+ const v1 = new THREE.Vector3();
55
+ const v2 = new THREE.Vector3();
56
+ const edge1 = new THREE.Vector3();
57
+ const edge2 = new THREE.Vector3();
58
+ const accum = new THREE.Vector3();
59
+
60
+ object.updateMatrixWorld?.(true);
61
+
62
+ const triCount = index ? Math.floor(index.count / 3) : Math.floor(pos.count / 3);
63
+ const samples = Math.min(triCount, 60);
64
+ let count = 0;
65
+
66
+ for (let tri = 0; tri < samples; tri += 1) {
67
+ let i0;
68
+ let i1;
69
+ let i2;
70
+
71
+ if (index) {
72
+ const base = tri * 3;
73
+ if (base + 2 >= index.count) break;
74
+ i0 = index.getX(base);
75
+ i1 = index.getX(base + 1);
76
+ i2 = index.getX(base + 2);
77
+ } else {
78
+ i0 = tri * 3;
79
+ i1 = i0 + 1;
80
+ i2 = i0 + 2;
81
+ if (i2 >= pos.count) break;
82
+ }
83
+
84
+ v0.set(pos.getX(i0), pos.getY(i0), pos.getZ(i0)).applyMatrix4(object.matrixWorld);
85
+ v1.set(pos.getX(i1), pos.getY(i1), pos.getZ(i1)).applyMatrix4(object.matrixWorld);
86
+ v2.set(pos.getX(i2), pos.getY(i2), pos.getZ(i2)).applyMatrix4(object.matrixWorld);
87
+
88
+ edge1.subVectors(v1, v0);
89
+ edge2.subVectors(v2, v0);
90
+ const normal = new THREE.Vector3().crossVectors(edge1, edge2);
91
+ if (normal.lengthSq() > 1e-10) {
92
+ accum.add(normal);
93
+ count += 1;
94
+ }
95
+ }
96
+
97
+ if (count === 0) return null;
98
+
99
+ accum.divideScalar(count);
100
+ if (accum.lengthSq() <= 1e-10) return null;
101
+
102
+ return accum.normalize();
103
+ }
104
+
105
+ export function estimateArrowLength(object) {
106
+ const geom = object?.geometry;
107
+ if (geom?.computeBoundingSphere) {
108
+ try {
109
+ geom.computeBoundingSphere();
110
+ const radius = geom.boundingSphere?.radius;
111
+ if (Number.isFinite(radius) && radius > 0) return Math.max(radius, 10);
112
+ } catch { /* ignore */ }
113
+ }
114
+ return 10;
115
+ }
@@ -0,0 +1,70 @@
1
+ export function applyHighlightMaterial(object, color, store, skipSets = [store]) {
2
+ if (!object || !color || !store) return false;
3
+
4
+ const guardSets = Array.isArray(skipSets)
5
+ ? skipSets.filter((m) => m && typeof m.has === 'function')
6
+ : [];
7
+ if (guardSets.length === 0) guardSets.push(store);
8
+
9
+ const targets = [];
10
+ object.traverse?.((child) => {
11
+ if (!child || !child.isObject3D) return;
12
+ if (child.material && child.material.color) targets.push(child);
13
+ });
14
+ if (targets.length === 0 && object.material && object.material.color) {
15
+ targets.push(object);
16
+ }
17
+
18
+ let modified = false;
19
+ for (const target of targets) {
20
+ const key = target.uuid;
21
+ if (guardSets.some((map) => map.has(key))) continue;
22
+
23
+ const originalMaterial = target.material;
24
+ let replaced = false;
25
+ let highlightMaterial = originalMaterial;
26
+ if (originalMaterial && typeof originalMaterial.clone === 'function') {
27
+ try {
28
+ const clone = originalMaterial.clone();
29
+ if (clone) {
30
+ highlightMaterial = clone;
31
+ replaced = clone !== originalMaterial;
32
+ }
33
+ } catch { /* ignore */ }
34
+ }
35
+
36
+ const previousColor = (!replaced && highlightMaterial?.color && highlightMaterial.color.clone)
37
+ ? highlightMaterial.color.clone()
38
+ : null;
39
+
40
+ try { highlightMaterial?.color?.set(color); } catch { /* ignore */ }
41
+
42
+ if (replaced) {
43
+ target.material = highlightMaterial;
44
+ }
45
+
46
+ store.set(key, {
47
+ object: target,
48
+ replaced,
49
+ originalMaterial,
50
+ previousColor,
51
+ });
52
+ modified = true;
53
+ }
54
+
55
+ return modified;
56
+ }
57
+
58
+ export function restoreHighlightRecords(map) {
59
+ if (!map || typeof map.size !== 'number' || map.size === 0) return;
60
+ for (const record of map.values()) {
61
+ try {
62
+ if (record.replaced && record.originalMaterial) {
63
+ record.object.material = record.originalMaterial;
64
+ } else if (record.previousColor && record.object.material?.color) {
65
+ record.object.material.color.copy(record.previousColor);
66
+ }
67
+ } catch { /* ignore */ }
68
+ }
69
+ map.clear();
70
+ }
@@ -0,0 +1,31 @@
1
+ import { evaluateConstraintNumericValue } from '../../assemblyConstraints/constraintExpressionUtils.js';
2
+
3
+ export function constraintLabelText(entry, constraintClass, partHistory = null) {
4
+ const cls = constraintClass || entry?.constraintClass || null;
5
+ const rawShortName = cls?.shortName || cls?.constraintShortName;
6
+ const shortName = rawShortName != null ? String(rawShortName).trim() : '';
7
+ const base = shortName
8
+ || cls?.longName
9
+ || cls?.constraintName
10
+ || entry?.constraintType
11
+ || entry?.type
12
+ || 'Constraint';
13
+
14
+ let distanceSuffix = '';
15
+ let angleSuffix = '';
16
+ if (entry?.type === 'distance' || cls?.constraintType === 'distance') {
17
+ const distance = evaluateConstraintNumericValue(partHistory, entry?.inputParams?.distance);
18
+ if (distance != null) distanceSuffix = String(distance);
19
+ }
20
+ if (entry?.type === 'angle' || cls?.constraintType === 'angle') {
21
+ const angle = evaluateConstraintNumericValue(partHistory, entry?.inputParams?.angle);
22
+ if (angle != null) angleSuffix = `${angle}°`;
23
+ }
24
+
25
+ const parts = [];
26
+ if (base) parts.push(String(base).trim());
27
+ if (distanceSuffix) parts.push(distanceSuffix);
28
+ if (angleSuffix) parts.push(angleSuffix);
29
+
30
+ return parts.join(' ').trim();
31
+ }
@@ -0,0 +1,64 @@
1
+ import * as THREE from 'three';
2
+ import { objectRepresentativePoint } from '../pmi/annUtils.js';
3
+
4
+ export function extractWorldPoint(object) {
5
+ if (!object) return null;
6
+ try { object.updateMatrixWorld?.(true); }
7
+ catch { /* ignore */ }
8
+
9
+ try {
10
+ if (typeof object.type === 'string'
11
+ && object.type.toUpperCase() === 'EDGE'
12
+ && typeof object.points === 'function') {
13
+ const pts = object.points(true);
14
+ if (Array.isArray(pts) && pts.length) {
15
+ const total = new THREE.Vector3();
16
+ let count = 0;
17
+ for (const pt of pts) {
18
+ if (!pt) continue;
19
+ const x = Number(pt.x);
20
+ const y = Number(pt.y);
21
+ const z = Number(pt.z);
22
+ if ([x, y, z].some((v) => !Number.isFinite(v))) continue;
23
+ total.add(new THREE.Vector3(x, y, z));
24
+ count += 1;
25
+ }
26
+ if (count > 0) {
27
+ total.divideScalar(count);
28
+ return total;
29
+ }
30
+ }
31
+ }
32
+ } catch { /* ignore */ }
33
+
34
+ try {
35
+ const rep = objectRepresentativePoint?.(null, object);
36
+ if (rep && typeof rep.clone === 'function') return rep.clone();
37
+ if (rep && rep.isVector3) return rep.clone();
38
+ } catch { /* ignore */ }
39
+
40
+ try {
41
+ if (typeof object.getWorldPosition === 'function') {
42
+ return object.getWorldPosition(new THREE.Vector3());
43
+ }
44
+ } catch { /* ignore */ }
45
+
46
+ try {
47
+ if (object.isVector3) return object.clone();
48
+ } catch { /* ignore */ }
49
+
50
+ try {
51
+ if (object.position) {
52
+ const pos = object.position.clone
53
+ ? object.position.clone()
54
+ : new THREE.Vector3(object.position.x, object.position.y, object.position.z);
55
+ if (object.parent?.matrixWorld) {
56
+ object.parent.updateMatrixWorld?.(true);
57
+ return pos.applyMatrix4(object.parent.matrixWorld);
58
+ }
59
+ return pos;
60
+ }
61
+ } catch { /* ignore */ }
62
+
63
+ return null;
64
+ }
@@ -0,0 +1,185 @@
1
+ export function resolveSelectionObject(scene, selection, options = {}) {
2
+ const scoreFn = typeof options.scoreFn === 'function' ? options.scoreFn : scoreObjectForNormal;
3
+ return internalResolveSelectionObject(scene, selection, { scoreFn });
4
+ }
5
+
6
+ function internalResolveSelectionObject(scene, selection, options) {
7
+ if (!scene || selection == null) return null;
8
+ if (selection.isObject3D) return selection;
9
+
10
+ if (Array.isArray(selection)) {
11
+ for (const item of selection) {
12
+ const resolved = internalResolveSelectionObject(scene, item, options);
13
+ if (resolved) return resolved;
14
+ }
15
+ return null;
16
+ }
17
+
18
+ if (typeof selection === 'string') {
19
+ return resolveObjectFromString(scene, selection, options);
20
+ }
21
+
22
+ if (typeof selection === 'object') {
23
+ if (selection.isObject3D) return selection;
24
+ const {
25
+ uuid,
26
+ name,
27
+ id,
28
+ path,
29
+ reference,
30
+ target,
31
+ } = selection;
32
+
33
+ if (typeof uuid === 'string') {
34
+ try {
35
+ const found = scene.getObjectByProperty?.('uuid', uuid);
36
+ if (found) return found;
37
+ } catch { /* ignore */ }
38
+ }
39
+
40
+ const resolveCandidate = (candidate) => (
41
+ typeof candidate === 'string'
42
+ ? resolveObjectFromString(scene, candidate, options)
43
+ : null
44
+ );
45
+
46
+ const nameCandidate = typeof name === 'string'
47
+ ? name
48
+ : (typeof selection?.selectionName === 'string' ? selection.selectionName : null);
49
+ const idCandidate = typeof id === 'string' ? id : null;
50
+
51
+ const nameResolved = resolveCandidate(nameCandidate);
52
+ if (nameResolved) return nameResolved;
53
+
54
+ const idResolved = resolveCandidate(idCandidate);
55
+ if (idResolved) return idResolved;
56
+
57
+ if (Array.isArray(path)) {
58
+ for (let i = path.length - 1; i >= 0; i -= 1) {
59
+ const segment = path[i];
60
+ if (typeof segment !== 'string') continue;
61
+ const resolved = resolveObjectFromString(scene, segment, options);
62
+ if (resolved) return resolved;
63
+ }
64
+ }
65
+
66
+ if (reference != null) {
67
+ const resolved = internalResolveSelectionObject(scene, reference, options);
68
+ if (resolved) return resolved;
69
+ }
70
+
71
+ if (target != null) {
72
+ const resolved = internalResolveSelectionObject(scene, target, options);
73
+ if (resolved) return resolved;
74
+ }
75
+ }
76
+
77
+ return null;
78
+ }
79
+
80
+ function resolveObjectFromString(scene, value, options) {
81
+ if (!scene || typeof value !== 'string') return null;
82
+ const trimmed = value.trim();
83
+ if (!trimmed) return null;
84
+
85
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
86
+ try {
87
+ const parsed = JSON.parse(trimmed);
88
+ if (parsed != null) {
89
+ const resolved = internalResolveSelectionObject(scene, parsed, options);
90
+ if (resolved) return resolved;
91
+ }
92
+ } catch { /* ignore JSON parse errors */ }
93
+ }
94
+
95
+ const direct = findObjectByName(scene, trimmed, options.scoreFn);
96
+ if (direct) return direct;
97
+
98
+ if (looksLikeUUID(trimmed)) {
99
+ try {
100
+ const byUuid = scene.getObjectByProperty?.('uuid', trimmed);
101
+ if (byUuid) return byUuid;
102
+ } catch { /* ignore */ }
103
+ }
104
+
105
+ const candidates = new Set();
106
+ candidates.add(trimmed);
107
+
108
+ const splitByDelims = trimmed.split(/›|>|\/|\||→|->/);
109
+ if (splitByDelims.length > 1) {
110
+ for (const segment of splitByDelims) {
111
+ const s = segment.trim();
112
+ if (s) candidates.add(s);
113
+ }
114
+ }
115
+
116
+ if (trimmed.includes(':')) {
117
+ for (const segment of trimmed.split(':')) {
118
+ const s = segment.trim();
119
+ if (s) candidates.add(s);
120
+ }
121
+ }
122
+
123
+ for (const candidate of candidates) {
124
+ const found = findObjectByName(scene, candidate, options.scoreFn);
125
+ if (found) return found;
126
+ }
127
+
128
+ let fallback = null;
129
+ try {
130
+ scene.traverse?.((obj) => {
131
+ if (fallback || !obj?.name) return;
132
+ if (!trimmed.includes(obj.name)) return;
133
+ if (!fallback) {
134
+ fallback = obj;
135
+ return;
136
+ }
137
+ const currentScore = options.scoreFn(fallback);
138
+ const nextScore = options.scoreFn(obj);
139
+ if (nextScore > currentScore || obj.name.length > fallback.name.length) {
140
+ fallback = obj;
141
+ }
142
+ });
143
+ } catch { /* ignore */ }
144
+
145
+ return fallback;
146
+ }
147
+
148
+ function findObjectByName(scene, name, scoreFn) {
149
+ if (!scene || typeof name !== 'string' || !name) return null;
150
+
151
+ if (typeof scene.traverse !== 'function') {
152
+ return scene?.getObjectByName?.(name) || null;
153
+ }
154
+
155
+ let best = null;
156
+ scene.traverse((obj) => {
157
+ if (!obj || obj.name !== name) return;
158
+ if (!best) {
159
+ best = obj;
160
+ return;
161
+ }
162
+ const currentScore = scoreFn(best);
163
+ const newScore = scoreFn(obj);
164
+ if (newScore > currentScore) best = obj;
165
+ });
166
+
167
+ if (best) return best;
168
+ if (typeof scene.getObjectByName === 'function') return scene.getObjectByName(name);
169
+ return null;
170
+ }
171
+
172
+ function looksLikeUUID(value) {
173
+ if (typeof value !== 'string') return false;
174
+ const trimmed = value.trim();
175
+ if (trimmed.length !== 36) return false;
176
+ return /^[0-9a-fA-F-]{36}$/.test(trimmed);
177
+ }
178
+
179
+ function scoreObjectForNormal(object) {
180
+ if (!object) return -Infinity;
181
+ const type = object.userData?.type || object.userData?.brepType || object.type;
182
+ if (String(type).toUpperCase() === 'FACE') return 3;
183
+ if (object.geometry) return 2;
184
+ return 1;
185
+ }
@@ -0,0 +1,142 @@
1
+ export function constraintStatusInfo(entry) {
2
+ const pd = entry?.persistentData || {};
3
+ const status = pd.status || 'Idle';
4
+ const isDisabled = entry?.enabled === false || status === 'disabled';
5
+ const out = { label: '', title: '', error: false, color: '#ffd60a' };
6
+
7
+ if (isDisabled) {
8
+ out.label = 'Disabled';
9
+ out.title = pd.message || 'Constraint disabled.';
10
+ out.color = '#8e8e93';
11
+ return out;
12
+ }
13
+
14
+ if (status === 'unimplemented') {
15
+ out.label = 'Unimplemented';
16
+ out.title = pd.message || 'Constraint solver not implemented yet.';
17
+ out.error = true;
18
+ out.color = '#ff3b30';
19
+ return out;
20
+ }
21
+
22
+ if (status === 'satisfied') {
23
+ out.label = 'Satisfied';
24
+ out.title = pd.message || 'Constraint satisfied within tolerance.';
25
+ out.color = '#30d158';
26
+ return out;
27
+ }
28
+
29
+ if (status === 'adjusted') {
30
+ out.label = 'Adjusting';
31
+ out.title = pd.message || 'Constraint nudging components toward the solution.';
32
+ out.color = '#ffd60a';
33
+ return out;
34
+ }
35
+
36
+ if (status === 'adjusting') {
37
+ out.label = 'Adjusting';
38
+ out.title = pd.message || 'Constraint nudging components toward the solution.';
39
+ out.color = '#ffd60a';
40
+ return out;
41
+ }
42
+
43
+ if (status === 'blocked') {
44
+ out.label = 'Blocked';
45
+ out.title = pd.message || 'Constraint cannot adjust locked components.';
46
+ out.error = true;
47
+ out.color = '#ff3b30';
48
+ return out;
49
+ }
50
+
51
+ if (status === 'pending') {
52
+ out.label = 'Pending';
53
+ out.title = pd.message || 'Constraint awaiting convergence.';
54
+ return out;
55
+ }
56
+
57
+ if (status === 'duplicate') {
58
+ out.label = 'Duplicate';
59
+ out.title = pd.message || 'Another constraint uses the same selections.';
60
+ out.error = true;
61
+ out.color = '#ff3b30';
62
+ return out;
63
+ }
64
+
65
+ if (status === 'error') {
66
+ out.label = 'Error';
67
+ out.title = pd.message || 'Constraint evaluation failed.';
68
+ out.error = true;
69
+ out.color = '#ff3b30';
70
+ return out;
71
+ }
72
+
73
+ if (status === 'applied' || status === 'computed') {
74
+ const delta = Array.isArray(pd.lastDelta)
75
+ ? pd.lastDelta.map((v) => Number(v).toFixed(3)).join(', ')
76
+ : null;
77
+ out.label = status === 'applied' ? 'Applied' : 'Pending';
78
+ if (delta) out.label += ` · Δ[${delta}]`;
79
+ if (Array.isArray(pd.lastAppliedMoves) && pd.lastAppliedMoves.length) {
80
+ const summary = pd.lastAppliedMoves
81
+ .map((move) => {
82
+ if (!move || !Array.isArray(move.move)) return null;
83
+ const vec = move.move.map((v) => Number(v).toFixed(3)).join(', ');
84
+ return `${move.element}: [${vec}]`;
85
+ })
86
+ .filter(Boolean)
87
+ .join(' | ');
88
+ if (summary) out.title = summary;
89
+ }
90
+ return out;
91
+ }
92
+
93
+ if (status === 'incomplete') {
94
+ out.label = 'Incomplete';
95
+ out.title = 'Select the required components to define the constraint.';
96
+ return out;
97
+ }
98
+
99
+ if (status === 'invalid-selection') {
100
+ out.label = 'Invalid selection';
101
+ out.title = 'Unable to resolve world positions for selections.';
102
+ out.error = true;
103
+ return out;
104
+ }
105
+
106
+ if (status === 'pending-component') {
107
+ out.label = 'Pending component';
108
+ out.title = 'Offset stored but no component selected to move.';
109
+ return out;
110
+ }
111
+
112
+ if (status === 'fixed') {
113
+ out.label = 'Locked';
114
+ out.title = pd.message || 'Both components are fixed; nothing moved.';
115
+ return out;
116
+ }
117
+
118
+ if (status === 'noop') {
119
+ out.label = 'No change';
120
+ out.title = pd.message || 'Selections already satisfy this constraint.';
121
+ return out;
122
+ }
123
+
124
+ if (status === 'apply-failed') {
125
+ out.label = 'Failed';
126
+ out.title = pd.error || 'Apply failed';
127
+ out.error = true;
128
+ out.color = '#ff3b30';
129
+ return out;
130
+ }
131
+
132
+ if (status && status !== 'Idle') {
133
+ out.label = status.charAt(0).toUpperCase() + status.slice(1);
134
+ if (pd.message) out.title = pd.message;
135
+ }
136
+
137
+ if (out.error && !out.color) {
138
+ out.color = '#ff3b30';
139
+ }
140
+
141
+ return out;
142
+ }