brep-io-kernel 1.0.0

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 (403) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +144 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/dist-kernel/help/CONTRIBUTING.html +248 -0
  5. package/dist-kernel/help/LICENSE.html +248 -0
  6. package/dist-kernel/help/MODELING.png +0 -0
  7. package/dist-kernel/help/PMI.png +0 -0
  8. package/dist-kernel/help/SKETCH.png +0 -0
  9. package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
  10. package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
  11. package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
  12. package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
  13. package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
  14. package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
  15. package/dist-kernel/help/assembly-constraints__angle-constraint.html +248 -0
  16. package/dist-kernel/help/assembly-constraints__coincident-constraint.html +248 -0
  17. package/dist-kernel/help/assembly-constraints__distance-constraint.html +248 -0
  18. package/dist-kernel/help/assembly-constraints__fixed-constraint.html +248 -0
  19. package/dist-kernel/help/assembly-constraints__parallel-constraint.html +248 -0
  20. package/dist-kernel/help/assembly-constraints__solver.html +248 -0
  21. package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +248 -0
  22. package/dist-kernel/help/brep-api.html +263 -0
  23. package/dist-kernel/help/brep-kernel.html +258 -0
  24. package/dist-kernel/help/brep-model.html +248 -0
  25. package/dist-kernel/help/cylindrical-face-radius-embedding.html +290 -0
  26. package/dist-kernel/help/dialog-screenshots.html +248 -0
  27. package/dist-kernel/help/extruded-sketch-radius-embedding.html +336 -0
  28. package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
  29. package/dist-kernel/help/features__Boolean_dialog.png +0 -0
  30. package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
  31. package/dist-kernel/help/features__Datium_dialog.png +0 -0
  32. package/dist-kernel/help/features__Extrude_dialog.png +0 -0
  33. package/dist-kernel/help/features__Fillet_dialog.png +0 -0
  34. package/dist-kernel/help/features__Helix_dialog.png +0 -0
  35. package/dist-kernel/help/features__Hole_dialog.png +0 -0
  36. package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
  37. package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
  38. package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
  39. package/dist-kernel/help/features__Loft_dialog.png +0 -0
  40. package/dist-kernel/help/features__Mirror_dialog.png +0 -0
  41. package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
  42. package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
  43. package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
  44. package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
  45. package/dist-kernel/help/features__Pattern_dialog.png +0 -0
  46. package/dist-kernel/help/features__Plane_dialog.png +0 -0
  47. package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
  48. package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
  49. package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
  50. package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
  51. package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
  52. package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
  53. package/dist-kernel/help/features__Remesh_dialog.png +0 -0
  54. package/dist-kernel/help/features__Revolve_dialog.png +0 -0
  55. package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
  56. package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
  57. package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
  58. package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
  59. package/dist-kernel/help/features__Sketch_dialog.png +0 -0
  60. package/dist-kernel/help/features__Spline_dialog.png +0 -0
  61. package/dist-kernel/help/features__Sweep_dialog.png +0 -0
  62. package/dist-kernel/help/features__Transform_dialog.png +0 -0
  63. package/dist-kernel/help/features__Tube_dialog.png +0 -0
  64. package/dist-kernel/help/features__assembly-component.html +248 -0
  65. package/dist-kernel/help/features__boolean.html +248 -0
  66. package/dist-kernel/help/features__chamfer.html +248 -0
  67. package/dist-kernel/help/features__datium.html +248 -0
  68. package/dist-kernel/help/features__datum.html +248 -0
  69. package/dist-kernel/help/features__extrude.html +248 -0
  70. package/dist-kernel/help/features__fillet.html +248 -0
  71. package/dist-kernel/help/features__helix.html +248 -0
  72. package/dist-kernel/help/features__hole.html +248 -0
  73. package/dist-kernel/help/features__image-heightmap-solid.html +248 -0
  74. package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
  75. package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
  76. package/dist-kernel/help/features__image-to-face.html +248 -0
  77. package/dist-kernel/help/features__import-3d-model.html +248 -0
  78. package/dist-kernel/help/features__index.html +248 -0
  79. package/dist-kernel/help/features__loft.html +248 -0
  80. package/dist-kernel/help/features__mirror.html +248 -0
  81. package/dist-kernel/help/features__offset-shell.html +248 -0
  82. package/dist-kernel/help/features__pattern-linear.html +248 -0
  83. package/dist-kernel/help/features__pattern-radial.html +248 -0
  84. package/dist-kernel/help/features__pattern.html +248 -0
  85. package/dist-kernel/help/features__plane.html +248 -0
  86. package/dist-kernel/help/features__primitive-cone.html +248 -0
  87. package/dist-kernel/help/features__primitive-cube.html +248 -0
  88. package/dist-kernel/help/features__primitive-cylinder.html +248 -0
  89. package/dist-kernel/help/features__primitive-pyramid.html +248 -0
  90. package/dist-kernel/help/features__primitive-sphere.html +248 -0
  91. package/dist-kernel/help/features__primitive-torus.html +248 -0
  92. package/dist-kernel/help/features__remesh.html +248 -0
  93. package/dist-kernel/help/features__revolve.html +248 -0
  94. package/dist-kernel/help/features__sheet-metal-contour-flange.html +248 -0
  95. package/dist-kernel/help/features__sheet-metal-flange.html +248 -0
  96. package/dist-kernel/help/features__sheet-metal-tab.html +248 -0
  97. package/dist-kernel/help/features__sketch.html +248 -0
  98. package/dist-kernel/help/features__spline.html +248 -0
  99. package/dist-kernel/help/features__sweep.html +248 -0
  100. package/dist-kernel/help/features__transform.html +248 -0
  101. package/dist-kernel/help/features__tube.html +248 -0
  102. package/dist-kernel/help/file-formats.html +248 -0
  103. package/dist-kernel/help/getting-started.html +248 -0
  104. package/dist-kernel/help/highlights.html +248 -0
  105. package/dist-kernel/help/history-systems.html +248 -0
  106. package/dist-kernel/help/how-it-works.html +248 -0
  107. package/dist-kernel/help/index.html +862 -0
  108. package/dist-kernel/help/input-params-schema.html +363 -0
  109. package/dist-kernel/help/inspector-improvements.html +248 -0
  110. package/dist-kernel/help/inspector.html +248 -0
  111. package/dist-kernel/help/modes__modeling.html +248 -0
  112. package/dist-kernel/help/modes__pmi.html +248 -0
  113. package/dist-kernel/help/modes__sketch.html +248 -0
  114. package/dist-kernel/help/plugins.html +248 -0
  115. package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
  116. package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
  117. package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
  118. package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
  119. package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
  120. package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
  121. package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
  122. package/dist-kernel/help/pmi-annotations__angle-dimension.html +248 -0
  123. package/dist-kernel/help/pmi-annotations__explode-body.html +248 -0
  124. package/dist-kernel/help/pmi-annotations__hole-callout.html +248 -0
  125. package/dist-kernel/help/pmi-annotations__index.html +248 -0
  126. package/dist-kernel/help/pmi-annotations__leader.html +248 -0
  127. package/dist-kernel/help/pmi-annotations__linear-dimension.html +248 -0
  128. package/dist-kernel/help/pmi-annotations__note.html +248 -0
  129. package/dist-kernel/help/pmi-annotations__radial-dimension.html +248 -0
  130. package/dist-kernel/help/search-index.json +464 -0
  131. package/dist-kernel/help/simplified-radial-dimensions.html +298 -0
  132. package/dist-kernel/help/solid-methods.html +359 -0
  133. package/dist-kernel/help/table-of-contents.html +330 -0
  134. package/dist-kernel/help/ui-overview.html +248 -0
  135. package/dist-kernel/help/whats-new.html +248 -0
  136. package/package.json +54 -0
  137. package/src/BREP/AssemblyComponent.js +42 -0
  138. package/src/BREP/BREP.js +43 -0
  139. package/src/BREP/BetterSolid.js +805 -0
  140. package/src/BREP/Edge.js +103 -0
  141. package/src/BREP/Extrude.js +403 -0
  142. package/src/BREP/Face.js +187 -0
  143. package/src/BREP/MeshRepairer.js +634 -0
  144. package/src/BREP/OffsetShellSolid.js +614 -0
  145. package/src/BREP/PointCloudWrap.js +302 -0
  146. package/src/BREP/Revolve.js +345 -0
  147. package/src/BREP/SolidMethods/authoring.js +112 -0
  148. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  149. package/src/BREP/SolidMethods/chamfer.js +122 -0
  150. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  151. package/src/BREP/SolidMethods/fillet.js +792 -0
  152. package/src/BREP/SolidMethods/index.js +72 -0
  153. package/src/BREP/SolidMethods/io.js +105 -0
  154. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  155. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  156. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  157. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  158. package/src/BREP/SolidMethods/metadata.js +106 -0
  159. package/src/BREP/SolidMethods/metrics.js +51 -0
  160. package/src/BREP/SolidMethods/transforms.js +361 -0
  161. package/src/BREP/SolidMethods/visualize.js +508 -0
  162. package/src/BREP/SolidShared.js +26 -0
  163. package/src/BREP/Sweep.js +1596 -0
  164. package/src/BREP/Tube.js +857 -0
  165. package/src/BREP/Vertex.js +43 -0
  166. package/src/BREP/applyBooleanOperation.js +704 -0
  167. package/src/BREP/boundsUtils.js +48 -0
  168. package/src/BREP/chamfer.js +551 -0
  169. package/src/BREP/edgePolylineUtils.js +85 -0
  170. package/src/BREP/fillets/common.js +388 -0
  171. package/src/BREP/fillets/fillet.js +1422 -0
  172. package/src/BREP/fillets/filletGeometry.js +15 -0
  173. package/src/BREP/fillets/inset.js +389 -0
  174. package/src/BREP/fillets/offsetHelper.js +143 -0
  175. package/src/BREP/fillets/outset.js +88 -0
  176. package/src/BREP/helix.js +193 -0
  177. package/src/BREP/meshToBrep.js +234 -0
  178. package/src/BREP/primitives.js +279 -0
  179. package/src/BREP/setupManifold.js +71 -0
  180. package/src/BREP/threadGeometry.js +1120 -0
  181. package/src/BREP/triangleUtils.js +8 -0
  182. package/src/BREP/triangulate.js +608 -0
  183. package/src/FeatureRegistry.js +183 -0
  184. package/src/PartHistory.js +1132 -0
  185. package/src/UI/AccordionWidget.js +292 -0
  186. package/src/UI/CADmaterials.js +850 -0
  187. package/src/UI/EnvMonacoEditor.js +522 -0
  188. package/src/UI/FloatingWindow.js +396 -0
  189. package/src/UI/HistoryWidget.js +457 -0
  190. package/src/UI/MainToolbar.js +131 -0
  191. package/src/UI/ModelLibraryView.js +194 -0
  192. package/src/UI/OrthoCameraIdle.js +206 -0
  193. package/src/UI/PluginsWidget.js +280 -0
  194. package/src/UI/SceneListing.js +606 -0
  195. package/src/UI/SelectionFilter.js +629 -0
  196. package/src/UI/ViewCube.js +389 -0
  197. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  198. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  199. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  200. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  201. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  202. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  203. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  204. package/src/UI/assembly/constraintPointUtils.js +64 -0
  205. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  206. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  207. package/src/UI/componentSelectorModal.js +240 -0
  208. package/src/UI/controls/CombinedTransformControls.js +386 -0
  209. package/src/UI/dialogs.js +351 -0
  210. package/src/UI/expressionsManager.js +100 -0
  211. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  212. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  213. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  214. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  215. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  216. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  217. package/src/UI/featureDialogWidgets/index.js +34 -0
  218. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  219. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  220. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  221. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  222. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  223. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  224. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  225. package/src/UI/featureDialogWidgets/utils.js +43 -0
  226. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  227. package/src/UI/featureDialogs.js +1414 -0
  228. package/src/UI/fileManagerWidget.js +615 -0
  229. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  230. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  231. package/src/UI/history/historyDisplayInfo.js +133 -0
  232. package/src/UI/mobile.js +28 -0
  233. package/src/UI/objectDump.js +442 -0
  234. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  235. package/src/UI/pmi/AnnotationHistory.js +353 -0
  236. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  237. package/src/UI/pmi/BaseAnnotation.js +269 -0
  238. package/src/UI/pmi/LabelOverlay.css +102 -0
  239. package/src/UI/pmi/LabelOverlay.js +191 -0
  240. package/src/UI/pmi/PMIMode.js +1550 -0
  241. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  242. package/src/UI/pmi/annUtils.js +729 -0
  243. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  244. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  245. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  246. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  247. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  248. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  249. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  250. package/src/UI/pmi/pmiStyle.js +44 -0
  251. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  252. package/src/UI/sketcher/dimensions.js +674 -0
  253. package/src/UI/sketcher/glyphs.js +236 -0
  254. package/src/UI/sketcher/highlights.js +60 -0
  255. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  256. package/src/UI/toolbarButtons/exportButton.js +609 -0
  257. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  258. package/src/UI/toolbarButtons/importButton.js +160 -0
  259. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  260. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  261. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  262. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  263. package/src/UI/toolbarButtons/saveButton.js +99 -0
  264. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  265. package/src/UI/toolbarButtons/testsButton.js +26 -0
  266. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  267. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  268. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  269. package/src/UI/triangleDebuggerWindow.js +945 -0
  270. package/src/UI/viewer.js +4228 -0
  271. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  272. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  273. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  274. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  275. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  276. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  277. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  278. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  279. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  280. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  281. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  282. package/src/core/entities/HistoryCollectionBase.js +72 -0
  283. package/src/core/entities/ListEntityBase.js +109 -0
  284. package/src/core/entities/schemaProcesser.js +121 -0
  285. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  286. package/src/exporters/sheetMetalUnfold.js +862 -0
  287. package/src/exporters/step.js +1135 -0
  288. package/src/exporters/threeMF.js +575 -0
  289. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  290. package/src/features/boolean/BooleanFeature.js +94 -0
  291. package/src/features/chamfer/ChamferFeature.js +116 -0
  292. package/src/features/datium/DatiumFeature.js +80 -0
  293. package/src/features/edgeFeatureUtils.js +41 -0
  294. package/src/features/extrude/ExtrudeFeature.js +143 -0
  295. package/src/features/fillet/FilletFeature.js +197 -0
  296. package/src/features/helix/HelixFeature.js +405 -0
  297. package/src/features/hole/HoleFeature.js +1050 -0
  298. package/src/features/hole/screwClearance.js +86 -0
  299. package/src/features/hole/threadDesignationCatalog.js +149 -0
  300. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  301. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  302. package/src/features/imageToFace/imageEditor.js +1270 -0
  303. package/src/features/imageToFace/traceUtils.js +971 -0
  304. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  305. package/src/features/loft/LoftFeature.js +605 -0
  306. package/src/features/mirror/MirrorFeature.js +151 -0
  307. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  308. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  309. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  310. package/src/features/pattern/PatternFeature.js +275 -0
  311. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  312. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  313. package/src/features/plane/PlaneFeature.js +154 -0
  314. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  315. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  316. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  317. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  318. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  319. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  320. package/src/features/remesh/RemeshFeature.js +97 -0
  321. package/src/features/revolve/RevolveFeature.js +111 -0
  322. package/src/features/selectionUtils.js +118 -0
  323. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  324. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  325. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  326. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  327. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  328. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  329. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  330. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  331. package/src/features/sheetMetal/profileUtils.js +25 -0
  332. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  333. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  334. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  335. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  336. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  337. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  338. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  339. package/src/features/sketch/SketchFeature.js +955 -0
  340. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  341. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  342. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  343. package/src/features/spline/SplineEditorSession.js +988 -0
  344. package/src/features/spline/SplineFeature.js +1388 -0
  345. package/src/features/spline/splineUtils.js +218 -0
  346. package/src/features/sweep/SweepFeature.js +110 -0
  347. package/src/features/transform/TransformFeature.js +152 -0
  348. package/src/features/tube/TubeFeature.js +635 -0
  349. package/src/fs.proxy.js +625 -0
  350. package/src/idbStorage.js +254 -0
  351. package/src/index.js +12 -0
  352. package/src/main.js +15 -0
  353. package/src/metadataManager.js +64 -0
  354. package/src/path.proxy.js +277 -0
  355. package/src/plugins/ghLoader.worker.js +151 -0
  356. package/src/plugins/pluginManager.js +286 -0
  357. package/src/pmi/PMIViewsManager.js +134 -0
  358. package/src/services/componentLibrary.js +198 -0
  359. package/src/tests/ConsoleCapture.js +189 -0
  360. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  361. package/src/tests/browserTests.js +597 -0
  362. package/src/tests/debugBoolean.js +225 -0
  363. package/src/tests/partFiles/badBoolean.json +957 -0
  364. package/src/tests/partFiles/extrudeTest.json +88 -0
  365. package/src/tests/partFiles/filletFail.json +58 -0
  366. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  367. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  368. package/src/tests/test_boolean_subtract.js +27 -0
  369. package/src/tests/test_chamfer.js +17 -0
  370. package/src/tests/test_extrudeFace.js +24 -0
  371. package/src/tests/test_fillet.js +17 -0
  372. package/src/tests/test_fillet_nonClosed.js +45 -0
  373. package/src/tests/test_filletsMoreDifficult.js +46 -0
  374. package/src/tests/test_history_features_basic.js +149 -0
  375. package/src/tests/test_hole.js +282 -0
  376. package/src/tests/test_mirror.js +16 -0
  377. package/src/tests/test_offsetShellGrouping.js +85 -0
  378. package/src/tests/test_plane.js +4 -0
  379. package/src/tests/test_primitiveCone.js +11 -0
  380. package/src/tests/test_primitiveCube.js +7 -0
  381. package/src/tests/test_primitiveCylinder.js +8 -0
  382. package/src/tests/test_primitivePyramid.js +9 -0
  383. package/src/tests/test_primitiveSphere.js +17 -0
  384. package/src/tests/test_primitiveTorus.js +21 -0
  385. package/src/tests/test_pushFace.js +126 -0
  386. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  387. package/src/tests/test_sheetMetal_features.js +80 -0
  388. package/src/tests/test_sketch_openLoop.js +45 -0
  389. package/src/tests/test_solidMetrics.js +58 -0
  390. package/src/tests/test_stlLoader.js +1889 -0
  391. package/src/tests/test_sweepFace.js +55 -0
  392. package/src/tests/test_tube.js +45 -0
  393. package/src/tests/test_tube_closedLoop.js +67 -0
  394. package/src/tests/tests.js +493 -0
  395. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  396. package/src/tools/dialogCapturePageFactory.js +227 -0
  397. package/src/tools/featureDialogCapturePage.js +47 -0
  398. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  399. package/src/utils/axisHelpers.js +99 -0
  400. package/src/utils/deepClone.js +69 -0
  401. package/src/utils/geometryTolerance.js +37 -0
  402. package/src/utils/normalizeTypeString.js +8 -0
  403. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,780 @@
