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,971 @@
1
+ // Shared clean-room trace + smoothing utilities for ImageToFace
2
+
3
+ export function traceImageDataToPolylines(imageData, options = {}) {
4
+ const opt = {
5
+ threshold: 128,
6
+ mode: "luma", // "alpha" | "luma" | "luma+alpha"
7
+ invert: false,
8
+ minArea: 0,
9
+ mergeCollinear: true,
10
+ simplify: 0,
11
+ includeOrientation: false,
12
+ ...options,
13
+ };
14
+
15
+ const w = imageData?.width | 0;
16
+ const h = imageData?.height | 0;
17
+ if (!w || !h) return [];
18
+
19
+ const mask = binarize(imageData, w, h, opt);
20
+ const edges = buildBoundaryEdges(mask, w, h);
21
+ const loops = stitchEdgesToLoops(edges);
22
+
23
+ const out = [];
24
+ for (const loop of loops) {
25
+ let poly = loop;
26
+
27
+ if (opt.mergeCollinear) poly = removeCollinear(poly);
28
+ const area = polygonArea(poly);
29
+
30
+ if (Math.abs(area) < opt.minArea) continue;
31
+
32
+ if (opt.simplify > 0 && poly.length >= 4) {
33
+ poly = rdpClosed(poly, opt.simplify);
34
+ if (opt.mergeCollinear) poly = removeCollinear(poly);
35
+ }
36
+
37
+ if (poly.length >= 3) {
38
+ out.push(opt.includeOrientation ? { polyline: poly, area } : poly);
39
+ }
40
+ }
41
+
42
+ return out;
43
+ }
44
+
45
+ function binarize(imageData, w, h, opt) {
46
+ const src = imageData.data;
47
+ const mask = new Uint8Array(w * h);
48
+
49
+ for (let i = 0, p = 0; i < src.length; i += 4, p++) {
50
+ const r = src[i + 0];
51
+ const g = src[i + 1];
52
+ const b = src[i + 2];
53
+ const a = src[i + 3];
54
+
55
+ const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
56
+
57
+ let fg;
58
+ if (opt.mode === "alpha") {
59
+ fg = a >= opt.threshold;
60
+ } else if (opt.mode === "luma+alpha") {
61
+ fg = a > 0 && luma < opt.threshold;
62
+ } else {
63
+ fg = luma < opt.threshold;
64
+ }
65
+
66
+ if (opt.invert) fg = !fg;
67
+ mask[p] = fg ? 1 : 0;
68
+ }
69
+
70
+ return mask;
71
+ }
72
+
73
+ function at(mask, w, h, x, y) {
74
+ if (x < 0 || y < 0 || x >= w || y >= h) return 0;
75
+ return mask[y * w + x];
76
+ }
77
+
78
+ function buildBoundaryEdges(mask, w, h) {
79
+ const edges = [];
80
+
81
+ for (let y = 0; y < h; y++) {
82
+ for (let x = 0; x < w; x++) {
83
+ if (!at(mask, w, h, x, y)) continue;
84
+
85
+ if (!at(mask, w, h, x, y - 1)) edges.push({ sx: x, sy: y, ex: x + 1, ey: y, dir: 0 });
86
+ if (!at(mask, w, h, x + 1, y)) edges.push({ sx: x + 1, sy: y, ex: x + 1, ey: y + 1, dir: 1 });
87
+ if (!at(mask, w, h, x, y + 1)) edges.push({ sx: x + 1, sy: y + 1, ex: x, ey: y + 1, dir: 2 });
88
+ if (!at(mask, w, h, x - 1, y)) edges.push({ sx: x, sy: y + 1, ex: x, ey: y, dir: 3 });
89
+ }
90
+ }
91
+
92
+ return edges;
93
+ }
94
+
95
+ function stitchEdgesToLoops(edges) {
96
+ const startMap = new Map();
97
+ const visited = new Uint8Array(edges.length);
98
+
99
+ for (let i = 0; i < edges.length; i++) {
100
+ const e = edges[i];
101
+ const k = vkey(e.sx, e.sy);
102
+ let arr = startMap.get(k);
103
+ if (!arr) startMap.set(k, (arr = []));
104
+ arr.push(i);
105
+ }
106
+
107
+ const loops = [];
108
+
109
+ for (let i = 0; i < edges.length; i++) {
110
+ if (visited[i]) continue;
111
+
112
+ const loop = [];
113
+ let currEdge = edges[i];
114
+ visited[i] = 1;
115
+
116
+ const startX = currEdge.sx;
117
+ const startY = currEdge.sy;
118
+
119
+ loop.push({ x: startX, y: startY });
120
+
121
+ let cx = currEdge.ex;
122
+ let cy = currEdge.ey;
123
+ let dir = currEdge.dir;
124
+ const maxSteps = edges.length + 10;
125
+
126
+ for (let steps = 0; steps < maxSteps; steps++) {
127
+ if (cx === startX && cy === startY) break;
128
+
129
+ loop.push({ x: cx, y: cy });
130
+
131
+ const nextIndex = pickNextEdge(startMap, edges, visited, cx, cy, dir);
132
+ if (nextIndex < 0) {
133
+ loop.length = 0;
134
+ break;
135
+ }
136
+
137
+ const ne = edges[nextIndex];
138
+ visited[nextIndex] = 1;
139
+
140
+ cx = ne.ex;
141
+ cy = ne.ey;
142
+ dir = ne.dir;
143
+ }
144
+
145
+ if (loop.length >= 3 && (loop[0].x !== loop[loop.length - 1].x || loop[0].y !== loop[loop.length - 1].y)) {
146
+ loops.push(loop);
147
+ }
148
+ }
149
+
150
+ return loops;
151
+ }
152
+
153
+ function pickNextEdge(startMap, edges, visited, vx, vy, prevDir) {
154
+ const k = vkey(vx, vy);
155
+ const candidates = startMap.get(k);
156
+ if (!candidates || candidates.length === 0) return -1;
157
+
158
+ const preferred = [
159
+ (prevDir + 1) & 3,
160
+ prevDir,
161
+ (prevDir + 3) & 3,
162
+ (prevDir + 2) & 3,
163
+ ];
164
+
165
+ let bestIdx = -1;
166
+ let bestRank = 999;
167
+
168
+ for (const ei of candidates) {
169
+ if (visited[ei]) continue;
170
+ const d = edges[ei].dir;
171
+ const rank = preferred.indexOf(d);
172
+ if (rank >= 0 && rank < bestRank) {
173
+ bestRank = rank;
174
+ bestIdx = ei;
175
+ if (bestRank === 0) break;
176
+ }
177
+ }
178
+
179
+ return bestIdx;
180
+ }
181
+
182
+ function vkey(x, y) {
183
+ return `${x},${y}`;
184
+ }
185
+
186
+ function removeCollinear(poly) {
187
+ if (poly.length < 4) return poly;
188
+
189
+ const out = [];
190
+ const n = poly.length;
191
+
192
+ for (let i = 0; i < n; i++) {
193
+ const a = poly[(i - 1 + n) % n];
194
+ const b = poly[i];
195
+ const c = poly[(i + 1) % n];
196
+
197
+ const abx = b.x - a.x, aby = b.y - a.y;
198
+ const bcx = c.x - b.x, bcy = c.y - b.y;
199
+
200
+ const cross = abx * bcy - aby * bcx;
201
+ if (cross !== 0) {
202
+ out.push(b);
203
+ continue;
204
+ }
205
+
206
+ if ((abx === 0 && aby === 0) || (bcx === 0 && bcy === 0)) out.push(b);
207
+ }
208
+
209
+ return out.length >= 3 ? out : poly;
210
+ }
211
+
212
+ function polygonArea(poly) {
213
+ let a = 0;
214
+ const n = poly.length;
215
+ for (let i = 0; i < n; i++) {
216
+ const p = poly[i];
217
+ const q = poly[(i + 1) % n];
218
+ a += p.x * q.y - q.x * p.y;
219
+ }
220
+ return a / 2;
221
+ }
222
+
223
+ function rdpClosed(poly, eps) {
224
+ if (poly.length < 4) return poly;
225
+
226
+ const centroid = poly.reduce((acc, p) => ({ x: acc.x + p.x, y: acc.y + p.y }), { x: 0, y: 0 });
227
+ centroid.x /= poly.length;
228
+ centroid.y /= poly.length;
229
+
230
+ let split = 0;
231
+ let best = -1;
232
+ for (let i = 0; i < poly.length; i++) {
233
+ const dx = poly[i].x - centroid.x;
234
+ const dy = poly[i].y - centroid.y;
235
+ const d2 = dx * dx + dy * dy;
236
+ if (d2 > best) {
237
+ best = d2;
238
+ split = i;
239
+ }
240
+ }
241
+
242
+ const open = poly.slice(split).concat(poly.slice(0, split + 1));
243
+ const simplified = rdpOpen(open, eps);
244
+
245
+ simplified.pop();
246
+
247
+ const rotated = simplified.slice(-split).concat(simplified.slice(0, -split));
248
+ return rotated;
249
+ }
250
+
251
+ function rdpOpen(points, eps) {
252
+ if (points.length <= 2) return points;
253
+
254
+ const keep = new Uint8Array(points.length);
255
+ keep[0] = 1;
256
+ keep[points.length - 1] = 1;
257
+
258
+ const stack = [[0, points.length - 1]];
259
+ const eps2 = eps * eps;
260
+
261
+ while (stack.length) {
262
+ const [a, b] = stack.pop();
263
+ let maxDist2 = -1;
264
+ let idx = -1;
265
+
266
+ const p1 = points[a];
267
+ const p2 = points[b];
268
+
269
+ for (let i = a + 1; i < b; i++) {
270
+ const d2 = pointToSegmentDist2(points[i], p1, p2);
271
+ if (d2 > maxDist2) {
272
+ maxDist2 = d2;
273
+ idx = i;
274
+ }
275
+ }
276
+
277
+ if (maxDist2 > eps2 && idx !== -1) {
278
+ keep[idx] = 1;
279
+ stack.push([a, idx], [idx, b]);
280
+ }
281
+ }
282
+
283
+ const out = [];
284
+ for (let i = 0; i < points.length; i++) if (keep[i]) out.push(points[i]);
285
+ return out;
286
+ }
287
+
288
+ function pointToSegmentDist2(p, a, b) {
289
+ const abx = b.x - a.x;
290
+ const aby = b.y - a.y;
291
+ const apx = p.x - a.x;
292
+ const apy = p.y - a.y;
293
+
294
+ const abLen2 = abx * abx + aby * aby;
295
+ if (abLen2 === 0) return apx * apx + apy * apy;
296
+
297
+ let t = (apx * abx + apy * aby) / abLen2;
298
+ t = Math.max(0, Math.min(1, t));
299
+
300
+ const cx = a.x + t * abx;
301
+ const cy = a.y + t * aby;
302
+
303
+ const dx = p.x - cx;
304
+ const dy = p.y - cy;
305
+ return dx * dx + dy * dy;
306
+ }
307
+
308
+ function pointToSegmentDist2XY(px, py, ax, ay, bx, by) {
309
+ const abx = bx - ax;
310
+ const aby = by - ay;
311
+ const apx = px - ax;
312
+ const apy = py - ay;
313
+
314
+ const abLen2 = abx * abx + aby * aby;
315
+ if (abLen2 === 0) return apx * apx + apy * apy;
316
+
317
+ let t = (apx * abx + apy * aby) / abLen2;
318
+ t = Math.max(0, Math.min(1, t));
319
+
320
+ const cx = ax + t * abx;
321
+ const cy = ay + t * aby;
322
+
323
+ const dx = px - cx;
324
+ const dy = py - cy;
325
+ return dx * dx + dy * dy;
326
+ }
327
+
328
+ export function rdp(points, epsilon) {
329
+ if (points.length <= 3) return points.slice();
330
+ const open = points.slice(0, points.length - 1);
331
+ const simplified = rdpRecursive(open, epsilon);
332
+ if (!simplified.length) return points.slice();
333
+ simplified.push([simplified[0][0], simplified[0][1]]);
334
+ return simplified;
335
+ }
336
+
337
+ function rdpRecursive(points, epsilon) {
338
+ if (points.length < 3) return points.slice();
339
+ const p0 = points[0];
340
+ const pN = points[points.length - 1];
341
+ let index = -1; let dmax = 0;
342
+ for (let i = 1; i < points.length - 1; i++) {
343
+ const d = pointLineDist(points[i], p0, pN);
344
+ if (d > dmax) { index = i; dmax = d; }
345
+ }
346
+ if (dmax > epsilon) {
347
+ const left = rdpRecursive(points.slice(0, index + 1), epsilon);
348
+ const right = rdpRecursive(points.slice(index), epsilon);
349
+ return left.slice(0, left.length - 1).concat(right);
350
+ } else {
351
+ return [p0, pN];
352
+ }
353
+ }
354
+
355
+ function pointLineDist(p, a, b) {
356
+ const x = p[0], y = p[1];
357
+ const x1 = a[0], y1 = a[1];
358
+ const x2 = b[0], y2 = b[1];
359
+ const A = x - x1; const B = y - y1; const C = x2 - x1; const D = y2 - y1;
360
+ const dot = A * C + B * D;
361
+ const len2 = C * C + D * D;
362
+ const t = len2 > 0 ? Math.max(0, Math.min(1, dot / len2)) : 0;
363
+ const px = x1 + t * C; const py = y1 + t * D;
364
+ const dx = x - px; const dy = y - py;
365
+ return Math.hypot(dx, dy);
366
+ }
367
+
368
+ export function applyCurveFit(loops, { tolerance = 0.75, cornerThresholdDeg = 70, iterations = 3 } = {}) {
369
+ const tol = Math.max(1e-4, tolerance);
370
+ const angThresh = Math.max(0, Math.min(180, cornerThresholdDeg)) * (Math.PI / 180);
371
+
372
+ const fitLoop = (loop) => {
373
+ if (!Array.isArray(loop) || loop.length < 3) return loop.slice();
374
+ const ring = (loop[0][0] === loop[loop.length - 1][0] && loop[0][1] === loop[loop.length - 1][1]) ? loop.slice(0, -1) : loop.slice();
375
+ if (ring.length < 3) return loop.slice();
376
+
377
+ const corners = findCorners(ring, angThresh);
378
+ let smoothed;
379
+ if (corners.length === 0) {
380
+ smoothed = chaikinClosed(ring, iterations);
381
+ } else {
382
+ smoothed = smoothWithAnchors(ring, corners, iterations);
383
+ }
384
+
385
+ let closed = smoothed.slice();
386
+ if (closed[0][0] !== closed[closed.length - 1][0] || closed[0][1] !== closed[closed.length - 1][1]) {
387
+ closed.push([closed[0][0], closed[0][1]]);
388
+ }
389
+ closed = rdp(closed, tol);
390
+ if (closed[0][0] !== closed[closed.length - 1][0] || closed[0][1] !== closed[closed.length - 1][1]) {
391
+ closed.push([closed[0][0], closed[0][1]]);
392
+ }
393
+ return closed;
394
+ };
395
+
396
+ return loops.map((l) => fitLoop(l));
397
+ }
398
+
399
+ function cleanLoop2D(loop, eps) {
400
+ if (!Array.isArray(loop)) return [];
401
+ const out = [];
402
+ const n = loop.length;
403
+ for (let i = 0; i < n; i++) {
404
+ const p = loop[i];
405
+ if (!Array.isArray(p) || p.length < 2) continue;
406
+ const x = Number(p[0]);
407
+ const y = Number(p[1]);
408
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
409
+ if (!out.length) {
410
+ out.push([x, y]);
411
+ continue;
412
+ }
413
+ const prev = out[out.length - 1];
414
+ const dx = x - prev[0];
415
+ const dy = y - prev[1];
416
+ if ((dx * dx + dy * dy) <= eps * eps) continue;
417
+ out.push([x, y]);
418
+ }
419
+ if (out.length >= 2) {
420
+ const first = out[0];
421
+ const last = out[out.length - 1];
422
+ const dx = first[0] - last[0];
423
+ const dy = first[1] - last[1];
424
+ if ((dx * dx + dy * dy) <= eps * eps) out.pop();
425
+ }
426
+ return out;
427
+ }
428
+
429
+ function loopSelfIntersects(loop, eps) {
430
+ const ring = cleanLoop2D(loop, eps);
431
+ const n = ring.length;
432
+ if (n < 4) return false;
433
+ const orient = (ax, ay, bx, by, cx, cy) => (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
434
+ const onSeg = (ax, ay, bx, by, cx, cy) =>
435
+ cx >= Math.min(ax, bx) - eps && cx <= Math.max(ax, bx) + eps
436
+ && cy >= Math.min(ay, by) - eps && cy <= Math.max(ay, by) + eps;
437
+ const segsIntersect = (a, b, c, d) => {
438
+ const o1 = orient(a[0], a[1], b[0], b[1], c[0], c[1]);
439
+ const o2 = orient(a[0], a[1], b[0], b[1], d[0], d[1]);
440
+ const o3 = orient(c[0], c[1], d[0], d[1], a[0], a[1]);
441
+ const o4 = orient(c[0], c[1], d[0], d[1], b[0], b[1]);
442
+ if (Math.abs(o1) <= eps && onSeg(a[0], a[1], b[0], b[1], c[0], c[1])) return true;
443
+ if (Math.abs(o2) <= eps && onSeg(a[0], a[1], b[0], b[1], d[0], d[1])) return true;
444
+ if (Math.abs(o3) <= eps && onSeg(c[0], c[1], d[0], d[1], a[0], a[1])) return true;
445
+ if (Math.abs(o4) <= eps && onSeg(c[0], c[1], d[0], d[1], b[0], b[1])) return true;
446
+ return (o1 * o2 < -eps) && (o3 * o4 < -eps);
447
+ };
448
+ for (let i = 0; i < n; i++) {
449
+ const a = ring[i];
450
+ const b = ring[(i + 1) % n];
451
+ for (let j = i + 1; j < n; j++) {
452
+ const isAdjacent = (j === i) || (j === i + 1) || (i === 0 && j === n - 1);
453
+ if (isAdjacent) continue;
454
+ const c = ring[j];
455
+ const d = ring[(j + 1) % n];
456
+ if (segsIntersect(a, b, c, d)) return true;
457
+ }
458
+ }
459
+ return false;
460
+ }
461
+
462
+ function loopAreaAbs(loop) {
463
+ const ring = cleanLoop2D(loop, 1e-12);
464
+ const n = ring.length;
465
+ if (n < 3) return 0;
466
+ let a = 0;
467
+ for (let i = 0; i < n; i++) {
468
+ const p = ring[i];
469
+ const q = ring[(i + 1) % n];
470
+ a += p[0] * q[1] - q[0] * p[1];
471
+ }
472
+ return Math.abs(a * 0.5);
473
+ }
474
+
475
+ function loopsIntersect2D(loopA, loopB, eps) {
476
+ const a = cleanLoop2D(loopA, eps);
477
+ const b = cleanLoop2D(loopB, eps);
478
+ if (a.length < 2 || b.length < 2) return false;
479
+ const orient = (ax, ay, bx, by, cx, cy) => (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
480
+ const onSeg = (ax, ay, bx, by, cx, cy) =>
481
+ cx >= Math.min(ax, bx) - eps && cx <= Math.max(ax, bx) + eps
482
+ && cy >= Math.min(ay, by) - eps && cy <= Math.max(ay, by) + eps;
483
+ const segsIntersect = (p1, p2, p3, p4) => {
484
+ const o1 = orient(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
485
+ const o2 = orient(p1[0], p1[1], p2[0], p2[1], p4[0], p4[1]);
486
+ const o3 = orient(p3[0], p3[1], p4[0], p4[1], p1[0], p1[1]);
487
+ const o4 = orient(p3[0], p3[1], p4[0], p4[1], p2[0], p2[1]);
488
+ if (Math.abs(o1) <= eps && onSeg(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1])) return true;
489
+ if (Math.abs(o2) <= eps && onSeg(p1[0], p1[1], p2[0], p2[1], p4[0], p4[1])) return true;
490
+ if (Math.abs(o3) <= eps && onSeg(p3[0], p3[1], p4[0], p4[1], p1[0], p1[1])) return true;
491
+ if (Math.abs(o4) <= eps && onSeg(p3[0], p3[1], p4[0], p4[1], p2[0], p2[1])) return true;
492
+ return (o1 * o2 < -eps) && (o3 * o4 < -eps);
493
+ };
494
+ const na = a.length;
495
+ const nb = b.length;
496
+ for (let i = 0; i < na; i++) {
497
+ const a0 = a[i];
498
+ const a1 = a[(i + 1) % na];
499
+ for (let j = 0; j < nb; j++) {
500
+ const b0 = b[j];
501
+ const b1 = b[(j + 1) % nb];
502
+ if (segsIntersect(a0, a1, b0, b1)) return true;
503
+ }
504
+ }
505
+ return false;
506
+ }
507
+
508
+ export function sanitizeLoopsForExtrude(loops, fallbackLoops, { eps = 1e-6 } = {}) {
509
+ const base = Array.isArray(loops) ? loops : [];
510
+ const fallback = Array.isArray(fallbackLoops) ? fallbackLoops : [];
511
+ const out = [];
512
+ for (let i = 0; i < base.length; i++) {
513
+ let loop = cleanLoop2D(base[i], eps);
514
+ if (loop.length < 3) { out.push(loop); continue; }
515
+ if (loopSelfIntersects(loop, eps)) {
516
+ const fb = cleanLoop2D(fallback[i] || loop, eps);
517
+ if (fb.length >= 3 && !loopSelfIntersects(fb, eps)) loop = fb;
518
+ else loop = [];
519
+ }
520
+ out.push(loop);
521
+ }
522
+ return out;
523
+ }
524
+
525
+ export function dropIntersectingLoops(loops, { eps = 1e-6 } = {}) {
526
+ const list = Array.isArray(loops) ? loops : [];
527
+ const n = list.length;
528
+ if (n < 2) return list.slice();
529
+ const areas = list.map((l) => loopAreaAbs(l));
530
+ const drop = new Set();
531
+ for (let i = 0; i < n; i++) {
532
+ if (drop.has(i)) continue;
533
+ for (let j = i + 1; j < n; j++) {
534
+ if (drop.has(j)) continue;
535
+ if (!loopsIntersect2D(list[i], list[j], eps)) continue;
536
+ if (areas[i] <= areas[j]) drop.add(i);
537
+ else drop.add(j);
538
+ }
539
+ }
540
+ return list.filter((_, idx) => !drop.has(idx));
541
+ }
542
+
543
+ export function assignBreaksToLoops(loops, breaks, { snapDist = Infinity } = {}) {
544
+ const out = Array.isArray(loops) ? loops.map(() => []) : [];
545
+ if (!Array.isArray(loops) || !Array.isArray(breaks) || !breaks.length) return out;
546
+ const snap2 = Number.isFinite(snapDist) ? snapDist * snapDist : Infinity;
547
+
548
+ for (const bp of breaks) {
549
+ const px = Array.isArray(bp) ? Number(bp[0]) : NaN;
550
+ const py = Array.isArray(bp) ? Number(bp[1]) : NaN;
551
+ if (!Number.isFinite(px) || !Number.isFinite(py)) continue;
552
+
553
+ let bestLoop = -1;
554
+ let bestDist2 = Infinity;
555
+
556
+ for (let li = 0; li < loops.length; li++) {
557
+ const loop = loops[li];
558
+ if (!Array.isArray(loop) || loop.length < 2) continue;
559
+ const ring = loop.length > 1
560
+ && loop[0][0] === loop[loop.length - 1][0]
561
+ && loop[0][1] === loop[loop.length - 1][1]
562
+ ? loop.slice(0, -1)
563
+ : loop;
564
+ const n = ring.length;
565
+ if (n < 2) continue;
566
+ for (let i = 0; i < n; i++) {
567
+ const a = ring[i];
568
+ const b = ring[(i + 1) % n];
569
+ const d2 = pointToSegmentDist2XY(px, py, a[0], a[1], b[0], b[1]);
570
+ if (d2 < bestDist2) {
571
+ bestDist2 = d2;
572
+ bestLoop = li;
573
+ }
574
+ }
575
+ }
576
+
577
+ if (bestLoop >= 0 && bestDist2 <= snap2) {
578
+ out[bestLoop].push([px, py]);
579
+ }
580
+ }
581
+
582
+ return out;
583
+ }
584
+
585
+ function findCorners(ring, angThresh) {
586
+ const n = ring.length;
587
+ if (n < 3) return [];
588
+ const window = Math.max(2, Math.min(8, Math.floor(n / 40) || 2));
589
+ const straightThresh = 0.85;
590
+ const minSpan = window * 0.75;
591
+ const corners = [];
592
+
593
+ const sampleDir = (startIdx, step) => {
594
+ let sx = 0;
595
+ let sy = 0;
596
+ let total = 0;
597
+ for (let k = 0; k < window; k++) {
598
+ const i0 = (startIdx + k * step + n) % n;
599
+ const i1 = (i0 + step + n) % n;
600
+ const dx = ring[i1][0] - ring[i0][0];
601
+ const dy = ring[i1][1] - ring[i0][1];
602
+ const len = Math.hypot(dx, dy);
603
+ if (!len) continue;
604
+ sx += dx;
605
+ sy += dy;
606
+ total += len;
607
+ }
608
+ const mag = Math.hypot(sx, sy);
609
+ return {
610
+ dir: mag > 1e-9 ? [sx / mag, sy / mag] : [0, 0],
611
+ straightness: total > 0 ? mag / total : 0,
612
+ span: total
613
+ };
614
+ };
615
+
616
+ for (let i = 0; i < n; i++) {
617
+ const prev = sampleDir(i - window, 1);
618
+ const next = sampleDir(i, 1);
619
+ if (prev.span < minSpan || next.span < minSpan) continue;
620
+ if (prev.straightness < straightThresh || next.straightness < straightThresh) continue;
621
+ const dot = prev.dir[0] * next.dir[0] + prev.dir[1] * next.dir[1];
622
+ const ang = Math.acos(Math.max(-1, Math.min(1, dot)));
623
+ if (ang > angThresh) corners.push(i);
624
+ }
625
+ return corners;
626
+ }
627
+
628
+ function chaikinClosed(points, iterations) {
629
+ let pts = points.slice();
630
+ for (let k = 0; k < iterations; k++) {
631
+ const next = [];
632
+ for (let i = 0; i < pts.length; i++) {
633
+ const a = pts[i];
634
+ const b = pts[(i + 1) % pts.length];
635
+ const q = [0.75 * a[0] + 0.25 * b[0], 0.75 * a[1] + 0.25 * b[1]];
636
+ const r = [0.25 * a[0] + 0.75 * b[0], 0.25 * a[1] + 0.75 * b[1]];
637
+ next.push(q, r);
638
+ }
639
+ pts = next;
640
+ }
641
+ if (pts[0][0] !== pts[pts.length - 1][0] || pts[0][1] !== pts[pts.length - 1][1]) {
642
+ pts.push([pts[0][0], pts[0][1]]);
643
+ }
644
+ return pts;
645
+ }
646
+
647
+ function chaikinOpen(points, iterations) {
648
+ let pts = points.slice();
649
+ for (let k = 0; k < iterations; k++) {
650
+ const next = [];
651
+ next.push(pts[0]);
652
+ for (let i = 0; i < pts.length - 1; i++) {
653
+ const a = pts[i];
654
+ const b = pts[i + 1];
655
+ const q = [0.75 * a[0] + 0.25 * b[0], 0.75 * a[1] + 0.25 * b[1]];
656
+ const r = [0.25 * a[0] + 0.75 * b[0], 0.25 * a[1] + 0.75 * b[1]];
657
+ next.push(q, r);
658
+ }
659
+ next.push(pts[pts.length - 1]);
660
+ pts = next;
661
+ }
662
+ return pts;
663
+ }
664
+
665
+ function smoothWithAnchors(ring, corners, iterations) {
666
+ const n = ring.length;
667
+ const out = [];
668
+ const anchors = corners.slice();
669
+ anchors.sort((a, b) => a - b);
670
+ const uniq = [];
671
+ for (const idx of anchors) {
672
+ if (!uniq.length || uniq[uniq.length - 1] !== idx) uniq.push(idx);
673
+ }
674
+ anchors.length = 0; anchors.push(...uniq);
675
+
676
+ for (let ci = 0; ci < anchors.length; ci++) {
677
+ const aIdx = anchors[ci];
678
+ const bIdx = anchors[(ci + 1) % anchors.length];
679
+ const seg = [];
680
+ seg.push(ring[aIdx]);
681
+ let idx = (aIdx + 1) % n;
682
+ while (idx !== bIdx) {
683
+ seg.push(ring[idx]);
684
+ idx = (idx + 1) % n;
685
+ }
686
+ seg.push(ring[bIdx]);
687
+
688
+ const sm = chaikinOpen(seg, iterations);
689
+ if (ci === 0) {
690
+ for (const p of sm) out.push(p);
691
+ } else {
692
+ for (let i = 1; i < sm.length; i++) out.push(sm[i]);
693
+ }
694
+ }
695
+ if (out[0][0] !== out[out.length - 1][0] || out[0][1] !== out[out.length - 1][1]) {
696
+ out.push([out[0][0], out[0][1]]);
697
+ }
698
+ return out;
699
+ }
700
+
701
+ export function splitLoopIntoEdges(loop2D, {
702
+ angleDeg = 70,
703
+ minSegLen = 1e-6,
704
+ cornerSpacing = 0,
705
+ manualBreaks = [],
706
+ suppressedBreaks = [],
707
+ autoBreaks = true,
708
+ returnDebug = false,
709
+ } = {}) {
710
+ if (!Array.isArray(loop2D) || loop2D.length < 2) return returnDebug ? { segments: [] } : [];
711
+ let ring = loop2D.slice();
712
+ if (ring.length >= 2 && ring[0][0] === ring[ring.length - 1][0] && ring[0][1] === ring[ring.length - 1][1]) {
713
+ ring.pop();
714
+ }
715
+ let n = ring.length;
716
+ if (n < 2) return returnDebug ? { segments: [] } : [];
717
+
718
+ const manualPts = Array.isArray(manualBreaks) ? manualBreaks : [];
719
+ const manualCornerIndices = [];
720
+ if (manualPts.length) {
721
+ const perSeg = new Map();
722
+ const vertexBreaks = new Set();
723
+ const endpointEps = Math.max(minSegLen * 0.25, 1e-6);
724
+ const closestOnRing = (pt) => {
725
+ const px = pt[0];
726
+ const py = pt[1];
727
+ let best = { dist2: Infinity, segIndex: -1, t: 0, point: null };
728
+ for (let i = 0; i < n; i++) {
729
+ const a = ring[i];
730
+ const b = ring[(i + 1) % n];
731
+ const abx = b[0] - a[0];
732
+ const aby = b[1] - a[1];
733
+ const abLen2 = abx * abx + aby * aby;
734
+ if (abLen2 <= 0) continue;
735
+ let t = ((px - a[0]) * abx + (py - a[1]) * aby) / abLen2;
736
+ t = Math.max(0, Math.min(1, t));
737
+ const cx = a[0] + t * abx;
738
+ const cy = a[1] + t * aby;
739
+ const dx = px - cx;
740
+ const dy = py - cy;
741
+ const d2 = dx * dx + dy * dy;
742
+ if (d2 < best.dist2) {
743
+ best = { dist2: d2, segIndex: i, t, point: [cx, cy] };
744
+ }
745
+ }
746
+ return best;
747
+ };
748
+
749
+ for (const pt of manualPts) {
750
+ if (!Array.isArray(pt) || pt.length < 2) continue;
751
+ const px = Number(pt[0]);
752
+ const py = Number(pt[1]);
753
+ if (!Number.isFinite(px) || !Number.isFinite(py)) continue;
754
+ const res = closestOnRing([px, py]);
755
+ if (res.segIndex < 0 || !res.point) continue;
756
+ const a = ring[res.segIndex];
757
+ const b = ring[(res.segIndex + 1) % n];
758
+ const da = Math.hypot(res.point[0] - a[0], res.point[1] - a[1]);
759
+ const db = Math.hypot(res.point[0] - b[0], res.point[1] - b[1]);
760
+ if (da <= endpointEps) {
761
+ vertexBreaks.add(res.segIndex);
762
+ continue;
763
+ }
764
+ if (db <= endpointEps) {
765
+ vertexBreaks.add((res.segIndex + 1) % n);
766
+ continue;
767
+ }
768
+ let arr = perSeg.get(res.segIndex);
769
+ if (!arr) { arr = []; perSeg.set(res.segIndex, arr); }
770
+ arr.push({ t: res.t, point: res.point });
771
+ }
772
+
773
+ if (vertexBreaks.size || perSeg.size) {
774
+ const expanded = [];
775
+ const markManualIndex = (idx) => {
776
+ if (!manualCornerIndices.length || manualCornerIndices[manualCornerIndices.length - 1] !== idx) {
777
+ manualCornerIndices.push(idx);
778
+ }
779
+ };
780
+ for (let i = 0; i < n; i++) {
781
+ const a = ring[i];
782
+ const startIndex = expanded.length;
783
+ expanded.push(a);
784
+ if (vertexBreaks.has(i)) markManualIndex(startIndex);
785
+ const inserts = perSeg.get(i);
786
+ if (inserts && inserts.length) {
787
+ inserts.sort((u, v) => u.t - v.t);
788
+ for (const ins of inserts) {
789
+ const p = ins.point;
790
+ const last = expanded[expanded.length - 1];
791
+ if (!last || last[0] !== p[0] || last[1] !== p[1]) {
792
+ expanded.push(p);
793
+ markManualIndex(expanded.length - 1);
794
+ }
795
+ }
796
+ }
797
+ }
798
+ ring = expanded;
799
+ n = ring.length;
800
+ }
801
+ }
802
+
803
+ const angThresh = Math.max(0, Math.min(180, angleDeg)) * (Math.PI / 180);
804
+ let totalLen = 0;
805
+ const cum = new Array(n).fill(0);
806
+ for (let i = 0; i < n; i++) {
807
+ const a = ring[i];
808
+ const b = ring[(i + 1) % n];
809
+ totalLen += Math.hypot(b[0] - a[0], b[1] - a[1]);
810
+ if (i + 1 < n) cum[i + 1] = totalLen;
811
+ }
812
+ const avgLen = totalLen > 1e-9 ? (totalLen / n) : minSegLen;
813
+ const spanLen = Math.max(minSegLen, avgLen * 4);
814
+ const minSpan = spanLen * 0.75;
815
+ const straightnessThresh = 0.97;
816
+ const minCornerSpacing = Math.max(spanLen * 1.5, totalLen * 0.015, minSegLen * 2, cornerSpacing || 0);
817
+
818
+ const sampleDir = (startIdx, step) => {
819
+ let sx = 0;
820
+ let sy = 0;
821
+ let acc = 0;
822
+ let idx = startIdx;
823
+ for (let guard = 0; guard < n; guard++) {
824
+ const next = (idx + step + n) % n;
825
+ const dx = ring[next][0] - ring[idx][0];
826
+ const dy = ring[next][1] - ring[idx][1];
827
+ const len = Math.hypot(dx, dy);
828
+ if (len > 0) {
829
+ sx += dx;
830
+ sy += dy;
831
+ acc += len;
832
+ }
833
+ idx = next;
834
+ if (acc >= spanLen) break;
835
+ }
836
+ const mag = Math.hypot(sx, sy);
837
+ const straightness = acc > 0 ? (mag / acc) : 0;
838
+ return {
839
+ dir: mag > 1e-9 ? [sx / mag, sy / mag] : [0, 0],
840
+ span: acc,
841
+ straightness
842
+ };
843
+ };
844
+
845
+ const candidates = [];
846
+ if (autoBreaks !== false) {
847
+ for (let i = 0; i < n; i++) {
848
+ const prev = sampleDir(i, -1);
849
+ const next = sampleDir(i, 1);
850
+ if (prev.span < minSpan || next.span < minSpan) continue;
851
+ if (prev.straightness < straightnessThresh || next.straightness < straightnessThresh) continue;
852
+ const inDir = [-prev.dir[0], -prev.dir[1]];
853
+ const dot = inDir[0] * next.dir[0] + inDir[1] * next.dir[1];
854
+ const ang = Math.acos(Math.max(-1, Math.min(1, dot)));
855
+ if (ang >= angThresh) candidates.push({ idx: i, ang });
856
+ }
857
+ }
858
+
859
+ const arcDist = (a, b) => {
860
+ const da = Math.abs(cum[a] - cum[b]);
861
+ return Math.min(da, totalLen - da);
862
+ };
863
+ const corners = [];
864
+ candidates.sort((a, b) => b.ang - a.ang);
865
+ for (const cand of candidates) {
866
+ let tooClose = false;
867
+ for (const sel of corners) {
868
+ if (arcDist(cand.idx, sel.idx) < minCornerSpacing) { tooClose = true; break; }
869
+ }
870
+ if (!tooClose) corners.push(cand);
871
+ }
872
+ if (corners.length < 2 && candidates.length) {
873
+ corners.length = 0;
874
+ corners.push(...candidates.sort((a, b) => a.idx - b.idx));
875
+ }
876
+
877
+ const suppressedIdx = new Set();
878
+ const suppressedPts = Array.isArray(suppressedBreaks) ? suppressedBreaks : [];
879
+ if (autoBreaks !== false && suppressedPts.length) {
880
+ const snapDist = Math.max(minSegLen * 0.5, 1e-6);
881
+ const snapDist2 = snapDist * snapDist;
882
+ for (const pt of suppressedPts) {
883
+ if (!Array.isArray(pt) || pt.length < 2) continue;
884
+ const px = Number(pt[0]);
885
+ const py = Number(pt[1]);
886
+ if (!Number.isFinite(px) || !Number.isFinite(py)) continue;
887
+ let bestIdx = -1;
888
+ let bestD2 = Infinity;
889
+ for (let i = 0; i < n; i++) {
890
+ const p = ring[i];
891
+ const dx = p[0] - px;
892
+ const dy = p[1] - py;
893
+ const d2 = dx * dx + dy * dy;
894
+ if (d2 < bestD2) {
895
+ bestD2 = d2;
896
+ bestIdx = i;
897
+ }
898
+ }
899
+ if (bestIdx >= 0 && bestD2 <= snapDist2) {
900
+ suppressedIdx.add(bestIdx);
901
+ }
902
+ }
903
+ }
904
+
905
+ if (suppressedIdx.size) {
906
+ for (let i = corners.length - 1; i >= 0; i--) {
907
+ if (suppressedIdx.has(corners[i].idx)) corners.splice(i, 1);
908
+ }
909
+ }
910
+
911
+ const cornerIdx = [];
912
+ for (const c of corners) cornerIdx.push(c.idx);
913
+ for (const idx of manualCornerIndices) cornerIdx.push(idx);
914
+ if (cornerIdx.length < 2) {
915
+ const loopOut = ring.concat([ring[0]]);
916
+ return returnDebug
917
+ ? { segments: [loopOut], corners: [], manualCorners: manualCornerIndices.slice(), ring: ring.slice() }
918
+ : [loopOut];
919
+ }
920
+ cornerIdx.sort((a, b) => a - b);
921
+ const uniq = [];
922
+ for (const idx of cornerIdx) {
923
+ if (!uniq.length || uniq[uniq.length - 1] !== idx) uniq.push(idx);
924
+ }
925
+ if (uniq.length < 2) {
926
+ const loopOut = ring.concat([ring[0]]);
927
+ return returnDebug
928
+ ? { segments: [loopOut], corners: [], manualCorners: manualCornerIndices.slice(), ring: ring.slice() }
929
+ : [loopOut];
930
+ }
931
+
932
+ const segments = [];
933
+ const dedupeSeg = (seg) => {
934
+ const out = [];
935
+ let prev = null;
936
+ for (const p of seg) {
937
+ if (!prev || p[0] !== prev[0] || p[1] !== prev[1]) out.push(p);
938
+ prev = p;
939
+ }
940
+ return out;
941
+ };
942
+ for (let i = 0; i < uniq.length; i++) {
943
+ const start = uniq[i];
944
+ const end = uniq[(i + 1) % uniq.length];
945
+ const seg = [];
946
+ let k = start;
947
+ for (let guard = 0; guard <= n; guard++) {
948
+ seg.push(ring[k]);
949
+ if (k === end) break;
950
+ k = (k + 1) % n;
951
+ }
952
+ const cleaned = dedupeSeg(seg);
953
+ if (cleaned.length >= 2) segments.push(cleaned);
954
+ }
955
+ if (!segments.length) {
956
+ const loopOut = ring.concat([ring[0]]);
957
+ return returnDebug
958
+ ? { segments: [loopOut], corners: [], manualCorners: manualCornerIndices.slice(), ring: ring.slice() }
959
+ : [loopOut];
960
+ }
961
+
962
+ if (!returnDebug) return segments;
963
+ const manualSet = new Set(manualCornerIndices);
964
+ const autoCorners = uniq.filter((idx) => !manualSet.has(idx));
965
+ return {
966
+ segments,
967
+ corners: autoCorners,
968
+ manualCorners: Array.from(new Set(manualCornerIndices)).sort((a, b) => a - b),
969
+ ring: ring.slice(),
970
+ };
971
+ }