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,635 @@
1
+ import { BREP } from '../../BREP/BREP.js';
2
+ import { createQuantizer, deriveTolerance } from '../../utils/geometryTolerance.js';
3
+
4
+ const inputParamsSchema = {
5
+ id: {
6
+ type: 'string',
7
+ default_value: null,
8
+ hint: 'Unique identifier for the tube feature'
9
+ },
10
+ path: {
11
+ type: 'reference_selection',
12
+ selectionFilter: ['EDGE'],
13
+ multiple: true,
14
+ default_value: null,
15
+ hint: 'Select one or more connected edges defining the tube path'
16
+ },
17
+ radius: {
18
+ type: 'number',
19
+ default_value: 5,
20
+ hint: 'Outer radius of the tube'
21
+ },
22
+ innerRadius: {
23
+ type: 'number',
24
+ default_value: 0,
25
+ hint: 'Optional inner radius for hollow tubes (0 for solid)'
26
+ },
27
+ resolution: {
28
+ type: 'number',
29
+ default_value: 32,
30
+ hint: 'Segments around the tube circumference'
31
+ },
32
+ mode: {
33
+ type: 'options',
34
+ options: ['Light (fast)', 'Heavy (slow)'],
35
+ default_value: 'Light (fast)',
36
+ hint: 'Light runs fast-first tube (auto fallback), Heavy forces the slower robust build'
37
+ },
38
+ debug: {
39
+ type: 'boolean',
40
+ default_value: false,
41
+ hint: 'Log path points and parameters for debugging'
42
+ },
43
+ boolean: {
44
+ type: 'boolean_operation',
45
+ default_value: { targets: [], operation: 'NONE' },
46
+ hint: 'Optional boolean operation with target solids'
47
+ }
48
+ };
49
+
50
+ const THREE = BREP.THREE;
51
+
52
+ function extractPathPolylineWorld(edgeObj) {
53
+ const pts = [];
54
+ if (!edgeObj) return pts;
55
+ const cached = edgeObj?.userData?.polylineLocal;
56
+ const isWorld = !!(edgeObj?.userData?.polylineWorld);
57
+ const v = new THREE.Vector3();
58
+ if (Array.isArray(cached) && cached.length >= 2) {
59
+ if (isWorld) {
60
+ for (const p of cached) pts.push([p[0], p[1], p[2]]);
61
+ } else {
62
+ for (const p of cached) {
63
+ v.set(p[0], p[1], p[2]).applyMatrix4(edgeObj.matrixWorld);
64
+ pts.push([v.x, v.y, v.z]);
65
+ }
66
+ }
67
+ } else {
68
+ const posAttr = edgeObj?.geometry?.getAttribute?.('position');
69
+ if (posAttr && posAttr.itemSize === 3 && posAttr.count >= 2) {
70
+ for (let i = 0; i < posAttr.count; i++) {
71
+ v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(edgeObj.matrixWorld);
72
+ pts.push([v.x, v.y, v.z]);
73
+ }
74
+ } else {
75
+ const aStart = edgeObj?.geometry?.attributes?.instanceStart;
76
+ const aEnd = edgeObj?.geometry?.attributes?.instanceEnd;
77
+ if (aStart && aEnd && aStart.itemSize === 3 && aEnd.itemSize === 3 && aStart.count === aEnd.count && aStart.count >= 1) {
78
+ v.set(aStart.getX(0), aStart.getY(0), aStart.getZ(0)).applyMatrix4(edgeObj.matrixWorld);
79
+ pts.push([v.x, v.y, v.z]);
80
+ for (let i = 0; i < aEnd.count; i++) {
81
+ v.set(aEnd.getX(i), aEnd.getY(i), aEnd.getZ(i)).applyMatrix4(edgeObj.matrixWorld);
82
+ pts.push([v.x, v.y, v.z]);
83
+ }
84
+ }
85
+ }
86
+ }
87
+ for (let i = pts.length - 2; i >= 0; i--) {
88
+ const a = pts[i];
89
+ const b = pts[i + 1];
90
+ if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) pts.splice(i + 1, 1);
91
+ }
92
+ return pts;
93
+ }
94
+
95
+ function collectEdgePolylines(edges) {
96
+ const polys = [];
97
+ const validEdges = [];
98
+ if (!Array.isArray(edges) || edges.length === 0) return { polys, edges: validEdges };
99
+ for (const edge of edges) {
100
+ const pts = extractPathPolylineWorld(edge);
101
+ if (pts.length >= 2) {
102
+ polys.push(pts);
103
+ validEdges.push(edge);
104
+ }
105
+ }
106
+ return { polys, edges: validEdges };
107
+ }
108
+
109
+ function combinePathPolylines(edges, tol = 1e-5) {
110
+ const { polys } = collectEdgePolylines(edges);
111
+ if (polys.length === 0) return [];
112
+ if (polys.length === 1) return polys[0];
113
+
114
+ const effectiveTol = deriveTolerance(polys, tol);
115
+ const tol2 = effectiveTol * effectiveTol;
116
+ const d2 = (a, b) => {
117
+ const dx = a[0] - b[0], dy = a[1] - b[1], dz = a[2] - b[2];
118
+ return dx * dx + dy * dy + dz * dz;
119
+ };
120
+ const { q, k } = createQuantizer(effectiveTol);
121
+
122
+ const nodes = new Map();
123
+ const endpoints = [];
124
+ const addNode = (pt) => {
125
+ const qp = q(pt);
126
+ const key = k(qp);
127
+ if (!nodes.has(key)) nodes.set(key, { p: qp, edges: new Set() });
128
+ return key;
129
+ };
130
+ for (let i = 0; i < polys.length; i++) {
131
+ const p = polys[i];
132
+ const sKey = addNode(p[0]);
133
+ const eKey = addNode(p[p.length - 1]);
134
+ nodes.get(sKey).edges.add(i);
135
+ nodes.get(eKey).edges.add(i);
136
+ endpoints.push({ sKey, eKey });
137
+ }
138
+
139
+ let startNodeKey = null;
140
+ for (const [key, val] of nodes.entries()) {
141
+ if ((val.edges.size % 2) === 1) { startNodeKey = key; break; }
142
+ }
143
+ if (!startNodeKey) startNodeKey = nodes.keys().next().value;
144
+
145
+ const used = new Array(polys.length).fill(false);
146
+ const chain = [];
147
+
148
+ const appendPoly = (poly, reverse = false) => {
149
+ const pts = reverse ? poly.slice().reverse() : poly;
150
+ if (chain.length === 0) {
151
+ chain.push(...pts);
152
+ return;
153
+ }
154
+ const last = chain[chain.length - 1];
155
+ const first = pts[0];
156
+ if (d2(last, first) <= tol2) chain.push(...pts.slice(1));
157
+ else chain.push(...pts);
158
+ };
159
+
160
+ const tryConsumeFromNode = (nodeKey) => {
161
+ const node = nodes.get(nodeKey);
162
+ if (!node) return false;
163
+ for (const ei of Array.from(node.edges)) {
164
+ if (used[ei]) continue;
165
+ const { sKey, eKey } = endpoints[ei];
166
+ const forward = (sKey === nodeKey);
167
+ used[ei] = true;
168
+ nodes.get(sKey)?.edges.delete(ei);
169
+ nodes.get(eKey)?.edges.delete(ei);
170
+ appendPoly(polys[ei], !forward);
171
+ return forward ? eKey : sKey;
172
+ }
173
+ return null;
174
+ };
175
+
176
+ let cursorKey = startNodeKey;
177
+ let nextKey = tryConsumeFromNode(cursorKey);
178
+ while (nextKey) {
179
+ cursorKey = nextKey;
180
+ nextKey = tryConsumeFromNode(cursorKey);
181
+ }
182
+
183
+ let best = chain.slice();
184
+ for (let s = 0; s < polys.length; s++) {
185
+ if (used[s]) continue;
186
+ const localUsed = new Array(polys.length).fill(false);
187
+ const localChain = [];
188
+ localUsed[s] = true;
189
+ const append = (poly, reverse = false) => {
190
+ const pts = reverse ? poly.slice().reverse() : poly;
191
+ if (localChain.length === 0) { localChain.push(...pts); return; }
192
+ const last = localChain[localChain.length - 1];
193
+ const first = pts[0];
194
+ if (d2(last, first) <= tol2) localChain.push(...pts.slice(1)); else localChain.push(...pts);
195
+ };
196
+ append(polys[s], false);
197
+ let head = k(q(localChain[0]));
198
+ let tail = k(q(localChain[localChain.length - 1]));
199
+ let grew = true;
200
+ while (grew) {
201
+ grew = false;
202
+ for (let i = 0; i < polys.length; i++) {
203
+ if (localUsed[i]) continue;
204
+ const { sKey, eKey } = endpoints[i];
205
+ if (sKey === tail) { append(polys[i], false); tail = eKey; localUsed[i] = true; grew = true; continue; }
206
+ if (eKey === tail) { append(polys[i], true); tail = sKey; localUsed[i] = true; grew = true; continue; }
207
+ if (eKey === head) {
208
+ const pts = polys[i].slice();
209
+ localChain.unshift(...pts.slice(0, pts.length - 1));
210
+ head = sKey;
211
+ localUsed[i] = true;
212
+ grew = true;
213
+ continue;
214
+ }
215
+ if (sKey === head) {
216
+ const pts = polys[i].slice().reverse();
217
+ localChain.unshift(...pts.slice(0, pts.length - 1));
218
+ head = eKey;
219
+ localUsed[i] = true;
220
+ grew = true;
221
+ continue;
222
+ }
223
+ }
224
+ }
225
+ if (localChain.length > best.length) best = localChain;
226
+ }
227
+
228
+ for (let i = best.length - 2; i >= 0; i--) {
229
+ const a = best[i];
230
+ const b = best[i + 1];
231
+ if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) best.splice(i + 1, 1);
232
+ }
233
+ return best;
234
+ }
235
+
236
+ function groupEdgesByConnectivity(edges, tol = 1e-5) {
237
+ const { polys, edges: validEdges } = collectEdgePolylines(edges);
238
+ if (polys.length === 0) return [];
239
+
240
+ const effectiveTol = deriveTolerance(polys, tol);
241
+ const { q, k } = createQuantizer(effectiveTol);
242
+ const nodeEdges = new Map();
243
+ const endpoints = [];
244
+
245
+ const register = (key, idx) => {
246
+ if (!nodeEdges.has(key)) nodeEdges.set(key, new Set());
247
+ nodeEdges.get(key).add(idx);
248
+ };
249
+
250
+ for (let i = 0; i < polys.length; i++) {
251
+ const poly = polys[i];
252
+ const startKey = k(q(poly[0]));
253
+ const endKey = k(q(poly[poly.length - 1]));
254
+ endpoints.push([startKey, endKey]);
255
+ register(startKey, i);
256
+ register(endKey, i);
257
+ }
258
+
259
+ const visited = new Array(polys.length).fill(false);
260
+ const groups = [];
261
+ for (let i = 0; i < polys.length; i++) {
262
+ if (visited[i]) continue;
263
+ const stack = [i];
264
+ const component = [];
265
+ visited[i] = true;
266
+ while (stack.length) {
267
+ const idx = stack.pop();
268
+ component.push(validEdges[idx]);
269
+ const [sKey, eKey] = endpoints[idx];
270
+ const neighbors = new Set([...(nodeEdges.get(sKey) || []), ...(nodeEdges.get(eKey) || [])]);
271
+ for (const n of neighbors) {
272
+ if (visited[n]) continue;
273
+ visited[n] = true;
274
+ stack.push(n);
275
+ }
276
+ }
277
+ if (component.length) groups.push(component);
278
+ }
279
+ return groups;
280
+ }
281
+
282
+ function dedupePoints(points, eps = 1e-7) {
283
+ if (!Array.isArray(points) || points.length === 0) return [];
284
+ const epsSq = eps * eps;
285
+ const out = [points[0]];
286
+ for (let i = 1; i < points.length; i++) {
287
+ const p = points[i];
288
+ const prev = out[out.length - 1];
289
+ const dx = p[0] - prev[0];
290
+ const dy = p[1] - prev[1];
291
+ const dz = p[2] - prev[2];
292
+ if ((dx * dx + dy * dy + dz * dz) > epsSq) out.push(p);
293
+ }
294
+ return out;
295
+ }
296
+
297
+ function extendPathEndpoints(points, extension = 0) {
298
+ if (!Array.isArray(points) || points.length < 2 || !(extension > 0)) {
299
+ return Array.isArray(points) ? points.map(p => Array.isArray(p) ? [p[0], p[1], p[2]] : p) : points;
300
+ }
301
+ const extended = points.map(p => [p[0], p[1], p[2]]);
302
+ const start = extended[0];
303
+ const next = extended[1];
304
+ const sx = next[0] - start[0];
305
+ const sy = next[1] - start[1];
306
+ const sz = next[2] - start[2];
307
+ const startLen = Math.hypot(sx, sy, sz);
308
+ if (startLen > 1e-9) {
309
+ const inv = extension / startLen;
310
+ start[0] -= sx * inv;
311
+ start[1] -= sy * inv;
312
+ start[2] -= sz * inv;
313
+ }
314
+
315
+ const end = extended[extended.length - 1];
316
+ const prev = extended[extended.length - 2];
317
+ const ex = end[0] - prev[0];
318
+ const ey = end[1] - prev[1];
319
+ const ez = end[2] - prev[2];
320
+ const endLen = Math.hypot(ex, ey, ez);
321
+ if (endLen > 1e-9) {
322
+ const inv = extension / endLen;
323
+ end[0] += ex * inv;
324
+ end[1] += ey * inv;
325
+ end[2] += ez * inv;
326
+ }
327
+
328
+ return extended;
329
+ }
330
+
331
+ export class TubeFeature {
332
+ static shortName = 'TU';
333
+ static longName = 'Tube';
334
+ static inputParamsSchema = inputParamsSchema;
335
+
336
+ constructor() {
337
+ this.inputParams = {};
338
+ this.persistentData = {};
339
+ }
340
+
341
+ async run(partHistory) {
342
+ const { featureID, path, radius, innerRadius, resolution, debug, mode } = this.inputParams;
343
+ const radiusValue = Number(radius);
344
+ if (!(radiusValue > 0)) {
345
+ throw new Error('Tube requires a positive radius.');
346
+ }
347
+ const inner = Number(innerRadius) || 0;
348
+ if (inner < 0) {
349
+ throw new Error('Inside radius cannot be negative.');
350
+ }
351
+ if (inner > 0 && inner >= radiusValue) {
352
+ throw new Error('Inside radius must be smaller than the outer radius.');
353
+ }
354
+ const edges = Array.isArray(path) ? path.filter(Boolean) : (path ? [path] : []);
355
+ if (!edges.length) {
356
+ throw new Error('Tube requires at least one EDGE selection for the path.');
357
+ }
358
+
359
+ const edgeGroups = groupEdgesByConnectivity(edges);
360
+ if (!edgeGroups.length) {
361
+ throw new Error('Unable to build a connected path for the tube.');
362
+ }
363
+
364
+ const baseResolution = Math.max(8, Math.floor(Number(resolution) || 32));
365
+ const modeSelection = typeof mode === 'string'
366
+ ? mode
367
+ : (mode == null ? inputParamsSchema.mode.default_value : String(mode));
368
+ const preferFast = String(modeSelection || '').toLowerCase().startsWith('light');
369
+ const TubeBuilder = BREP.Tube;
370
+ const outerSolids = [];
371
+ const innerSolids = [];
372
+ const debugExtras = [];
373
+ for (let i = 0; i < edgeGroups.length; i++) {
374
+ const group = edgeGroups[i];
375
+ const pathPoints = dedupePoints(combinePathPolylines(group));
376
+ if (pathPoints.length < 2) {
377
+ throw new Error('Unable to build a connected path for the tube.');
378
+ }
379
+
380
+ const isClosedLoop = pathPoints.length > 2 && (() => {
381
+ const first = pathPoints[0];
382
+ const last = pathPoints[pathPoints.length - 1];
383
+ const dx = first[0] - last[0];
384
+ const dy = first[1] - last[1];
385
+ const dz = first[2] - last[2];
386
+ const distSq = dx * dx + dy * dy + dz * dz;
387
+ const pathScale = Math.max(...pathPoints.map(p => Math.max(Math.abs(p[0]), Math.abs(p[1]), Math.abs(p[2])))) || 1;
388
+ const tolerance = pathScale * 1e-6;
389
+ return distSq <= tolerance * tolerance;
390
+ })();
391
+
392
+ const finalPoints = isClosedLoop ? pathPoints.slice(0, -1) : pathPoints;
393
+ const tubeName = (() => {
394
+ if (!featureID) return featureID;
395
+ if (edgeGroups.length === 1) return featureID;
396
+
397
+ // get the name of the first edge in the group if possible
398
+ const firstEdge = group[0];
399
+ if (firstEdge) {
400
+ const edgeName = firstEdge.name || firstEdge.id;
401
+ if (edgeName) {
402
+ return `${featureID}_${edgeName}`;
403
+ }
404
+ }
405
+
406
+ return `${featureID}_${i + 1}`;
407
+ })();
408
+
409
+ if (debug) {
410
+ console.log('[TubeFeature debug] params', {
411
+ featureID,
412
+ radius: radiusValue,
413
+ innerRadius: inner,
414
+ resolution: baseResolution,
415
+ mode: modeSelection,
416
+ builder: preferFast ? 'Tube (fast-first)' : 'Tube (slow)',
417
+ groupIndex: i,
418
+ isClosedLoop,
419
+ pathPointCount: finalPoints.length,
420
+ points: finalPoints
421
+ });
422
+ }
423
+
424
+ const outerTube = new TubeBuilder({
425
+ points: finalPoints,
426
+ radius: radiusValue,
427
+ innerRadius: 0,
428
+ resolution: baseResolution,
429
+ closed: isClosedLoop,
430
+ name: tubeName,
431
+ debugSpheres: !!debug,
432
+ preferFast,
433
+ });
434
+ if (debug && Array.isArray(outerTube.debugSphereSolids)) {
435
+ debugExtras.push(...outerTube.debugSphereSolids);
436
+ }
437
+ outerSolids.push(outerTube);
438
+
439
+ if (inner > 0) {
440
+ const innerName = tubeName ? `${tubeName}_Inner` : null;
441
+ const baseScale = Math.max(radiusValue, inner);
442
+ const capExtension = (!isClosedLoop && baseScale > 0)
443
+ ? Math.max(baseScale * 5e-3, 1e-4)
444
+ : 0;
445
+ const innerPoints = (!isClosedLoop && capExtension > 0)
446
+ ? extendPathEndpoints(finalPoints, capExtension)
447
+ : finalPoints;
448
+ const innerTube = new TubeBuilder({
449
+ points: innerPoints,
450
+ radius: inner,
451
+ innerRadius: 0,
452
+ resolution: baseResolution,
453
+ closed: isClosedLoop,
454
+ name: innerName,
455
+ debugSpheres: !!debug,
456
+ preferFast,
457
+ });
458
+ if (debug && Array.isArray(innerTube.debugSphereSolids)) {
459
+ debugExtras.push(...innerTube.debugSphereSolids);
460
+ }
461
+ innerSolids.push(innerTube);
462
+ }
463
+ }
464
+
465
+ if (!outerSolids.length) {
466
+ throw new Error('Unable to build a connected path for the tube.');
467
+ }
468
+
469
+ const attemptUnionSolids = (solids, label) => {
470
+ if (!Array.isArray(solids) || solids.length === 0) return null;
471
+ if (solids.length === 1) {
472
+ if (label) {
473
+ try { solids[0].name = label; } catch (_) { }
474
+ }
475
+ return solids[0];
476
+ }
477
+ console.log('Attempting union of solids:', solids);
478
+
479
+
480
+
481
+ try {
482
+ let result = solids[0];
483
+
484
+ for (let idx = 1; idx < solids.length; idx++) {
485
+ try {
486
+ result = result.union(solids[idx]);
487
+ } catch (err) {
488
+ console.warn(`[TubeFeature] Union step failed at index ${idx}:`, err?.message || err);
489
+ return null;
490
+ }
491
+ }
492
+ if (label) {
493
+ try { result.name = label; } catch (_) { }
494
+ }
495
+ return result;
496
+ } catch (error) {
497
+ console.warn('[TubeFeature] Union attempt failed:', error?.message || error);
498
+ return null;
499
+ }
500
+ };
501
+
502
+ const booleanParam = this.inputParams.boolean;
503
+ const booleanOp = String(booleanParam?.operation || 'NONE').toUpperCase();
504
+ const booleanTargets = Array.isArray(booleanParam?.targets) ? booleanParam.targets.filter(Boolean) : [];
505
+ const shouldApplyBoolean = booleanOp !== 'NONE' && booleanTargets.length > 0;
506
+
507
+ // Always attempt to union outer segments into one solid when possible
508
+ const outerUnionLabel = featureID || outerSolids[0]?.name || 'TubeUnion';
509
+ const outerUnion = (outerSolids.length > 1) ? attemptUnionSolids(outerSolids, outerUnionLabel) : (outerSolids[0] || null);
510
+
511
+ // Always attempt to union inner segments into one cutter when inside radius is set
512
+ const innerUnionLabel = featureID ? `${featureID}_InnerUnion` : (innerSolids[0]?.name || null);
513
+ const innerUnion = (inner > 0 && innerSolids.length > 0)
514
+ ? ((innerSolids.length > 1) ? attemptUnionSolids(innerSolids, innerUnionLabel) : innerSolids[0])
515
+ : null;
516
+
517
+ // Build the base (hollow) shell per required pipeline:
518
+ // final = union(outer) minus union(inner)
519
+ let baseSolids = [];
520
+ if (inner > 0) {
521
+ // Preferred path: single subtract of the two unions
522
+ if (outerUnion && innerUnion) {
523
+ try {
524
+ baseSolids = [outerUnion.subtract(innerUnion)];
525
+ } catch (e) {
526
+ console.warn('[TubeFeature] Subtract of unioned inner from unioned outer failed; falling back:', e?.message || e);
527
+ }
528
+ }
529
+
530
+ // Fallbacks if the preferred single subtract was not possible
531
+ if (!baseSolids.length) {
532
+ if (outerUnion && innerSolids.length > 0) {
533
+ // Subtract each inner from the outer union
534
+ let shell = outerUnion;
535
+ for (const cutter of innerUnion ? [innerUnion] : innerSolids) {
536
+ try { if (cutter) shell = shell.subtract(cutter); } catch (err) {
537
+ console.warn('[TubeFeature] Fallback subtract (outerUnion - cutter) failed:', err?.message || err);
538
+ }
539
+ }
540
+ baseSolids = [shell];
541
+ } else if (!outerUnion && innerUnion && outerSolids.length > 0) {
542
+ // Subtract inner union from each outer, then try to union the result
543
+ const shells = [];
544
+ for (const outer of outerSolids) {
545
+ try { shells.push(outer.subtract(innerUnion)); } catch (err) {
546
+ console.warn('[TubeFeature] Fallback subtract (outer - innerUnion) failed:', err?.message || err);
547
+ shells.push(outer);
548
+ }
549
+ }
550
+ const unifiedShell = attemptUnionSolids(shells, outerUnionLabel) || null;
551
+ baseSolids = unifiedShell ? [unifiedShell] : shells.filter(Boolean);
552
+ } else {
553
+ // Last resort: pairwise subtract matching inners when available
554
+ const shells = [];
555
+ for (let i = 0; i < outerSolids.length; i++) {
556
+ const outer = outerSolids[i];
557
+ const cutter = innerSolids[i] || innerUnion || null;
558
+ if (outer) {
559
+ try {
560
+ shells.push(cutter ? outer.subtract(cutter) : outer);
561
+ } catch (err) {
562
+ console.warn(`[TubeFeature] Pairwise subtract failed for segment ${i}:`, err?.message || err);
563
+ shells.push(outer);
564
+ }
565
+ }
566
+ }
567
+ const unifiedShell = attemptUnionSolids(shells, outerUnionLabel) || null;
568
+ baseSolids = unifiedShell ? [unifiedShell] : shells.filter(Boolean);
569
+ }
570
+ }
571
+ } else {
572
+ // Solid tube (no inner). Prefer a unified outer shell if available
573
+ baseSolids = outerUnion ? [outerUnion] : outerSolids.slice();
574
+ }
575
+
576
+ baseSolids = baseSolids.filter(Boolean);
577
+
578
+ if (debug && debugExtras.length) {
579
+ for (const s of debugExtras) {
580
+ try { s.visualize(); } catch (_) { }
581
+ }
582
+ }
583
+
584
+ if (!baseSolids.length) {
585
+ throw new Error('Tube generation failed to produce a valid solid.');
586
+ }
587
+
588
+ for (const solid of baseSolids) {
589
+ if (!solid) continue;
590
+ if (featureID) {
591
+ try { solid.owningFeatureID = featureID; } catch (_) { }
592
+ if (!solid.name) {
593
+ try { solid.name = featureID; } catch (_) { }
594
+ }
595
+ }
596
+ try { solid.visualize(); } catch (_) { }
597
+ }
598
+
599
+ if (shouldApplyBoolean) {
600
+ const added = [];
601
+ const removed = [];
602
+ let booleanBases = baseSolids;
603
+ if (booleanOp === 'UNION' && baseSolids.length > 1) {
604
+ const unionLabel = featureID || baseSolids[0]?.name || 'TubeUnion';
605
+ const unified = attemptUnionSolids(baseSolids, unionLabel);
606
+ if (unified) {
607
+ booleanBases = [unified];
608
+ try { unified.owningFeatureID = featureID; } catch (_) { }
609
+ try { unified.visualize(); } catch (_) { }
610
+ }
611
+ }
612
+ for (const base of booleanBases) {
613
+ const effects = await BREP.applyBooleanOperation(partHistory || {}, base, booleanParam, featureID);
614
+ if (effects?.added?.length) {
615
+ for (const solid of effects.added) {
616
+ if (solid) added.push(solid);
617
+ }
618
+ }
619
+ if (effects?.removed?.length) {
620
+ for (const solid of effects.removed) {
621
+ if (solid) removed.push(solid);
622
+ }
623
+ }
624
+ }
625
+ if (debug && debugExtras.length) {
626
+ added.push(...debugExtras);
627
+ }
628
+ return { added, removed };
629
+ }
630
+
631
+ const added = [...baseSolids];
632
+ if (debug && debugExtras.length) added.push(...debugExtras);
633
+ return { added, removed: [] };
634
+ }
635
+ }