1
+ import JSZip from 'jszip';
2
+ import { ThreeMFLoader } from 'three/examples/jsm/loaders/3MFLoader.js';
3
+ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
4
+ import { BREP } from '../../BREP/BREP.js';
5
+ import { base64ToUint8Array, getComponentRecord } from '../../services/componentLibrary.js';
6
+
7
+ const THREE = BREP.THREE;
8
+
9
+ function handleComponentSelection(ctx, record) {
10
+ if (!ctx || !ctx.feature) return;
11
+ const feature = ctx.feature;
12
+ feature.inputParams = feature.inputParams || {};
13
+ feature.persistentData = feature.persistentData || {};
14
+
15
+ if (!record || !record.data3mf) {
16
+ feature.inputParams.componentName = '';
17
+ delete feature.persistentData.componentData;
18
+ return;
19
+ }
20
+
21
+ feature.inputParams.componentName = record.name || '';
22
+ feature.persistentData.componentData = {
23
+ name: record.name || '',
24
+ savedAt: record.savedAt || null,
25
+ data3mf: record.data3mf,
26
+ featureInfo: null,
27
+ };
28
+ }
29
+
30
+ const DEFAULT_TRANSFORM = {
31
+ position: [0, 0, 0],
32
+ rotationEuler: [0, 0, 0],
33
+ scale: [1, 1, 1],
34
+ };
35
+
36
+ const inputParamsSchema = {
37
+ id: {
38
+ type: 'string',
39
+ default_value: null,
40
+ hint: 'Unique identifier for the assembly component feature.',
41
+ },
42
+ componentName: {
43
+ type: 'component_selector',
44
+ label: 'Component',
45
+ buttonLabel: 'Select…',
46
+ hint: 'Pick a saved 3MF component to insert into this assembly.',
47
+ dialogTitle: 'Select Component',
48
+ onSelect: handleComponentSelection,
49
+ },
50
+ isFixed: {
51
+ type: 'boolean',
52
+ default_value: false,
53
+ label: 'Fixed in place',
54
+ hint: 'Lock this component so assembly constraints cannot move it.',
55
+ },
56
+ transform: {
57
+ type: 'transform',
58
+ default_value: DEFAULT_TRANSFORM,
59
+ label: 'Placement',
60
+ hint: 'Use the transform gizmo to position the component.',
61
+ },
62
+ };
63
+
64
+ export class AssemblyComponentFeature {
65
+ static shortName = 'ACOMP';
66
+ static longName = 'Assembly Component';
67
+ static inputParamsSchema = inputParamsSchema;
68
+
69
+ constructor() {
70
+ this.inputParams = {};
71
+ this.persistentData = {};
72
+ }
73
+
74
+ async run(partHistory) {
75
+ const componentData = await this._resolveComponentData();
76
+ if (!componentData || !componentData.bytes || componentData.bytes.length === 0) {
77
+ const hasSelectionIntent = Boolean((this.inputParams && this.inputParams.componentName) || (this.persistentData && this.persistentData.componentData));
78
+ if (hasSelectionIntent) {
79
+ console.warn('[AssemblyComponentFeature] Component payload missing or failed to load.');
80
+ }
81
+ return { added: [], removed: [] };
82
+ }
83
+
84
+ const featureId = this._sanitizeFeatureId(this.inputParams?.featureID);
85
+ const group = await this._loadThreeMF(componentData.bytes);
86
+ if (!group) {
87
+ console.warn('[AssemblyComponentFeature] Failed to parse 3MF component.');
88
+ return { added: [], removed: [] };
89
+ }
90
+
91
+ const solids = await this._buildSolidsFromGroup(group, componentData);
92
+ if (!solids.length) {
93
+ console.warn('[AssemblyComponentFeature] No solids recovered from component.');
94
+ return { added: [], removed: [] };
95
+ }
96
+
97
+ //const componentName = this.inputParams.componentName || componentData.name ;
98
+ const componentName = `${this.inputParams.componentName || componentData.name}_${featureId}`;
99
+ const component = new BREP.AssemblyComponent({
100
+ name: componentName,
101
+ fixed: !!this.inputParams.isFixed,
102
+ });
103
+
104
+
105
+ for (const solid of solids) {
106
+ if (featureId) {
107
+ if (solid?.type === 'SOLID') {
108
+ this._applyFeaturePrefixToSolid(solid, featureId);
109
+ } else {
110
+ this._applyFeaturePrefixToObject3D(solid, featureId);
111
+ }
112
+ }
113
+
114
+ try {
115
+ if (typeof solid?.visualize === 'function') {
116
+ solid.visualize();
117
+ }
118
+ } catch { /* ignore */ }
119
+
120
+ if (featureId) {
121
+ this._applyFeaturePrefixToObject3D(solid, featureId);
122
+ }
123
+
124
+ component.addBody(solid);
125
+ }
126
+
127
+ if (featureId) {
128
+ this._applyFeaturePrefixToObject3D(component, featureId);
129
+ }
130
+
131
+ const transformMatrix = this._composeTransformMatrix(this.inputParams.transform || DEFAULT_TRANSFORM);
132
+ if (transformMatrix) {
133
+ this._applyMatrixToComponent(component, transformMatrix);
134
+ }
135
+
136
+ component.userData = component.userData || {};
137
+ component.userData.componentSource = {
138
+ name: componentData.name || componentName,
139
+ savedAt: componentData.savedAt || null,
140
+ };
141
+ if (componentData.featureInfo) {
142
+ component.userData.featureInfo = componentData.featureInfo;
143
+ }
144
+
145
+ // Persist canonical payload so reruns do not depend on local storage state.
146
+ this.persistentData.componentData = {
147
+ name: componentData.name || componentName,
148
+ savedAt: componentData.savedAt || null,
149
+ data3mf: componentData.base64,
150
+ featureInfo: componentData.featureInfo || null,
151
+ };
152
+
153
+ return { added: [component], removed: [] };
154
+ }
155
+
156
+ async _resolveComponentData() {
157
+ const persisted = this.persistentData && this.persistentData.componentData;
158
+ if (persisted && persisted.data3mf) {
159
+ const bytes = base64ToUint8Array(persisted.data3mf);
160
+ let featureInfo = persisted.featureInfo;
161
+ if (!featureInfo || !featureInfo.history) {
162
+ featureInfo = await this._extractFeatureInfo(bytes);
163
+ }
164
+ if (featureInfo) {
165
+ this.persistentData.componentData.featureInfo = featureInfo;
166
+ }
167
+ return {
168
+ name: persisted.name || '',
169
+ savedAt: persisted.savedAt || null,
170
+ base64: persisted.data3mf,
171
+ bytes,
172
+ featureInfo,
173
+ };
174
+ }
175
+
176
+ const selectedName = this.inputParams && this.inputParams.componentName;
177
+ if (!selectedName) return null;
178
+
179
+ const record = getComponentRecord(selectedName);
180
+ if (!record || !record.data3mf) return null;
181
+
182
+ const bytes = base64ToUint8Array(record.data3mf);
183
+ const featureInfo = await this._extractFeatureInfo(bytes);
184
+
185
+ this.persistentData = this.persistentData || {};
186
+ this.persistentData.componentData = {
187
+ name: record.name || selectedName,
188
+ savedAt: record.savedAt || null,
189
+ data3mf: record.data3mf,
190
+ featureInfo,
191
+ };
192
+
193
+ return {
194
+ name: record.name || selectedName,
195
+ savedAt: record.savedAt || null,
196
+ base64: record.data3mf,
197
+ bytes,
198
+ featureInfo,
199
+ };
200
+ }
201
+
202
+ async _loadThreeMF(bytes) {
203
+ try {
204
+ const loader = new ThreeMFLoader();
205
+ const buffer = this._toArrayBuffer(bytes);
206
+ if (!buffer) return null;
207
+ return await loader.parse(buffer);
208
+ } catch (err) {
209
+ console.warn('[AssemblyComponentFeature] ThreeMFLoader.parse failed:', err);
210
+ return null;
211
+ }
212
+ }
213
+
214
+ async _buildSolidsFromGroup(group, componentData) {
215
+ const solids = [];
216
+ const componentName = this.inputParams.componentName || componentData.name || 'Component';
217
+ const facetInfo = componentData.featureInfo?.facets || null;
218
+ const metadataMap = componentData.featureInfo?.metadata || null;
219
+
220
+ group.updateMatrixWorld(true);
221
+
222
+ const faceNameCounts = new Map();
223
+ const meshes = [];
224
+ group.traverse((obj) => {
225
+ const geom = obj && obj.isMesh ? obj.geometry : null;
226
+ if (!geom || !geom.isBufferGeometry) return;
227
+ const posAttr = geom.getAttribute('position');
228
+ if (posAttr && posAttr.count >= 3) meshes.push(obj);
229
+ });
230
+
231
+ if (!meshes.length) {
232
+ console.warn(`[AssemblyComponentFeature] Component "${componentName}" contained no mesh primitives.`);
233
+ return await this._rebuildSolidsFromHistory(componentData, componentName);
234
+ }
235
+
236
+ const meshGroups = new Map();
237
+ for (const mesh of meshes) {
238
+ const rawName = String(mesh?.name || '').trim();
239
+ const key = rawName.length ? rawName : `__mesh_${meshGroups.size + 1}`;
240
+ let entry = meshGroups.get(key);
241
+ if (!entry) {
242
+ entry = { sourceName: rawName, meshes: [] };
243
+ meshGroups.set(key, entry);
244
+ }
245
+ entry.meshes.push(mesh);
246
+ }
247
+
248
+ let index = 0;
249
+ for (const entry of meshGroups.values()) {
250
+ const groupName = entry.sourceName || '';
251
+ const groupMeshes = entry.meshes;
252
+ const solidName = this._resolveSolidName(groupName, componentName, ++index);
253
+ const built = this._buildSolidFromMeshes(groupMeshes, faceNameCounts, groupName);
254
+ const solid = built?.solid || null;
255
+ const colorHints = built?.colorHints || null;
256
+ if (!solid || !solid._triVerts || solid._triVerts.length === 0) {
257
+ const meshName = groupName ? `"${groupName}"` : '(unnamed mesh group)';
258
+ console.warn(`[AssemblyComponentFeature] Failed to recover triangles for ${meshName}; skipping.`);
259
+ index--; // keep numbering tight if conversion failed
260
+ continue;
261
+ }
262
+
263
+ solid.name = solidName;
264
+
265
+ if (colorHints) {
266
+ this._applyColorHintsToSolid(solid, colorHints);
267
+ }
268
+
269
+ const faceMeta = facetInfo && facetInfo[solidName];
270
+ if (faceMeta && typeof solid.setFaceMetadata === 'function') {
271
+ for (const key of Object.keys(faceMeta)) {
272
+ try { solid.setFaceMetadata(key, faceMeta[key]); } catch { /* ignore */ }
273
+ }
274
+ }
275
+
276
+ if (metadataMap && metadataMap[solidName]) {
277
+ this._mergeSolidMetadata(solid, metadataMap[solidName]);
278
+ }
279
+ this._applyFaceMetadataFromMap(solid, metadataMap);
280
+
281
+ solids.push(solid);
282
+ }
283
+
284
+ if (!solids.length && meshes.length) {
285
+ try {
286
+ const worldGeometries = [];
287
+ for (const mesh of meshes) {
288
+ const geom = mesh?.geometry;
289
+ if (!geom || typeof geom.clone !== 'function') continue;
290
+ try { mesh.updateWorldMatrix(true, false); }
291
+ catch { }
292
+ const cloned = geom.clone();
293
+ try { cloned.applyMatrix4(mesh.matrixWorld); }
294
+ catch { /* ignore transform issues */ }
295
+ worldGeometries.push(cloned);
296
+ }
297
+ if (worldGeometries.length) {
298
+ const merged = mergeGeometries(worldGeometries, false);
299
+ if (merged) {
300
+ try {
301
+ const fallbackSolid = new BREP.MeshToBrep(merged, 30, 1e-5);
302
+ const fallbackKey = metadataMap && Object.keys(metadataMap).length === 1
303
+ ? Object.keys(metadataMap)[0]
304
+ : (facetInfo && Object.keys(facetInfo).length === 1 ? Object.keys(facetInfo)[0] : null);
305
+ const solidName = fallbackKey || this._resolveSolidName('', componentName, 1);
306
+ fallbackSolid.name = solidName;
307
+ console.warn(`[AssemblyComponentFeature] Using merged-geometry fallback for component "${componentName}" (mesh count: ${meshes.length}).`);
308
+
309
+ if (facetInfo && facetInfo[solidName] && typeof fallbackSolid.setFaceMetadata === 'function') {
310
+ for (const key of Object.keys(facetInfo[solidName])) {
311
+ try { fallbackSolid.setFaceMetadata(key, facetInfo[solidName][key]); }
312
+ catch { /* ignore */ }
313
+ }
314
+ }
315
+
316
+ if (metadataMap && metadataMap[solidName]) {
317
+ this._mergeSolidMetadata(fallbackSolid, metadataMap[solidName]);
318
+ }
319
+ this._applyFaceMetadataFromMap(fallbackSolid, metadataMap);
320
+
321
+ solids.push(fallbackSolid);
322
+ } catch (err) {
323
+ console.warn('[AssemblyComponentFeature] Merged-geometry fallback failed:', err);
324
+ } finally {
325
+ try { merged.dispose(); } catch { }
326
+ }
327
+ }
328
+ }
329
+ for (const g of worldGeometries) {
330
+ try { g.dispose(); } catch { }
331
+ }
332
+ } catch (err) {
333
+ console.warn('[AssemblyComponentFeature] Unable to merge component meshes for fallback:', err);
334
+ }
335
+ }
336
+
337
+ if (!solids.length) {
338
+ const historyFallback = await this._rebuildSolidsFromHistory(componentData, componentName);
339
+ if (historyFallback && historyFallback.length) {
340
+ solids.push(...historyFallback);
341
+ }
342
+ }
343
+
344
+ return solids;
345
+ }
346
+
347
+ _getMaterialName(material) {
348
+ if (Array.isArray(material)) {
349
+ for (const entry of material) {
350
+ if (entry && typeof entry.name === 'string' && entry.name.trim().length) return entry.name;
351
+ }
352
+ return '';
353
+ }
354
+ return material && typeof material.name === 'string' ? material.name : '';
355
+ }
356
+
357
+ _getMaterialColorHex(material) {
358
+ if (!material) return null;
359
+ const mat = Array.isArray(material)
360
+ ? (material.find((entry) => entry?.color?.isColor) || material[0])
361
+ : material;
362
+ const color = mat?.color;
363
+ if (!color || typeof color.getHexString !== 'function') return null;
364
+ try { return `#${color.getHexString()}`; } catch { return null; }
365
+ }
366
+
367
+ _hasColorEntry(metadata, keys) {
368
+ if (!metadata || typeof metadata !== 'object') return false;
369
+ for (const key of keys) {
370
+ if (!Object.prototype.hasOwnProperty.call(metadata, key)) continue;
371
+ const raw = metadata[key];
372
+ if (raw == null) continue;
373
+ if (typeof raw === 'string' && raw.trim() === '') continue;
374
+ return true;
375
+ }
376
+ return false;
377
+ }
378
+
379
+ _appendMeshToSolid(solid, mesh, faceNameCounts) {
380
+ const geometry = mesh?.geometry;
381
+ const posAttr = geometry?.getAttribute?.('position');
382
+ if (!solid || !geometry || !posAttr || posAttr.count < 3) return null;
383
+
384
+ try { mesh.updateWorldMatrix(true, false); }
385
+ catch { /* matrix update best-effort */ }
386
+
387
+ const matrixWorld = mesh.matrixWorld;
388
+ const indexAttr = typeof geometry.getIndex === 'function' ? geometry.getIndex() : null;
389
+ const materialName = this._getMaterialName(mesh.material);
390
+ const baseFaceName = this._safeName(materialName || mesh.name || `FACE_${faceNameCounts.size + 1}`);
391
+ const faceName = this._uniqueName(faceNameCounts, baseFaceName);
392
+
393
+ const a = new THREE.Vector3();
394
+ const b = new THREE.Vector3();
395
+ const c = new THREE.Vector3();
396
+
397
+ let added = 0;
398
+ const writeTriangle = (ia, ib, ic) => {
399
+ if (!Number.isFinite(ia) || !Number.isFinite(ib) || !Number.isFinite(ic)) return;
400
+ if (ia < 0 || ib < 0 || ic < 0) return;
401
+ if (ia >= posAttr.count || ib >= posAttr.count || ic >= posAttr.count) return;
402
+ a.fromBufferAttribute(posAttr, ia).applyMatrix4(matrixWorld);
403
+ b.fromBufferAttribute(posAttr, ib).applyMatrix4(matrixWorld);
404
+ c.fromBufferAttribute(posAttr, ic).applyMatrix4(matrixWorld);
405
+ solid.addTriangle(faceName, [a.x, a.y, a.z], [b.x, b.y, b.z], [c.x, c.y, c.z]);
406
+ added++;
407
+ };
408
+
409
+ if (indexAttr && indexAttr.count >= 3) {
410
+ const triCount = Math.floor(indexAttr.count / 3);
411
+ for (let i = 0; i < triCount; i++) {
412
+ const base = i * 3;
413
+ writeTriangle(indexAttr.getX(base + 0), indexAttr.getX(base + 1), indexAttr.getX(base + 2));
414
+ }
415
+ } else {
416
+ const triCount = Math.floor(posAttr.count / 3);
417
+ for (let i = 0; i < triCount; i++) {
418
+ const base = i * 3;
419
+ writeTriangle(base + 0, base + 1, base + 2);
420
+ }
421
+ }
422
+
423
+ if (added === 0) return null;
424
+ return {
425
+ faceName,
426
+ materialName,
427
+ colorHex: this._getMaterialColorHex(mesh.material),
428
+ };
429
+ }
430
+
431
+ _buildSolidFromMeshes(meshes, faceNameCounts, sourceName) {
432
+ if (!Array.isArray(meshes) || meshes.length === 0) return null;
433
+ const solid = new BREP.Solid();
434
+ const faceInfos = [];
435
+ for (const mesh of meshes) {
436
+ const info = this._appendMeshToSolid(solid, mesh, faceNameCounts);
437
+ if (info) faceInfos.push(info);
438
+ }
439
+ if (!solid._triVerts || solid._triVerts.length === 0) return null;
440
+ const colorHints = this._deriveColorHints(faceInfos, sourceName);
441
+ return { solid, colorHints };
442
+ }
443
+
444
+ _deriveColorHints(faceInfos, sourceName) {
445
+ const faceColors = new Map();
446
+ let solidColor = null;
447
+ const solidMaterialName = sourceName ? `${sourceName}_SOLID` : '';
448
+
449
+ if (Array.isArray(faceInfos)) {
450
+ for (const info of faceInfos) {
451
+ const hex = info?.colorHex;
452
+ if (!hex) continue;
453
+ if (solidMaterialName && info.materialName === solidMaterialName && !solidColor) {
454
+ solidColor = hex;
455
+ continue;
456
+ }
457
+ if (info.faceName) faceColors.set(info.faceName, hex);
458
+ }
459
+ }
460
+
461
+ if (!solidColor && Array.isArray(faceInfos)) {
462
+ const colored = faceInfos.filter((info) => info?.colorHex);
463
+ if (colored.length === 1) {
464
+ solidColor = colored[0].colorHex;
465
+ if (colored[0].faceName) faceColors.delete(colored[0].faceName);
466
+ }
467
+ }
468
+
469
+ return { solidColor, faceColors };
470
+ }
471
+
472
+ _applyColorHintsToSolid(solid, colorHints) {
473
+ if (!solid || !colorHints) return;
474
+ const solidKeys = ['solidColor', 'color'];
475
+ const faceKeys = ['faceColor', 'color'];
476
+
477
+ if (colorHints.solidColor) {
478
+ solid.userData = solid.userData || {};
479
+ const existing = solid.userData.metadata && typeof solid.userData.metadata === 'object'
480
+ ? solid.userData.metadata
481
+ : {};
482
+ if (!this._hasColorEntry(existing, solidKeys)) {
483
+ solid.userData.metadata = { ...existing, color: colorHints.solidColor };
484
+ }
485
+ }
486
+
487
+ if (colorHints.faceColors && typeof solid.setFaceMetadata === 'function') {
488
+ for (const [faceName, hex] of colorHints.faceColors.entries()) {
489
+ const existing = typeof solid.getFaceMetadata === 'function' ? solid.getFaceMetadata(faceName) : null;
490
+ if (this._hasColorEntry(existing, faceKeys)) continue;
491
+ solid.setFaceMetadata(faceName, { color: hex });
492
+ }
493
+ }
494
+ }
495
+
496
+ _mergeSolidMetadata(solid, metadata) {
497
+ if (!solid || !metadata || typeof metadata !== 'object') return;
498
+ solid.userData = solid.userData || {};
499
+ const existing = solid.userData.metadata && typeof solid.userData.metadata === 'object'
500
+ ? solid.userData.metadata
501
+ : {};
502
+ solid.userData.metadata = { ...existing, ...metadata };
503
+ }
504
+
505
+ _applyFaceMetadataFromMap(solid, metadataMap) {
506
+ if (!solid || !metadataMap || typeof metadataMap !== 'object' || typeof solid.setFaceMetadata !== 'function') return;
507
+ const faceNames = typeof solid.getFaceNames === 'function'
508
+ ? solid.getFaceNames()
509
+ : (solid._faceNameToID instanceof Map ? Array.from(solid._faceNameToID.keys()) : []);
510
+ if (!faceNames || !faceNames.length) return;
511
+ for (const faceName of faceNames) {
512
+ const meta = metadataMap[faceName];
513
+ if (!meta || typeof meta !== 'object') continue;
514
+ try { solid.setFaceMetadata(faceName, meta); } catch { /* ignore */ }
515
+ }
516
+ }
517
+
518
+ _buildSolidFromMesh(mesh, faceNameCounts) {
519
+ try {
520
+ const solid = new BREP.Solid();
521
+ const info = this._appendMeshToSolid(solid, mesh, faceNameCounts);
522
+ if (!info || !solid._triVerts || solid._triVerts.length === 0) return null;
523
+ return solid;
524
+ } catch (err) {
525
+ console.warn('[AssemblyComponentFeature] Failed to construct solid from mesh:', err);
526
+ return null;
527
+ }
528
+ }
529
+
530
+ _fallbackSolidFromMesh(mesh) {
531
+ try {
532
+ const geometry = mesh?.geometry;
533
+ const posAttr = geometry?.getAttribute?.('position');
534
+ if (!geometry || !posAttr || posAttr.count < 3) return null;
535
+ const cloned = geometry.clone();
536
+ cloned.applyMatrix4(mesh.matrixWorld);
537
+ return new BREP.MeshToBrep(cloned, 30, 1e-5);
538
+ } catch (err) {
539
+ console.warn('[AssemblyComponentFeature] Fallback MeshToBrep conversion failed:', err);
540
+ return null;
541
+ }
542
+ }
543
+
544
+ async _rebuildSolidsFromHistory(componentData, componentName) {
545
+ try {
546
+ const featureInfo = componentData?.featureInfo;
547
+ const historyPayload = featureInfo?.history;
548
+ const historyString = typeof historyPayload === 'string'
549
+ ? historyPayload
550
+ : (historyPayload ? JSON.stringify(historyPayload) : (featureInfo?.historyString || featureInfo?.rawJSON || null));
551
+ if (!historyString) return [];
552
+
553
+ const { PartHistory } = await import('../../PartHistory.js');
554
+ const sandbox = new PartHistory();
555
+ await sandbox.fromJSON(historyString);
556
+ await sandbox.runHistory();
557
+
558
+ const solids = [];
559
+ const nameCounts = new Map();
560
+ const facetInfo = featureInfo?.facets || null;
561
+ const metadataMap = featureInfo?.metadata || null;
562
+
563
+ const children = sandbox.scene.children.slice();
564
+ for (const child of children) {
565
+ if (!child || (child.type !== 'SOLID' && child.type !== 'COMPONENT')) continue;
566
+ if (child.__removeFlag) continue;
567
+ try { sandbox.scene.remove(child); } catch { }
568
+ child.parent = null;
569
+
570
+ const fallbackBase = child.type === 'COMPONENT' ? `${componentName || 'Component'}_Subassembly${solids.length + 1}` : `${componentName || 'Component'}_Body${solids.length + 1}`;
571
+ const baseName = child.name && child.name.trim().length ? child.name : fallbackBase;
572
+ const finalName = this._uniqueName(nameCounts, this._safeName(baseName));
573
+ child.name = finalName;
574
+
575
+ if (child.type !== 'COMPONENT') {
576
+ const faceMeta = facetInfo && facetInfo[finalName];
577
+ if (faceMeta && typeof child.setFaceMetadata === 'function') {
578
+ for (const key of Object.keys(faceMeta)) {
579
+ try { child.setFaceMetadata(key, faceMeta[key]); } catch { }
580
+ }
581
+ }
582
+
583
+ if (metadataMap && metadataMap[finalName]) {
584
+ this._mergeSolidMetadata(child, metadataMap[finalName]);
585
+ }
586
+ this._applyFaceMetadataFromMap(child, metadataMap);
587
+ }
588
+
589
+ solids.push(child);
590
+ }
591
+
592
+ if (solids.length) {
593
+ console.warn(`[AssemblyComponentFeature] Rebuilt component "${componentName}" from feature history (${solids.length} solid${solids.length === 1 ? '' : 's'}).`);
594
+ }
595
+
596
+ return solids;
597
+ } catch (err) {
598
+ console.warn('[AssemblyComponentFeature] Failed to rebuild component from feature history:', err);
599
+ return [];
600
+ }
601
+ }
602
+
603
+ _composeTransformMatrix(trs) {
604
+ if (!trs) return null;
605
+ try {
606
+ const pos = Array.isArray(trs.position) ? trs.position : [0, 0, 0];
607
+ const rot = Array.isArray(trs.rotationEuler) ? trs.rotationEuler : [0, 0, 0];
608
+ const scl = Array.isArray(trs.scale) ? trs.scale : [1, 1, 1];
609
+ const translation = new THREE.Vector3(pos[0] || 0, pos[1] || 0, pos[2] || 0);
610
+ const rotation = new THREE.Euler(
611
+ THREE.MathUtils.degToRad(rot[0] || 0),
612
+ THREE.MathUtils.degToRad(rot[1] || 0),
613
+ THREE.MathUtils.degToRad(rot[2] || 0),
614
+ 'XYZ'
615
+ );
616
+ const scale = new THREE.Vector3(scl[0] || 1, scl[1] || 1, scl[2] || 1);
617
+ const matrix = new THREE.Matrix4();
618
+ matrix.compose(translation, new THREE.Quaternion().setFromEuler(rotation), scale);
619
+ return matrix;
620
+ } catch {
621
+ return null;
622
+ }
623
+ }
624
+
625
+ _applyMatrixToComponent(component, matrix) {
626
+ if (!component || !matrix) return;
627
+ try {
628
+ const position = new THREE.Vector3();
629
+ const quaternion = new THREE.Quaternion();
630
+ const scale = new THREE.Vector3();
631
+ matrix.decompose(position, quaternion, scale);
632
+ component.position.copy(position);
633
+ component.quaternion.copy(quaternion);
634
+ component.scale.copy(scale);
635
+ component.updateMatrix();
636
+ component.updateMatrixWorld(true);
637
+ } catch {
638
+ /* noop */
639
+ }
640
+ }
641
+
642
+ _uniqueName(map, base) {
643
+ const key = base || 'FACE';
644
+ const count = map.get(key) || 0;
645
+ map.set(key, count + 1);
646
+ return count === 0 ? key : `${key}_${count}`;
647
+ }
648
+
649
+ _safeName(name) {
650
+ const str = String(name || '').trim();
651
+ return str.length ? str : 'FACE';
652
+ }
653
+
654
+ _resolveSolidName(original, componentName, index) {
655
+ const clean = String(original || '').trim();
656
+ if (clean.length) return clean;
657
+ return `${componentName}_Body${index}`;
658
+ }
659
+
660
+ _sanitizeFeatureId(rawId) {
661
+ if (typeof rawId !== 'string') return null;
662
+ const trimmed = rawId.trim();
663
+ return trimmed.length ? trimmed : null;
664
+ }
665
+
666
+ _withFeaturePrefix(featureId, name, fallback = '') {
667
+ const id = this._sanitizeFeatureId(featureId);
668
+ const base = this._safeName(name || fallback);
669
+ if (!id || base === id) return base;
670
+ const prefix = `${id}_`;
671
+ return base.startsWith(prefix) ? base : `${prefix}${base}`;
672
+ }
673
+
674
+ _applyFeaturePrefixToSolid(solid, featureId) {
675
+ const id = this._sanitizeFeatureId(featureId);
676
+ if (!id || !solid) return;
677
+
678
+ solid.name = this._withFeaturePrefix(id, solid.name, solid.type || 'SOLID');
679
+
680
+ const idToFace = solid._idToFaceName instanceof Map ? solid._idToFaceName : null;
681
+ if (idToFace && idToFace.size) {
682
+ const renamedIdToFace = new Map();
683
+ const renamedFaceToId = new Map();
684
+ for (const [faceId, faceName] of idToFace.entries()) {
685
+ const renamed = this._withFeaturePrefix(id, faceName, `FACE_${faceId}`);
686
+ renamedIdToFace.set(faceId, renamed);
687
+ renamedFaceToId.set(renamed, faceId);
688
+ }
689
+ solid._idToFaceName = renamedIdToFace;
690
+ solid._faceNameToID = renamedFaceToId;
691
+ }
692
+
693
+ const faceMetadata = solid._faceMetadata instanceof Map ? solid._faceMetadata : null;
694
+ if (faceMetadata && faceMetadata.size) {
695
+ const renamedMetadata = new Map();
696
+ for (const [faceName, metadata] of faceMetadata.entries()) {
697
+ const renamed = this._withFeaturePrefix(id, faceName, faceName || 'FACE');
698
+ renamedMetadata.set(renamed, metadata);
699
+ }
700
+ solid._faceMetadata = renamedMetadata;
701
+ }
702
+
703
+ if (Array.isArray(solid._auxEdges) && solid._auxEdges.length) {
704
+ for (const aux of solid._auxEdges) {
705
+ if (!aux) continue;
706
+ aux.name = this._withFeaturePrefix(id, aux.name, 'EDGE');
707
+ if (typeof aux.faceA === 'string') {
708
+ aux.faceA = this._withFeaturePrefix(id, aux.faceA, aux.faceA);
709
+ }
710
+ if (typeof aux.faceB === 'string') {
711
+ aux.faceB = this._withFeaturePrefix(id, aux.faceB, aux.faceB);
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ _applyFeaturePrefixToObject3D(object3D, featureId) {
718
+ const id = this._sanitizeFeatureId(featureId);
719
+ if (!id || !object3D) return;
720
+
721
+ const renamed = this._withFeaturePrefix(id, object3D.name, object3D.type || 'Object');
722
+ if (renamed !== object3D.name) {
723
+ object3D.name = renamed;
724
+ }
725
+
726
+ if (object3D.userData && typeof object3D.userData === 'object') {
727
+ if (typeof object3D.userData.faceName === 'string') {
728
+ object3D.userData.faceName = this._withFeaturePrefix(id, object3D.userData.faceName, object3D.userData.faceName);
729
+ }
730
+ if (typeof object3D.userData.faceA === 'string') {
731
+ object3D.userData.faceA = this._withFeaturePrefix(id, object3D.userData.faceA, object3D.userData.faceA);
732
+ }
733
+ if (typeof object3D.userData.faceB === 'string') {
734
+ object3D.userData.faceB = this._withFeaturePrefix(id, object3D.userData.faceB, object3D.userData.faceB);
735
+ }
736
+ }
737
+
738
+ if (Array.isArray(object3D.children) && object3D.children.length) {
739
+ for (const child of object3D.children) {
740
+ this._applyFeaturePrefixToObject3D(child, id);
741
+ }
742
+ }
743
+ }
744
+
745
+ async _extractFeatureInfo(bytes) {
746
+ try {
747
+ const buffer = this._toArrayBuffer(bytes);
748
+ if (!buffer) return null;
749
+ const zip = await JSZip.loadAsync(buffer);
750
+ const candidates = ['Metadata/featureHistory.json', 'metadata/featurehistory.json'];
751
+ for (const path of candidates) {
752
+ const file = zip.file(path);
753
+ if (!file) continue;
754
+ const text = await file.async('string');
755
+ try {
756
+ const parsed = JSON.parse(text);
757
+ return {
758
+ metadata: parsed?.metadata || {},
759
+ facets: parsed?.facets || null,
760
+ history: parsed || null,
761
+ historyString: text,
762
+ };
763
+ } catch {
764
+ return null;
765
+ }
766
+ }
767
+ } catch (err) {
768
+ console.warn('[AssemblyComponentFeature] Failed to extract feature history from component:', err);
769
+ }
770
+ return null;
771
+ }
772
+
773
+ _toArrayBuffer(uint8) {
774
+ if (!(uint8 instanceof Uint8Array)) return null;
775
+ if (uint8.byteOffset === 0 && uint8.byteLength === uint8.buffer.byteLength) {
776
+ return uint8.buffer;
777
+ }
778
+ return uint8.buffer.slice(uint8.byteOffset, uint8.byteOffset + uint8.byteLength);
779
+ }
780
+ }