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,605 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+ import { selectionHasSketch } from "../selectionUtils.js";
3
+ const THREE = BREP.THREE;
4
+
5
+ const inputParamsSchema = {
6
+ id: {
7
+ type: "string",
8
+ default_value: null,
9
+ hint: "unique identifier for the loft feature",
10
+ },
11
+ profiles: {
12
+ type: "reference_selection",
13
+ selectionFilter: ["SKETCH", "FACE"],
14
+ multiple: true,
15
+ default_value: [],
16
+ hint: "Select 2+ profiles (faces) to loft",
17
+ },
18
+ consumeProfileSketch: {
19
+ type: "boolean",
20
+ default_value: true,
21
+ hint: "Remove referenced sketches after creating the loft. Turn off to keep them in the scene.",
22
+ },
23
+ referencePoints: {
24
+ type: "reference_selection",
25
+ selectionFilter: ["VERTEX"],
26
+ multiple: true,
27
+ default_value: [],
28
+ label: "Start Points (optional)",
29
+ hint: "Optionally select start vertex per profile (order matches profiles)",
30
+ },
31
+ guideCurves: {
32
+ type: "reference_selection",
33
+ selectionFilter: ["EDGE"],
34
+ multiple: true,
35
+ default_value: [],
36
+ hint: "Optional guide curves (unused)",
37
+ },
38
+ loftType: {
39
+ type: "options",
40
+ options: ["normal"],
41
+ default_value: "normal",
42
+ hint: "Type of loft to create",
43
+ },
44
+ reverseFirstLoop: {
45
+ type: "boolean",
46
+ default_value: false,
47
+ label: "Reverse first loop order",
48
+ hint: "Reverse edge order of the first profile's outer loop",
49
+ },
50
+ boolean: {
51
+ type: "boolean_operation",
52
+ default_value: { targets: [], operation: 'NONE' },
53
+ hint: "Optional boolean operation with selected solids"
54
+ }
55
+ };
56
+
57
+ export class LoftFeature {
58
+ static shortName = "LOFT";
59
+ static longName = "Loft";
60
+ static inputParamsSchema = inputParamsSchema;
61
+
62
+ constructor() {
63
+ this.inputParams = {};
64
+ this.persistentData = {};
65
+ }
66
+
67
+ uiFieldsTest(context) {
68
+ const params = this.inputParams || context?.params || {};
69
+ const partHistory = context?.history || null;
70
+ return selectionHasSketch(params.profiles, partHistory) ? [] : ["consumeProfileSketch"];
71
+ }
72
+
73
+ async run(partHistory) {
74
+ const { profiles } = this.inputParams;
75
+ if (!Array.isArray(profiles) || profiles.length < 2) {
76
+ console.warn("LoftFeature: select at least two profiles (faces or sketches)");
77
+ return { added: [], removed: [] };
78
+ }
79
+
80
+ // Resolve input names to FACE objects; allow SKETCH that contains a FACE
81
+ const faces = [];
82
+ const removed = [];
83
+ const consumeSketch = this.inputParams?.consumeProfileSketch !== false;
84
+ for (const obj of profiles) {
85
+ if (!obj) continue;
86
+ let faceObj = obj;
87
+ if (obj && obj.type === 'SKETCH') {
88
+ faceObj = obj.children.find(ch => ch.type === 'FACE') || obj.children.find(ch => ch.userData?.faceName);
89
+ }
90
+ if (faceObj && faceObj.type === 'FACE') {
91
+ // If face came from a sketch, mark sketch for removal (structured)
92
+ if (consumeSketch && faceObj.parent && faceObj.parent.type === 'SKETCH') {
93
+ removed.push(faceObj.parent);
94
+ }
95
+ faces.push(faceObj);
96
+ }
97
+ }
98
+
99
+ if (faces.length < 2) {
100
+ console.warn("LoftFeature: need at least two resolved FACE objects");
101
+ return { added: [], removed: [] };
102
+ }
103
+
104
+ // Build a sidewall naming map using ONLY the first face's edge names
105
+ const firstFace = faces[0];
106
+ const key = (p) => `${p[0].toFixed(6)},${p[1].toFixed(6)},${p[2].toFixed(6)}`;
107
+ const pointToEdgeNames = new Map(); // key -> Set(edgeName)
108
+ const seedEdgePoint = (edgeName, arrP) => {
109
+ for (const p of arrP) {
110
+ const k = key(p);
111
+ let set = pointToEdgeNames.get(k);
112
+ if (!set) { set = new Set(); pointToEdgeNames.set(k, set); }
113
+ set.add(edgeName);
114
+ }
115
+ };
116
+ const collectEdgePolylineWorld = (edge) => {
117
+ const out = [];
118
+ const cached = edge?.userData?.polylineLocal;
119
+ const isWorld = !!(edge?.userData?.polylineWorld);
120
+ const v = new THREE.Vector3();
121
+ if (Array.isArray(cached) && cached.length >= 2) {
122
+ if (isWorld) return cached.map(p => [p[0], p[1], p[2]]);
123
+ for (const p of cached) { v.set(p[0], p[1], p[2]).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
124
+ return out;
125
+ }
126
+ const posAttr = edge?.geometry?.getAttribute?.('position');
127
+ if (posAttr && posAttr.itemSize === 3 && posAttr.count >= 2) {
128
+ for (let i = 0; i < posAttr.count; i++) { v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
129
+ return out;
130
+ }
131
+ const aStart = edge?.geometry?.attributes?.instanceStart;
132
+ const aEnd = edge?.geometry?.attributes?.instanceEnd;
133
+ if (aStart && aEnd && aStart.itemSize === 3 && aEnd.itemSize === 3 && aStart.count === aEnd.count && aStart.count >= 1) {
134
+ v.set(aStart.getX(0), aStart.getY(0), aStart.getZ(0)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]);
135
+ for (let i = 0; i < aEnd.count; i++) { v.set(aEnd.getX(i), aEnd.getY(i), aEnd.getZ(i)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
136
+ return out;
137
+ }
138
+ return out;
139
+ };
140
+ const edges0 = Array.isArray(firstFace?.edges) ? firstFace.edges : [];
141
+ const firstEdgesWorld = [];
142
+ for (const e of edges0) {
143
+ const name = e?.name || 'EDGE'; // use raw edge name from first face
144
+ const pts = collectEdgePolylineWorld(e);
145
+ if (pts.length >= 2) { seedEdgePoint(name, pts); firstEdgesWorld.push({ name, pts }); }
146
+ }
147
+ // Helper: unify loop form and cleanup
148
+ const closeAndDedup = (pts) => {
149
+ const pA = pts.slice();
150
+ if (pA.length >= 2) {
151
+ const first = pA[0], last = pA[pA.length - 1];
152
+ if (!(first[0] === last[0] && first[1] === last[1] && first[2] === last[2])) pA.push([first[0], first[1], first[2]]);
153
+ }
154
+ for (let i = pA.length - 2; i >= 0; i--) {
155
+ const a = pA[i], b = pA[i + 1];
156
+ if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) pA.splice(i + 1, 1);
157
+ }
158
+ return pA;
159
+ };
160
+
161
+ const getLoops = (face) => {
162
+ const loops = Array.isArray(face?.userData?.boundaryLoopsWorld) ? face.userData.boundaryLoopsWorld : null;
163
+ if (loops && loops.length) return loops.map(l => ({ isHole: !!(l && l.isHole), pts: Array.isArray(l?.pts) ? l.pts : l }));
164
+ // Fallback: approximate a single outer loop by concatenating edge polylines
165
+ const edges = Array.isArray(face?.edges) ? face.edges : [];
166
+ const poly = [];
167
+ for (const e of edges) {
168
+ const pts = collectEdgePolylineWorld(e);
169
+ if (pts.length) poly.push(...pts);
170
+ }
171
+ return poly.length ? [{ isHole: false, pts: poly }] : [];
172
+ };
173
+
174
+ // Helpers to manipulate loops: ensure closed, rotate start, and reverse
175
+ const ensureClosed = (pts) => {
176
+ if (!Array.isArray(pts) || pts.length === 0) return [];
177
+ const out = pts.slice();
178
+ if (out.length >= 2) {
179
+ const a = out[0], b = out[out.length - 1];
180
+ if (!(a[0] === b[0] && a[1] === b[1] && a[2] === b[2])) out.push([a[0], a[1], a[2]]);
181
+ }
182
+ return out;
183
+ };
184
+ const toOpen = (pts) => {
185
+ if (!Array.isArray(pts) || pts.length === 0) return [];
186
+ const out = pts.slice();
187
+ if (out.length >= 2) {
188
+ const a = out[0], b = out[out.length - 1];
189
+ if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) out.pop();
190
+ }
191
+ return out;
192
+ };
193
+ const rotateStart = (ptsClosed, startIndex) => {
194
+ const open = toOpen(ptsClosed);
195
+ const n = open.length;
196
+ if (n === 0) return ensureClosed(ptsClosed);
197
+ const k = ((startIndex % n) + n) % n;
198
+ const rotated = open.slice(k).concat(open.slice(0, k));
199
+ return ensureClosed(rotated);
200
+ };
201
+ const reverseLoop = (ptsClosed) => {
202
+ const open = toOpen(ptsClosed).slice().reverse();
203
+ return ensureClosed(open);
204
+ };
205
+
206
+ // Precompute loops for all faces (closed + dedup)
207
+ const loopsByFace = faces.map(f => getLoops(f).map(l => ({ ...l, pts: closeAndDedup(l.pts) })));
208
+
209
+ // Apply optional start-point alignment and first-loop reversal
210
+ try {
211
+ const refNames = Array.isArray(this.inputParams?.referencePoints) ? this.inputParams.referencePoints : [];
212
+ const scene = partHistory && partHistory.scene ? partHistory.scene : null;
213
+ const refObjs = [];
214
+ if (scene && refNames.length) {
215
+ for (const nm of refNames) {
216
+ try { refObjs.push(scene.getObjectByName(String(nm))); } catch { refObjs.push(null); }
217
+ }
218
+ }
219
+ const tmpV = new THREE.Vector3();
220
+ const dist2 = (a, b) => {
221
+ const dx=a[0]-b[0], dy=a[1]-b[1], dz=a[2]-b[2];
222
+ return dx*dx+dy*dy+dz*dz;
223
+ };
224
+ // For each face that has a provided vertex, rotate the OUTER loop (li=0) to start near that vertex
225
+ for (let i = 0; i < faces.length; i++) {
226
+ const ref = refObjs[i];
227
+ if (!ref || !loopsByFace[i] || !loopsByFace[i].length) continue;
228
+ try {
229
+ tmpV.set(0,0,0); ref.getWorldPosition(tmpV);
230
+ } catch { continue; }
231
+ const loop0 = loopsByFace[i][0];
232
+ const pts = Array.isArray(loop0?.pts) ? loop0.pts : [];
233
+ if (pts.length < 2) continue;
234
+ const rp = [tmpV.x, tmpV.y, tmpV.z];
235
+ // Find nearest point index on the closed loop
236
+ let bestIdx = 0; let bestD = +Infinity;
237
+ for (let j = 0; j < pts.length - 1; j++) { // ignore duplicate last
238
+ const d = dist2(pts[j], rp);
239
+ if (d < bestD) { bestD = d; bestIdx = j; }
240
+ }
241
+ loopsByFace[i][0] = { ...loop0, pts: rotateStart(pts, bestIdx) };
242
+ }
243
+ // Reverse first face's outer loop if requested
244
+ if (this.inputParams?.reverseFirstLoop && loopsByFace[0] && loopsByFace[0][0]) {
245
+ loopsByFace[0][0] = { ...loopsByFace[0][0], pts: reverseLoop(loopsByFace[0][0].pts) };
246
+ }
247
+ } catch (_) { /* alignment optional; ignore errors to keep legacy behavior */ }
248
+
249
+ // Robust pairwise alignment to prevent loft twisting
250
+ // - Resample B loop to match A loop count
251
+ // - Choose best rotation and orientation (reverse or not) to minimize distance
252
+ const toOpenLoop = (pts) => toOpen(pts);
253
+ const segLen = (p, q) => Math.hypot(p[0]-q[0], p[1]-q[1], p[2]-q[2]);
254
+ const ringLength = (open) => {
255
+ const n = open.length; if (n === 0) return 0;
256
+ let L = 0; for (let i=0;i<n;i++){ const a=open[i], b=open[(i+1)%n]; L += segLen(a,b);} return L;
257
+ };
258
+ const lerp = (a,b,t)=>[a[0]+(b[0]-a[0])*t, a[1]+(b[1]-a[1])*t, a[2]+(b[2]-a[2])*t];
259
+ const resampleRing = (open, count) => {
260
+ const n = open.length; if (n === 0 || count <= 0) return [];
261
+ if (n === count) return open.slice();
262
+ const total = ringLength(open);
263
+ if (total <= 0) return open.slice(0, count);
264
+ // build cumulative lengths
265
+ const cum = new Array(n+1); cum[0]=0;
266
+ for (let i=0;i<n;i++){ cum[i+1]=cum[i]+segLen(open[i], open[(i+1)%n]); }
267
+ const out = new Array(count);
268
+ const step = total / count;
269
+ let seg = 0; // current segment index
270
+ for (let k=0;k<count;k++){
271
+ const s = k*step;
272
+ // advance until cum[seg+1] >= s
273
+ while (seg < n && cum[seg+1] < s - 1e-9) seg++;
274
+ const a = open[seg % n];
275
+ const b = open[(seg+1)%n];
276
+ const segStart = cum[seg];
277
+ const segLenTot = Math.max(1e-12, cum[seg+1]-segStart);
278
+ const t = (s - segStart) / segLenTot;
279
+ out[k] = lerp(a,b,Math.max(0,Math.min(1,t)));
280
+ }
281
+ return out;
282
+ };
283
+ const rotateOpen = (open, off)=>{
284
+ const n=open.length; if (!n) return [];
285
+ const k=((off%n)+n)%n; return open.slice(k).concat(open.slice(0,k));
286
+ };
287
+ const reverseOpen = (open)=>open.slice().reverse();
288
+ const sumSqDist = (aOpen, bOpen)=>{
289
+ const n=aOpen.length; let s=0; for(let i=0;i<n;i++){ const p=aOpen[i], q=bOpen[i]; const dx=p[0]-q[0], dy=p[1]-q[1], dz=p[2]-q[2]; s+=dx*dx+dy*dy+dz*dz; } return s;
290
+ };
291
+ const bestAlignBtoA = (aClosed, bClosed) => {
292
+ const aOpen = toOpenLoop(aClosed);
293
+ let bOpen = toOpenLoop(bClosed);
294
+ const n = aOpen.length; if (n === 0 || bOpen.length === 0) return bClosed;
295
+ if (bOpen.length !== n) bOpen = resampleRing(bOpen, n);
296
+ let best = { cost: +Infinity, off: 0, rev: false };
297
+ // same orientation
298
+ for (let off=0; off<n; off++){
299
+ const cand = rotateOpen(bOpen, off);
300
+ const cost = sumSqDist(aOpen, cand);
301
+ if (cost < best.cost) best = { cost, off, rev: false };
302
+ }
303
+ // reversed orientation
304
+ const bRev = reverseOpen(bOpen);
305
+ for (let off=0; off<n; off++){
306
+ const cand = rotateOpen(bRev, off);
307
+ const cost = sumSqDist(aOpen, cand);
308
+ if (cost < best.cost) best = { cost, off, rev: true };
309
+ }
310
+ const chosenOpen = best.rev ? rotateOpen(bRev, best.off) : rotateOpen(bOpen, best.off);
311
+ return ensureClosed(chosenOpen);
312
+ };
313
+
314
+ // Build an aligned copy of loops across faces to avoid twisting
315
+ const loopsAligned = loopsByFace.map(faceLoops => faceLoops.map(l => ({ ...l, pts: l.pts.slice() })));
316
+ for (let i = 0; i < faces.length - 1; i++) {
317
+ const lfA = loopsAligned[i] || [];
318
+ const lfB = loopsAligned[i + 1] || [];
319
+ const L = Math.min(lfA.length, lfB.length);
320
+ for (let li = 0; li < L; li++) {
321
+ try {
322
+ const a = lfA[li].pts;
323
+ const b = lfB[li].pts;
324
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length < 2 || b.length < 2) continue;
325
+ lfB[li] = { ...lfB[li], pts: bestAlignBtoA(a, b) };
326
+ } catch (_) { /* continue */ }
327
+ }
328
+ }
329
+
330
+ const solid = new BREP.Solid();
331
+ solid.name = this.inputParams.featureID || 'Loft';
332
+
333
+ // Caps on first and last faces
334
+ const addCapFromFace = (face, capNamePrefix, reverseStart) => {
335
+ const groups = Array.isArray(face?.userData?.profileGroups) ? face.userData.profileGroups : null;
336
+ if (groups && groups.length) {
337
+ for (const g of groups) {
338
+ const contour2D = g.contour2D || [];
339
+ const holes2D = g.holes2D || [];
340
+ const contourW = g.contourW || [];
341
+ const holesW = g.holesW || [];
342
+ if (contour2D.length < 3 || contourW.length !== contour2D.length) continue;
343
+ const contourV2 = contour2D.map(p => new THREE.Vector2(p[0], p[1]));
344
+ const holesV2 = holes2D.map(h => h.map(p => new THREE.Vector2(p[0], p[1])));
345
+ const tris = THREE.ShapeUtils.triangulateShape(contourV2, holesV2);
346
+ const allW = contourW.concat(...holesW);
347
+ for (const t of tris) {
348
+ const p0 = allW[t[0]], p1 = allW[t[1]], p2 = allW[t[2]];
349
+ if (reverseStart) solid.addTriangle(`${capNamePrefix}_START`, p0, p2, p1);
350
+ else solid.addTriangle(`${capNamePrefix}_END`, p0, p1, p2);
351
+ }
352
+ }
353
+ } else {
354
+ const baseGeom = face.geometry;
355
+ const posAttr = baseGeom?.getAttribute?.('position');
356
+ if (posAttr) {
357
+ const idx = baseGeom.getIndex();
358
+ const hasIndex = !!idx;
359
+ const v = new THREE.Vector3();
360
+ const world = new Array(posAttr.count);
361
+ for (let i = 0; i < posAttr.count; i++) { v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(face.matrixWorld); world[i] = [v.x, v.y, v.z]; }
362
+ const addTri = (i0, i1, i2) => {
363
+ const p0 = world[i0], p1 = world[i1], p2 = world[i2];
364
+ if (reverseStart) solid.addTriangle(`${capNamePrefix}_START`, p0, p2, p1);
365
+ else solid.addTriangle(`${capNamePrefix}_END`, p0, p1, p2);
366
+ };
367
+ if (hasIndex) { for (let i = 0; i < idx.count; i += 3) addTri(idx.getX(i+0)>>>0, idx.getX(i+1)>>>0, idx.getX(i+2)>>>0); }
368
+ else { for (let t = 0; t < (posAttr.count/3|0); t++) addTri(3*t+0, 3*t+1, 3*t+2); }
369
+ }
370
+ }
371
+ };
372
+
373
+ // Add start and end caps
374
+ addCapFromFace(faces[0], `${faces[0].name || 'Face'}`, true);
375
+ addCapFromFace(faces[faces.length - 1], `${faces[faces.length - 1].name || 'Face'}`, false);
376
+
377
+ // Side walls: connect corresponding edges (1:1) between consecutive faces
378
+
379
+ // Order edges around the loop by connectivity; orient to follow boundary
380
+ const pointKey = (p)=> `${p[0].toFixed(5)},${p[1].toFixed(5)},${p[2].toFixed(5)}`;
381
+ const getOrderedOuterEdges = (face) => {
382
+ const all = Array.isArray(face?.edges) ? face.edges.filter(Boolean) : [];
383
+ // Prefer only outer-loop edges when flagged by SketchFeature
384
+ const outers = all.filter(e => !e?.userData?.isHole);
385
+ const list = (outers.length ? outers : all).map(e => ({ e, name: e?.name || 'EDGE', pts: collectEdgePolylineWorld(e) }));
386
+ // Build adjacency via endpoints
387
+ const nodes = new Map(); // key -> array of {idx, end:'start'|'end'}
388
+ const startKey = []; const endKey = [];
389
+ for (let i=0;i<list.length;i++){
390
+ const pts = list[i].pts;
391
+ if (!pts || pts.length < 2) continue;
392
+ const a = pts[0], b = pts[pts.length-1];
393
+ const ka = pointKey(a), kb = pointKey(b);
394
+ startKey[i]=ka; endKey[i]=kb;
395
+ if (!nodes.has(ka)) nodes.set(ka, []); nodes.get(ka).push({ idx:i, end:'start' });
396
+ if (!nodes.has(kb)) nodes.set(kb, []); nodes.get(kb).push({ idx:i, end:'end' });
397
+ }
398
+ if (!list.length) return [];
399
+ // Pick a start edge (arbitrary deterministic)
400
+ let curIdx = 0; let curPts = list[curIdx].pts.slice();
401
+ let curEnd = endKey[curIdx];
402
+ const used = new Set([curIdx]);
403
+ const ordered = [{ name:list[curIdx].name, pts:curPts }];
404
+ let guard = 0;
405
+ while (used.size < list.length && guard++ < list.length*2) {
406
+ const cand = (nodes.get(curEnd) || []).filter(n => !used.has(n.idx));
407
+ if (!cand.length) {
408
+ // Heuristic: find nearest unvisited edge start/end to curEnd
409
+ let best=null, bestD=+Infinity;
410
+ const toVec = (k)=>{ const s=k.split(',').map(Number); return {x:s[0],y:s[1],z:s[2]}; };
411
+ const C = toVec(curEnd);
412
+ for (let i=0;i<list.length;i++){
413
+ if (used.has(i)) continue;
414
+ const a=list[i].pts[0], b=list[i].pts[list[i].pts.length-1];
415
+ const dA=Math.hypot(a[0]-C.x,a[1]-C.y,a[2]-C.z);
416
+ const dB=Math.hypot(b[0]-C.x,b[1]-C.y,b[2]-C.z);
417
+ if (dA<bestD){ bestD=dA; best={idx:i, end:'start'}; }
418
+ if (dB<bestD){ bestD=dB; best={idx:i, end:'end'}; }
419
+ }
420
+ if (!best) break;
421
+ cand.push(best);
422
+ }
423
+ const next = cand[0];
424
+ used.add(next.idx);
425
+ let pts = list[next.idx].pts.slice();
426
+ // Orient to connect from current end
427
+ if (next.end === 'start') {
428
+ // already starts at curEnd
429
+ } else {
430
+ pts = pts.slice().reverse();
431
+ }
432
+ ordered.push({ name:list[next.idx].name, pts });
433
+ curEnd = pointKey(pts[pts.length-1]);
434
+ }
435
+ return ordered;
436
+ };
437
+
438
+ // Rotate ring so a vertex (if provided) lies on the first edge
439
+ const rotateEdgesByRef = (ring, refObj) => {
440
+ if (!refObj || !Array.isArray(ring) || ring.length===0) return ring;
441
+ const pos = new THREE.Vector3();
442
+ try { refObj.getWorldPosition(pos); } catch { return ring; }
443
+ const rp = [pos.x,pos.y,pos.z];
444
+ let bestI=0, bestD=+Infinity;
445
+ for (let i=0;i<ring.length;i++){
446
+ const pts = ring[i].pts; if (!pts || pts.length===0) continue;
447
+ const p = pts[0];
448
+ const d = (p[0]-rp[0])*(p[0]-rp[0])+(p[1]-rp[1])*(p[1]-rp[1])+(p[2]-rp[2])*(p[2]-rp[2]);
449
+ if (d<bestD){bestD=d; bestI=i;}
450
+ }
451
+ return ring.slice(bestI).concat(ring.slice(0,bestI));
452
+ };
453
+
454
+ const reverseEdgeRing = (ring) => ring.slice().reverse().map(seg => ({ name: seg.name, pts: seg.pts.slice().reverse() }));
455
+
456
+ const edgeRingMidpoints = (ring) => ring.map(seg => {
457
+ const pts = seg.pts || []; if (pts.length===0) return [0,0,0];
458
+ const a = pts[0], b = pts[pts.length-1]; return [(a[0]+b[0])/2,(a[1]+b[1])/2,(a[2]+b[2])/2];
459
+ });
460
+ const sumDist = (A,B)=>{ let s=0; for(let i=0;i<A.length;i++){ const p=A[i], q=B[i]; const dx=p[0]-q[0], dy=p[1]-q[1], dz=p[2]-q[2]; s += Math.hypot(dx,dy,dz); } return s; };
461
+ const rotateRing = (ring, off)=>{ const n=ring.length; const k=((off%n)+n)%n; return ring.slice(k).concat(ring.slice(0,k)); };
462
+ const bestAlignRings = (ringA, ringB) => {
463
+ // Try both orientations; choose rotation minimizing mid-point distance
464
+ const midA = edgeRingMidpoints(ringA);
465
+ let best = { rev:false, off:0, cost:+Infinity };
466
+ const tryOrient = (RB, rev) => {
467
+ const midB = edgeRingMidpoints(RB);
468
+ for (let off=0; off<RB.length; off++){
469
+ const rotB = rotateRing(midB, off);
470
+ const cost = sumDist(midA, rotB);
471
+ if (cost < best.cost) best = { rev, off, cost };
472
+ }
473
+ };
474
+ tryOrient(ringB, false);
475
+ tryOrient(reverseEdgeRing(ringB), true);
476
+ return best;
477
+ };
478
+
479
+ // Build reference of start points (optional) to rotate rings
480
+ const refNames = Array.isArray(this.inputParams?.referencePoints) ? this.inputParams.referencePoints : [];
481
+ const scene = partHistory && partHistory.scene ? partHistory.scene : null;
482
+ const refObjs = [];
483
+ if (scene && refNames.length) {
484
+ for (const nm of refNames) { try { refObjs.push(scene.getObjectByName(String(nm))); } catch { refObjs.push(null); } }
485
+ }
486
+ // Build aligned edge rings for all faces relative to the first face's ring
487
+ let refRing = getOrderedOuterEdges(faces[0]);
488
+ if (refObjs[0]) refRing = rotateEdgesByRef(refRing, refObjs[0]);
489
+ if (this.inputParams?.reverseFirstLoop) refRing = reverseEdgeRing(refRing);
490
+ const alignedRings = new Array(faces.length);
491
+ alignedRings[0] = refRing;
492
+ for (let j = 1; j < faces.length; j++) {
493
+ let ring = getOrderedOuterEdges(faces[j]);
494
+ if (refObjs[j]) ring = rotateEdgesByRef(ring, refObjs[j]);
495
+ const al = bestAlignRings(refRing, ring);
496
+ if (al.rev) ring = reverseEdgeRing(ring);
497
+ ring = rotateRing(ring, al.off);
498
+ alignedRings[j] = ring;
499
+ }
500
+
501
+ for (let i = 0; i < faces.length - 1; i++) {
502
+ // Use rings aligned to the first face for consistent naming
503
+ const ringA = alignedRings[i];
504
+ const ringB = alignedRings[i + 1];
505
+ if (!ringA || !ringB || !ringA.length || !ringB.length) continue;
506
+
507
+ const N = Math.min(ringA.length, ringB.length, refRing.length);
508
+ const d2 = (a,b)=>{ const dx=a[0]-b[0], dy=a[1]-b[1], dz=a[2]-b[2]; return dx*dx+dy*dy+dz*dz; };
509
+ const triArea2 = (p0,p1,p2)=>{
510
+ const ux=p1[0]-p0[0], uy=p1[1]-p0[1], uz=p1[2]-p0[2];
511
+ const vx=p2[0]-p0[0], vy=p2[1]-p0[1], vz=p2[2]-p0[2];
512
+ const cx=uy*vz-uz*vy, cy=uz*vx-ux*vz, cz=ux*vy-uy*vx;
513
+ return cx*cx+cy*cy+cz*cz;
514
+ };
515
+ const dedup = (arr)=>{ const out=[]; for(const p of (arr||[])){ const q=out[out.length-1]; if(!q||d2(q,p)>1e-18) out.push(p); } return out; };
516
+ const triangulateStripNoResample = (Araw, Braw, isHole, name) => {
517
+ const A = dedup(Araw), B = dedup(Braw);
518
+ const m = A.length, n = B.length;
519
+ if (m < 2 || n < 2) return;
520
+ let i = 0, j = 0;
521
+ const da = m - 1, db = n - 1;
522
+ // Balanced Bresenham-style walk so steps distribute evenly from both ends
523
+ if (da >= db) {
524
+ let err = (da - db) / 2; // shift start so the switch is centered
525
+ for (let step = 0; step < da; step++) {
526
+ // A step
527
+ const p0 = A[i], p1 = A[i + 1], p2 = B[j];
528
+ if (triArea2(p0, p1, p2) > 1e-24) {
529
+ if (isHole) solid.addTriangle(name, p0, p2, p1);
530
+ else solid.addTriangle(name, p0, p1, p2);
531
+ }
532
+ i++;
533
+ err -= db;
534
+ if (err < 0 && j < db) {
535
+ // B step
536
+ const q0 = A[i], q1 = B[j + 1], q2 = B[j];
537
+ if (triArea2(q0, q1, q2) > 1e-24) {
538
+ if (isHole) solid.addTriangle(name, q0, q1, q2);
539
+ else solid.addTriangle(name, q0, q2, q1);
540
+ }
541
+ j++;
542
+ err += da;
543
+ }
544
+ }
545
+ while (j < db) {
546
+ const r0 = A[i], r1 = B[j + 1], r2 = B[j];
547
+ if (triArea2(r0, r1, r2) > 1e-24) {
548
+ if (isHole) solid.addTriangle(name, r0, r1, r2);
549
+ else solid.addTriangle(name, r0, r2, r1);
550
+ }
551
+ j++;
552
+ }
553
+ } else {
554
+ let err = (db - da) / 2; // symmetric start for B-major
555
+ for (let step = 0; step < db; step++) {
556
+ // B step
557
+ const p0 = A[i], p1 = B[j + 1], p2 = B[j];
558
+ if (triArea2(p0, p1, p2) > 1e-24) {
559
+ if (isHole) solid.addTriangle(name, p0, p1, p2);
560
+ else solid.addTriangle(name, p0, p2, p1);
561
+ }
562
+ j++;
563
+ err -= da;
564
+ if (err < 0 && i < da) {
565
+ // A step
566
+ const q0 = A[i], q1 = A[i + 1], q2 = B[j];
567
+ if (triArea2(q0, q1, q2) > 1e-24) {
568
+ if (isHole) solid.addTriangle(name, q0, q2, q1);
569
+ else solid.addTriangle(name, q0, q1, q2);
570
+ }
571
+ i++;
572
+ err += db;
573
+ }
574
+ }
575
+ while (i < da) {
576
+ const r0 = A[i], r1 = A[i + 1], r2 = B[j];
577
+ if (triArea2(r0, r1, r2) > 1e-24) {
578
+ if (isHole) solid.addTriangle(name, r0, r2, r1);
579
+ else solid.addTriangle(name, r0, r1, r2);
580
+ }
581
+ i++;
582
+ }
583
+ }
584
+ };
585
+ for (let ei = 0; ei < N; ei++) {
586
+ const a = ringA[ei];
587
+ const b = ringB[ei];
588
+ const fname = `${refRing[ei]?.name || 'EDGE'}_LF`;
589
+ triangulateStripNoResample(a.pts, b.pts, false, fname);
590
+ }
591
+ }
592
+
593
+ try { solid.setEpsilon(1e-6); } catch {}
594
+ solid.visualize();
595
+ const effects = await BREP.applyBooleanOperation(partHistory || {}, solid, this.inputParams.boolean, this.inputParams.featureID);
596
+ const booleanRemoved = Array.isArray(effects.removed) ? effects.removed : [];
597
+ const removedArtifacts = [...removed, ...booleanRemoved];
598
+ // Flag removals (sketch parents + boolean effects)
599
+ try { for (const obj of removedArtifacts) { if (obj) obj.__removeFlag = true; } } catch {}
600
+ return {
601
+ added: Array.isArray(effects.added) ? effects.added : [],
602
+ removed: removedArtifacts,
603
+ };
604
+ }
605
+ }