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,8 @@
1
+ export function computeTriangleArea(ax, ay, az, bx, by, bz, cx, cy, cz) {
2
+ const ux = bx - ax, uy = by - ay, uz = bz - az;
3
+ const vx = cx - ax, vy = cy - ay, vz = cz - az;
4
+ const nx = uy * vz - uz * vy;
5
+ const ny = uz * vx - ux * vz;
6
+ const nz = ux * vy - uy * vx;
7
+ return 0.5 * Math.hypot(nx, ny, nz);
8
+ }
@@ -0,0 +1,608 @@
1
+ // CapOpenEndsNonMonotone.js
2
+ // ES6 module. Triangulates non-monotone boundary loops WITHOUT ear-clipping.
3
+ // Algorithm: Project loop to best-fit plane → y-monotone decomposition (sweep) → triangulate each monotone piece.
4
+ // Guarantees: uses ONLY the loop's original vertices (no simplification) to avoid T-junctions; watertight/manifold caps.
5
+
6
+ import * as THREE from 'three';
7
+
8
+ const EPS = 1e-12;
9
+
10
+ // ---------- Vector helpers ----------
11
+ function newellNormal(loop3D) {
12
+ let nx = 0, ny = 0, nz = 0;
13
+ for (let i = 0; i < loop3D.length; i++) {
14
+ const p = loop3D[i], q = loop3D[(i + 1) % loop3D.length];
15
+ nx += (p.y - q.y) * (p.z + q.z);
16
+ ny += (p.z - q.z) * (p.x + q.x);
17
+ nz += (p.x - q.x) * (p.y + q.y);
18
+ }
19
+ const n = new THREE.Vector3(nx, ny, nz);
20
+ if (n.lengthSq() < EPS) {
21
+ // fallback: first non-collinear triple
22
+ for (let i = 0; i < loop3D.length; i++) {
23
+ const a = loop3D[i], b = loop3D[(i + 1) % loop3D.length], c = loop3D[(i + 2) % loop3D.length];
24
+ const v1 = new THREE.Vector3().subVectors(b, a);
25
+ const v2 = new THREE.Vector3().subVectors(c, b);
26
+ const t = new THREE.Vector3().crossVectors(v1, v2);
27
+ if (t.lengthSq() > EPS) return t.normalize();
28
+ }
29
+ return new THREE.Vector3(0, 0, 1);
30
+ }
31
+ return n.normalize();
32
+ }
33
+ function buildLocalFrame(n) {
34
+ const up = Math.abs(n.z) < 0.999 ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(0, 1, 0);
35
+ const u = new THREE.Vector3().crossVectors(up, n).normalize();
36
+ const v = new THREE.Vector3().crossVectors(n, u).normalize();
37
+ return { u, v, n };
38
+ }
39
+ function projectTo2D(loop3D, u, v) {
40
+ return loop3D.map(p => ({ x: p.dot(u), y: p.dot(v) }));
41
+ }
42
+ function areaSigned2D(poly) {
43
+ let a = 0;
44
+ for (let i = 0; i < poly.length; i++) {
45
+ const p = poly[i], q = poly[(i + 1) % poly.length];
46
+ a += p.x * q.y - q.x * p.y;
47
+ }
48
+ return 0.5 * a;
49
+ }
50
+ function isCCW(poly) { return areaSigned2D(poly) > 0; }
51
+ function orient(a, b, c) {
52
+ return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
53
+ }
54
+
55
+ // ---------- Geometry / boundary detection ----------
56
+ function ensureIndexed(geometry) {
57
+ if (!geometry.index) {
58
+ // build a trivial index; we will not merge vertices to avoid changing boundary vertex set
59
+ const pos = geometry.getAttribute('position');
60
+ const index = new (pos.count > 65535 ? Uint32Array : Uint16Array)(pos.count);
61
+ for (let i = 0; i < pos.count; i++) index[i] = i;
62
+ geometry.setIndex(new THREE.BufferAttribute(index, 1));
63
+ }
64
+ return geometry;
65
+ }
66
+ function edgeKey(a,b){ return a<b?`${a}:${b}`:`${b}:${a}`; }
67
+
68
+ function collectBoundaryLoops(geometry) {
69
+ const idx = geometry.index.array;
70
+ const pos = geometry.getAttribute('position');
71
+ const counts = new Map();
72
+ for (let i = 0; i < idx.length; i += 3) {
73
+ const a = idx[i], b = idx[i+1], c = idx[i+2];
74
+ [[a,b],[b,c],[c,a]].forEach(([u,w])=>{
75
+ const k = edgeKey(u,w);
76
+ counts.set(k,(counts.get(k)||0)+1);
77
+ });
78
+ }
79
+ // boundary edges appear exactly once
80
+ const adj = new Map(); // vertex -> neighbors (boundary)
81
+ for (const [k,c] of counts) {
82
+ if (c === 1) {
83
+ const [u,w] = k.split(':').map(Number);
84
+ if (!adj.has(u)) adj.set(u,new Set());
85
+ if (!adj.has(w)) adj.set(w,new Set());
86
+ adj.get(u).add(w);
87
+ adj.get(w).add(u);
88
+ }
89
+ }
90
+ // walk loops
91
+ const visitedE = new Set();
92
+ const loops = [];
93
+ const markEdge = (a,b)=>visitedE.add(`${a}>${b}`), seenEdge=(a,b)=>visitedE.has(`${a}>${b}`);
94
+ for (const start of adj.keys()) {
95
+ // try all incident edges
96
+ for (const n of adj.get(start)) {
97
+ if (seenEdge(start,n)) continue;
98
+ const loop = [];
99
+ let a = start, b = n;
100
+ markEdge(a,b);
101
+ loop.push(a);
102
+ while (true) {
103
+ loop.push(b);
104
+ // choose the next neighbor of b that isn't a and boundary edge not visited
105
+ const nbrs = Array.from(adj.get(b) || []);
106
+ let cNext = undefined;
107
+ for (const c of nbrs) {
108
+ if (c === a) continue;
109
+ if (!seenEdge(b,c)) { cNext = c; break; }
110
+ }
111
+ if (cNext === undefined) break;
112
+ a = b; b = cNext; markEdge(a,b);
113
+ if (b === loop[0]) break;
114
+ }
115
+ if (loop.length >= 3 && loop[0] === loop[loop.length-1]) loop.pop();
116
+ if (loop.length >= 3) loops.push(loop);
117
+ }
118
+ }
119
+ // return as arrays of vertex indices
120
+ return loops;
121
+ }
122
+
123
+ // ---------- Monotone decomposition (Lee–Preparata) ----------
124
+ // We assume a simple polygon with unique vertices (no duplicates) and no self intersections.
125
+ // We do NOT remove collinear vertices.
126
+
127
+ function makeVertices(poly2D) {
128
+ // annotate each vertex with indices of prev/next in circular list
129
+ const n = poly2D.length;
130
+ return poly2D.map((p,i)=>({
131
+ x:p.x, y:p.y, i,
132
+ prev:(i+n-1)%n,
133
+ next:(i+1)%n
134
+ }));
135
+ }
136
+
137
+ function above(p, q) { // p higher than q in sweep order (y descending, x tie by smaller)
138
+ if (Math.abs(p.y - q.y) > EPS) return p.y > q.y;
139
+ return p.x < q.x;
140
+ }
141
+
142
+ function vertexType(vs, i, ccw=true) {
143
+ // Classify as start, end, split, merge, regular
144
+ const v = vs[i], p = vs[v.prev], n = vs[v.next];
145
+
146
+ const isHigherThanPrev = above(v, p);
147
+ const isHigherThanNext = above(v, n);
148
+ const isLowerThanPrev = above(p, v);
149
+ const isLowerThanNext = above(n, v);
150
+
151
+ const angleIsConvex = (ccw ? orient(p, v, n) > 0 : orient(p, v, n) < 0);
152
+
153
+ if (isHigherThanPrev && isHigherThanNext) {
154
+ // local maximum
155
+ return angleIsConvex ? 'start' : 'split';
156
+ }
157
+ if (isLowerThanPrev && isLowerThanNext) {
158
+ // local minimum
159
+ return angleIsConvex ? 'end' : 'merge';
160
+ }
161
+ return 'regular';
162
+ }
163
+
164
+ function xAtYOfEdge(a, b, y) {
165
+ if (Math.abs(a.y - b.y) < EPS) return Math.min(a.x, b.x); // horizontal; arbitrary pick min
166
+ return a.x + (y - a.y) * (b.x - a.x) / (b.y - a.y);
167
+ }
168
+
169
+ class StatusStructure {
170
+ // Balanced tree substitute: we’ll keep a sorted array by x at current sweep y.
171
+ // For typical cap loops (hundreds of verts) this is fine and simpler.
172
+ constructor() { this.edges = []; this.y = Infinity; }
173
+ setY(y) { this.y = y; this.sort(); }
174
+ sort() {
175
+ const y = this.y;
176
+ this.edges.sort((e1,e2)=>{
177
+ const x1 = xAtYOfEdge(e1.a, e1.b, y);
178
+ const x2 = xAtYOfEdge(e2.a, e2.b, y);
179
+ if (Math.abs(x1 - x2) > EPS) return x1 - x2;
180
+ // tie-breaker by ids
181
+ return e1.id - e2.id;
182
+ });
183
+ }
184
+ insert(edge) { this.edges.push(edge); this.sort(); }
185
+ remove(edge) {
186
+ const k = this.edges.findIndex(e=>e.id===edge.id); if (k>=0) this.edges.splice(k,1);
187
+ }
188
+ leftOf(x) {
189
+ // return edge strictly to the left of x (greatest x < x)
190
+ let best = null, bestX = -Infinity;
191
+ for (const e of this.edges) {
192
+ const ex = xAtYOfEdge(e.a, e.b, this.y);
193
+ if (ex < x - EPS && ex > bestX) { best = e; bestX = ex; }
194
+ }
195
+ return best;
196
+ }
197
+ }
198
+
199
+ function monotoneDecompose(poly2D) {
200
+ // Returns a list of diagonals (pairs of vertex indices) that decompose into y-monotone polygons.
201
+ const n = poly2D.length;
202
+ const vs = makeVertices(poly2D);
203
+
204
+ const ccw = isCCW(poly2D); // sweep assumes CCW interior on left; handle both
205
+ const order = [...vs].sort((a,b)=> {
206
+ if (Math.abs(a.y - b.y) > EPS) return b.y - a.y; // descending y
207
+ return a.x - b.x; // ascending x
208
+ });
209
+
210
+ const status = new StatusStructure();
211
+ const helper = new Map(); // edge.id -> vertex index
212
+ let edgeIdSeq = 1;
213
+
214
+ function edgeOf(i, j) { // directed edge vi -> vj
215
+ const a = vs[i], b = vs[j];
216
+ return { a, b, id: edgeIdSeq++, i, j };
217
+ }
218
+
219
+ const diagonals = [];
220
+
221
+ function addDiagonal(i, j) {
222
+ if (i === j) return;
223
+ diagonals.push([i, j]);
224
+ }
225
+
226
+ function addOrUpdateEdge(i, j, h) {
227
+ const e = edgeOf(i, j);
228
+ status.insert(e);
229
+ if (h !== undefined) helper.set(e.id, h);
230
+ return e;
231
+ }
232
+
233
+ function findEdgeLeftOf(x) {
234
+ return status.leftOf(x);
235
+ }
236
+
237
+ for (const v of order) {
238
+ status.setY(v.y + 1e-9); // slightly above v to order properly
239
+
240
+ const t = vertexType(vs, v.i, ccw);
241
+
242
+ if (t === 'start') {
243
+ const e = addOrUpdateEdge(v.i, vs[v.next].i, v.i);
244
+ // helper[e] = v
245
+ }
246
+ else if (t === 'end') {
247
+ // check edge (prev, v)
248
+ // find the edge whose (i -> v.i) exists in status: it’s the incoming edge from prev
249
+ // We locate by scanning (small n), acceptable.
250
+ let found = null, keyId = -1;
251
+ for (const e of status.edges) {
252
+ if (e.j === v.i) { found = e; keyId = e.id; break; }
253
+ }
254
+ if (found) {
255
+ const h = helper.get(keyId);
256
+ if (h !== undefined) {
257
+ if (vertexType(vs, h, ccw) === 'merge') {
258
+ addDiagonal(v.i, h);
259
+ }
260
+ }
261
+ status.remove(found);
262
+ helper.delete(keyId);
263
+ }
264
+ }
265
+ else if (t === 'split') {
266
+ // find left edge of v
267
+ const eLeft = findEdgeLeftOf(v.x);
268
+ if (!eLeft) {
269
+ // degenerate; skip adding diagonal
270
+ } else {
271
+ const h = helper.get(eLeft.id);
272
+ if (h !== undefined) addDiagonal(v.i, h);
273
+ helper.set(eLeft.id, v.i);
274
+ }
275
+ // insert edge (v, next)
276
+ const e = addOrUpdateEdge(v.i, vs[v.next].i, v.i);
277
+ }
278
+ else if (t === 'merge') {
279
+ // handle incoming edge (prev, v)
280
+ let incoming = null, incId = -1;
281
+ for (const e of status.edges) {
282
+ if (e.j === v.i) { incoming = e; incId = e.id; break; }
283
+ }
284
+ if (incoming) {
285
+ const h = helper.get(incId);
286
+ if (h !== undefined) addDiagonal(v.i, h);
287
+ status.remove(incoming);
288
+ helper.delete(incId);
289
+ }
290
+ // find left edge and check its helper
291
+ const eLeft = findEdgeLeftOf(v.x);
292
+ if (eLeft) {
293
+ const h = helper.get(eLeft.id);
294
+ if (h !== undefined && vertexType(vs, h, ccw) === 'merge') {
295
+ addDiagonal(v.i, h);
296
+ }
297
+ helper.set(eLeft.id, v.i);
298
+ }
299
+ }
300
+ else { // regular
301
+ // Determine if interior lies to the left or right of the vertex
302
+ // Using edge (prev -> v) orientation vs sweep direction
303
+ const onLeftChain = above(vs[v.next], v); // if next is below, we are on left chain (descending along next)
304
+ if (onLeftChain) {
305
+ // like END
306
+ let incoming = null, incId = -1;
307
+ for (const e of status.edges) {
308
+ if (e.j === v.i) { incoming = e; incId = e.id; break; }
309
+ }
310
+ if (incoming) {
311
+ const h = helper.get(incId);
312
+ if (h !== undefined && vertexType(vs, h, ccw) === 'merge') {
313
+ addDiagonal(v.i, h);
314
+ }
315
+ status.remove(incoming);
316
+ helper.delete(incId);
317
+ }
318
+ const e = addOrUpdateEdge(v.i, vs[v.next].i, v.i);
319
+ } else {
320
+ // like START side: connect to helper of left edge
321
+ const eLeft = findEdgeLeftOf(v.x);
322
+ if (eLeft) {
323
+ const h = helper.get(eLeft.id);
324
+ if (h !== undefined && vertexType(vs, h, ccw) === 'merge') {
325
+ addDiagonal(v.i, h);
326
+ }
327
+ helper.set(eLeft.id, v.i);
328
+ }
329
+ }
330
+ }
331
+ }
332
+
333
+ return diagonals;
334
+ }
335
+
336
+ // Split polygon by diagonals into monotone pieces (each as index cycle)
337
+ function buildPiecesFromDiagonals(loopIdxs, diagonals) {
338
+ // Construct adjacency of a planar straight-line graph formed by original cycle + diagonals.
339
+ // We then traverse faces that correspond to monotone pieces.
340
+ const n = loopIdxs.length;
341
+ const verts = loopIdxs.slice(); // indices into original mesh vertex buffer
342
+ // map each local vertex i to its two neighbors (single cycle)
343
+ const neighbors = new Map(); // localIndex -> Set of localIndices
344
+ function addEdge(i,j) {
345
+ if (!neighbors.has(i)) neighbors.set(i,new Set());
346
+ if (!neighbors.has(j)) neighbors.set(j,new Set());
347
+ neighbors.get(i).add(j);
348
+ neighbors.get(j).add(i);
349
+ }
350
+ for (let i=0;i<n;i++){
351
+ const a=i, b=(i+1)%n; addEdge(a,b);
352
+ }
353
+ for (const [a,b] of diagonals) addEdge(a,b);
354
+
355
+ // Build all simple cycles (faces). We'll extract only inner faces (skip the outer).
356
+ // Use half-edge traversal with angle sorting to walk around faces CCW.
357
+ // For this we need embedded 2D positions; we’ll supply them at callsite.
358
+ // Here we just produce an edge list; actual face extraction is done in triangulateByMonotone, which knows 2D pts.
359
+ return { neighbors };
360
+ }
361
+
362
+ // Triangulate a single y-monotone polygon (given as sequence of local indices and its 2D coords)
363
+ function triangulateMonotonePiece(indices, pts) {
364
+ // pts: array of {x,y} in local order (indices map to indices in this array)
365
+ // Algorithm: sort vertices by y (desc), then use stack on left/right chains.
366
+ // First, ensure the polygon is y-monotone and we have top/bottom.
367
+ const n = indices.length;
368
+ if (n < 3) return [];
369
+ const poly = indices.map(i => pts[i]);
370
+
371
+ // find top and bottom (max y and min y; break ties by x)
372
+ let top = 0, bottom = 0;
373
+ for (let k=1;k<n;k++){
374
+ const p = poly[k], t = poly[top], b = poly[bottom];
375
+ if (p.y > t.y + EPS || (Math.abs(p.y-t.y)<=EPS && p.x < t.x)) top = k;
376
+ if (p.y < b.y - EPS || (Math.abs(p.y-b.y)<=EPS && p.x > b.x)) bottom = k;
377
+ }
378
+
379
+ // split into left/right chains walking from top to bottom along both directions
380
+ const nextIdx = i => (i+1)%n, prevIdx = i => (i+n-1)%n;
381
+ const left = []; const right = [];
382
+ // walk clockwise from top to bottom
383
+ let i = top;
384
+ while (i !== bottom) { i = nextIdx(i); right.push(i); }
385
+ // walk counter-clockwise
386
+ i = top;
387
+ while (i !== bottom) { i = prevIdx(i); left.push(i); }
388
+
389
+ const chain = new Array(n).fill(''); // 'L' or 'R'
390
+ chain[top] = 'L'; chain[bottom] = 'L';
391
+ for (const k of left) chain[k]='L';
392
+ for (const k of right) chain[k]='R';
393
+
394
+ // sort vertices by y desc (x asc)
395
+ const order = [];
396
+ for (let k=0;k<n;k++) order.push(k);
397
+ order.sort((a,b)=>{
398
+ const A = poly[a], B = poly[b];
399
+ if (Math.abs(A.y-B.y)>EPS) return B.y - A.y;
400
+ return A.x - B.x;
401
+ });
402
+
403
+ const stack = [order[0], order[1]];
404
+ const tris = [];
405
+
406
+ for (let s=2; s<order.length-1; s++){
407
+ const cur = order[s];
408
+ if (chain[cur] !== chain[stack[stack.length-1]]) {
409
+ // different chain: pop all and form fan
410
+ for (let k=0; k<stack.length-1; k++){
411
+ const a = stack[k], b = stack[k+1], c = cur;
412
+ // ensure correct orientation (assume original polygon orientation)
413
+ tris.push([indices[a], indices[b], indices[c]]);
414
+ }
415
+ stack.length = 0;
416
+ stack.push(order[s-1], cur);
417
+ } else {
418
+ // same chain: pop while angle is convex
419
+ let last = stack.pop();
420
+ while (stack.length > 0) {
421
+ const a = stack[stack.length-1], b = last, c = cur;
422
+ const o = orient(poly[a], poly[b], poly[c]);
423
+ // For CCW polygon, we keep making triangles while we have a "right turn" on left chain or "left turn" on right chain.
424
+ const isConvex = (chain[cur]==='L') ? (o > EPS) : (o < -EPS);
425
+ if (!isConvex) break;
426
+ tris.push([indices[a], indices[b], indices[c]]);
427
+ last = stack.pop();
428
+ }
429
+ stack.push(last, cur);
430
+ }
431
+ }
432
+ // connect remaining
433
+ const lastV = order[order.length-1];
434
+ for (let k=0; k<stack.length-1; k++){
435
+ const a = stack[k], b = stack[k+1], c = lastV;
436
+ tris.push([indices[a], indices[b], indices[c]]);
437
+ }
438
+ return tris;
439
+ }
440
+
441
+ // Build monotone pieces (faces) from polygon + diagonals and triangulate them
442
+ function triangulateByMonotone(loop2D, diagonals) {
443
+ // Build graph
444
+ const localIdx = loop2D.map((_,i)=>i);
445
+ const { neighbors } = buildPiecesFromDiagonals(localIdx, diagonals);
446
+
447
+ // Convert neighbor graph into simple faces using angular sweep per vertex.
448
+ // We’ll enumerate half-edges (u->v), and for each not yet visited, walk the face by always taking the next edge with smallest clockwise angle.
449
+ const n = loop2D.length;
450
+ const visitedHE = new Set();
451
+ function heKey(u,v){return `${u}>${v}`;}
452
+
453
+ // Precompute angle-sorted neighbors around each vertex
454
+ const angNbrs = new Map();
455
+ for (let i=0;i<n;i++){
456
+ const p = loop2D[i];
457
+ const nbrs = Array.from(neighbors.get(i)||[]);
458
+ // sort by angle around p (atan2), clockwise from positive x
459
+ nbrs.sort((a,b)=>{
460
+ const va = {x: loop2D[a].x - p.x, y: loop2D[a].y - p.y};
461
+ const vb = {x: loop2D[b].x - p.x, y: loop2D[b].y - p.y};
462
+ const aa = Math.atan2(va.y, va.x);
463
+ const ab = Math.atan2(vb.y, vb.x);
464
+ return ab - aa; // descending = clockwise
465
+ });
466
+ angNbrs.set(i, nbrs);
467
+ }
468
+
469
+ const faces = [];
470
+
471
+ for (let u=0; u<n; u++){
472
+ const nbrs = angNbrs.get(u)||[];
473
+ for (const v of nbrs) {
474
+ const key = heKey(u,v);
475
+ if (visitedHE.has(key)) continue;
476
+ // start walking face keeping it on the left (CW next-edge rule as we sorted neighbors CW)
477
+ const face = [];
478
+ let a = u, b = v;
479
+ while (true){
480
+ visitedHE.add(heKey(a,b));
481
+ face.push(a);
482
+ // choose next edge at b: find a in its CW list, take the previous neighbor (turn right) to keep interior on left
483
+ const list = angNbrs.get(b)||[];
484
+ const k = list.indexOf(a);
485
+ const nextIdx = (k - 1 + list.length) % list.length;
486
+ const c = list[nextIdx];
487
+ a = b; b = c;
488
+ if (a === u && b === v) break;
489
+ if (face.length > n + diagonals.length*2 + 5) break; // safety
490
+ }
491
+ if (face.length >= 3) {
492
+ // Remove duplicate last if present
493
+ if (face[0] === face[face.length-1]) face.pop();
494
+ // Keep only inner faces: heuristic by signed area (same orientation as original polygon)
495
+ const ar = areaSigned2D(face.map(i=>loop2D[i]));
496
+ if (Math.abs(ar) > EPS) faces.push(face);
497
+ }
498
+ }
499
+ }
500
+
501
+ // Triangulate each face assuming it's monotone (by construction)
502
+ const tris = [];
503
+ for (const f of faces) {
504
+ const t = triangulateMonotonePiece(f, loop2D);
505
+ for (const tri of t) tris.push(tri);
506
+ }
507
+ return tris;
508
+ }
509
+
510
+ // ---------- End-to-end per loop ----------
511
+ function triangulateNonMonotoneLoop(loop3D, loopIdxMesh) {
512
+ const n = loop3D.length;
513
+ const normal = newellNormal(loop3D);
514
+ const { u, v } = buildLocalFrame(normal);
515
+ const loop2D = projectTo2D(loop3D, u, v);
516
+
517
+ // Ensure polygon is CCW in (u,v) plane; if not, reverse consistently
518
+ if (!isCCW(loop2D)) {
519
+ loop2D.reverse();
520
+ loopIdxMesh = loopIdxMesh.slice().reverse();
521
+ }
522
+
523
+ // Decompose into y-monotone pieces via diagonals (indices are local 0..n-1)
524
+ const diagonals = monotoneDecompose(loop2D);
525
+
526
+ // Triangulate all monotone pieces; triangles are local indices - map back to mesh indices
527
+ const localTris = triangulateByMonotone(loop2D, diagonals);
528
+ const meshTris = [];
529
+ for (const [a,b,c] of localTris) {
530
+ meshTris.push(loopIdxMesh[a], loopIdxMesh[b], loopIdxMesh[c]);
531
+ }
532
+
533
+ return { tris: meshTris, planeNormal: normal };
534
+ }
535
+
536
+ // ---------- Winding selection vs mesh outside ----------
537
+ function computeAverageBoundaryNormal(geometry, loopIdx) {
538
+ const nAttr = geometry.getAttribute('normal');
539
+ if (nAttr) {
540
+ const acc = new THREE.Vector3();
541
+ const t = new THREE.Vector3();
542
+ for (const i of loopIdx) {
543
+ t.set(nAttr.getX(i), nAttr.getY(i), nAttr.getZ(i));
544
+ acc.add(t);
545
+ }
546
+ if (acc.lengthSq() > EPS) return acc.normalize();
547
+ }
548
+ // fallback: Newell on loop positions
549
+ const pos = geometry.getAttribute('position');
550
+ const loop3D = loopIdx.map(i => new THREE.Vector3(pos.getX(i), pos.getY(i), pos.getZ(i)));
551
+ return newellNormal(loop3D);
552
+ }
553
+
554
+ function addTrianglesToGeometry(geometry, trisToAppend) {
555
+ const oldIndex = geometry.index.array;
556
+ const ctor = oldIndex.constructor;
557
+ const need32 = (oldIndex.length + trisToAppend.length) > 65535 && !(oldIndex instanceof Uint32Array);
558
+ const finalCtor = need32 ? Uint32Array : ctor;
559
+ const newIndex = new finalCtor(oldIndex.length + trisToAppend.length);
560
+ newIndex.set(oldIndex, 0);
561
+ newIndex.set(trisToAppend, oldIndex.length);
562
+ geometry.setIndex(new THREE.BufferAttribute(newIndex, 1));
563
+ }
564
+
565
+ // ---------- Public API ----------
566
+ export function capOpenEndsNonMonotone(mesh, { recomputeNormals = true } = {}) {
567
+ if (!mesh || !mesh.geometry) throw new Error('capOpenEndsNonMonotone: mesh with geometry is required.');
568
+ const geometry = ensureIndexed(mesh.geometry);
569
+ mesh.geometry = geometry;
570
+
571
+ const pos = geometry.getAttribute('position');
572
+ const loops = collectBoundaryLoops(geometry);
573
+ if (loops.length === 0) return mesh;
574
+
575
+ const allCapTris = [];
576
+
577
+ for (const loopIdx of loops) {
578
+ // gather 3D points
579
+ const loop3D = loopIdx.map(i => new THREE.Vector3(pos.getX(i), pos.getY(i), pos.getZ(i)));
580
+ const { tris, planeNormal } = triangulateNonMonotoneLoop(loop3D, loopIdx);
581
+
582
+ if (tris.length === 0) {
583
+ console.warn('capOpenEndsNonMonotone: triangulation produced no triangles for a loop (possibly self-intersecting).');
584
+ continue;
585
+ }
586
+
587
+ // Choose winding so cap normal aligns with the "outside"
588
+ const avgBoundaryNormal = computeAverageBoundaryNormal(geometry, loopIdx);
589
+ const flip = planeNormal.dot(avgBoundaryNormal) < 0;
590
+
591
+ if (flip) {
592
+ for (let i=0;i<tris.length;i+=3){
593
+ const a = tris[i], b = tris[i+1], c = tris[i+2];
594
+ allCapTris.push(a, c, b);
595
+ }
596
+ } else {
597
+ allCapTris.push(...tris);
598
+ }
599
+ }
600
+
601
+ if (allCapTris.length) {
602
+ addTrianglesToGeometry(geometry, allCapTris);
603
+ if (recomputeNormals) geometry.computeVertexNormals();
604
+ geometry.computeBoundingBox();
605
+ geometry.computeBoundingSphere();
606
+ }
607
+ return mesh;
608
+ }