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,862 @@
1
+ import * as THREE from 'three';
2
+
3
+ /**
4
+ * Sign convention:
5
+ * - bend.angleRad describes the folded rotation from aFace to bFace using the
6
+ * right-hand rule about bend.axisDir in folded space.
7
+ * - Unfolding rotates the moving face by -bend.angleRad when stepping from
8
+ * aFace -> bFace, and +bend.angleRad when stepping from bFace -> aFace.
9
+ */
10
+
11
+ const DEFAULT_EPSILON = 1e-5;
12
+ const DEFAULT_CLIP_EXTENT = 1e6;
13
+ const EPS = 1e-12;
14
+
15
+ /**
16
+ * @typedef {Object} Bend
17
+ * @property {string|number} bendId
18
+ * @property {number} aFaceId
19
+ * @property {number} bFaceId
20
+ * @property {THREE.Vector3} axisPoint
21
+ * @property {THREE.Vector3} axisDir
22
+ * @property {number} angleRad
23
+ * @property {number} radius
24
+ * @property {number} thickness
25
+ * @property {number} kFactor
26
+ * @property {'up'|'down'} upDown
27
+ */
28
+
29
+ /**
30
+ * @typedef {Object} FlatPattern
31
+ * @property {Map<number, THREE.Matrix4>} faceTransforms
32
+ * @property {Array<THREE.Vector2>} vertex2D
33
+ * @property {Array<Array<THREE.Vector2>>} outlines
34
+ * @property {Array<Array<THREE.Vector2>>} holes
35
+ * @property {Array<Object>} bendLines
36
+ */
37
+
38
+ const makeEdgeKey = (a, b) => (a < b ? `${a}|${b}` : `${b}|${a}`);
39
+ const makePairKey = (a, b) => (a < b ? `${a}|${b}` : `${b}|${a}`);
40
+
41
+ function snapValue(value, eps) {
42
+ if (!Number.isFinite(value)) return value;
43
+ const inv = 1 / eps;
44
+ return Math.round(value * inv) / inv;
45
+ }
46
+
47
+ function updateBounds(bounds, point) {
48
+ if (!bounds) return { min: point.clone(), max: point.clone() };
49
+ bounds.min.min(point);
50
+ bounds.max.max(point);
51
+ return bounds;
52
+ }
53
+
54
+ function buildFacePanels(geometry, faceIdAttr) {
55
+ const position = geometry.getAttribute('position');
56
+ const index = geometry.index;
57
+ const triCount = (index.count / 3) | 0;
58
+ const posArray = position.array;
59
+ const idxArray = index.array;
60
+
61
+ const faces = new Map();
62
+ const vA = new THREE.Vector3();
63
+ const vB = new THREE.Vector3();
64
+ const vC = new THREE.Vector3();
65
+ const vAB = new THREE.Vector3();
66
+ const vAC = new THREE.Vector3();
67
+ const vBC = new THREE.Vector3();
68
+ const vCA = new THREE.Vector3();
69
+ const n = new THREE.Vector3();
70
+ const centroid = new THREE.Vector3();
71
+
72
+ for (let t = 0; t < triCount; t++) {
73
+ const faceId = faceIdAttr.getX(t);
74
+ let face = faces.get(faceId);
75
+ if (!face) {
76
+ face = {
77
+ id: faceId,
78
+ triangles: [],
79
+ normalSum: new THREE.Vector3(),
80
+ centroidSum: new THREE.Vector3(),
81
+ areaSum: 0,
82
+ refNormal: null,
83
+ longestEdgeDir: new THREE.Vector3(),
84
+ longestEdgeLenSq: 0,
85
+ samplePoint: new THREE.Vector3(),
86
+ };
87
+ faces.set(faceId, face);
88
+ }
89
+ face.triangles.push(t);
90
+
91
+ const i0 = idxArray[t * 3 + 0];
92
+ const i1 = idxArray[t * 3 + 1];
93
+ const i2 = idxArray[t * 3 + 2];
94
+
95
+ vA.fromArray(posArray, i0 * 3);
96
+ vB.fromArray(posArray, i1 * 3);
97
+ vC.fromArray(posArray, i2 * 3);
98
+ if (face.triangles.length === 1) {
99
+ face.samplePoint.copy(vA);
100
+ }
101
+
102
+ vAB.subVectors(vB, vA);
103
+ vAC.subVectors(vC, vA);
104
+ n.crossVectors(vAB, vAC);
105
+ const doubleArea = n.length();
106
+ if (doubleArea < EPS) continue;
107
+
108
+ const area = doubleArea * 0.5;
109
+ n.multiplyScalar(1 / doubleArea);
110
+ if (!face.refNormal) face.refNormal = n.clone();
111
+ else if (n.dot(face.refNormal) < 0) n.negate();
112
+
113
+ face.normalSum.addScaledVector(n, area);
114
+ centroid.copy(vA).add(vB).add(vC).multiplyScalar(1 / 3);
115
+ face.centroidSum.addScaledVector(centroid, area);
116
+ face.areaSum += area;
117
+
118
+ const abLenSq = vAB.lengthSq();
119
+ if (abLenSq > face.longestEdgeLenSq) {
120
+ face.longestEdgeLenSq = abLenSq;
121
+ face.longestEdgeDir.copy(vAB);
122
+ }
123
+ vBC.subVectors(vC, vB);
124
+ const bcLenSq = vBC.lengthSq();
125
+ if (bcLenSq > face.longestEdgeLenSq) {
126
+ face.longestEdgeLenSq = bcLenSq;
127
+ face.longestEdgeDir.copy(vBC);
128
+ }
129
+ vCA.subVectors(vA, vC);
130
+ const caLenSq = vCA.lengthSq();
131
+ if (caLenSq > face.longestEdgeLenSq) {
132
+ face.longestEdgeLenSq = caLenSq;
133
+ face.longestEdgeDir.copy(vCA);
134
+ }
135
+ }
136
+
137
+ for (const face of faces.values()) {
138
+ const nrm = face.normalSum.lengthSq() > EPS
139
+ ? face.normalSum.normalize()
140
+ : (face.refNormal ? face.refNormal.clone().normalize() : new THREE.Vector3(0, 0, 1));
141
+
142
+ const origin = face.areaSum > EPS
143
+ ? face.centroidSum.multiplyScalar(1 / face.areaSum)
144
+ : face.samplePoint.clone();
145
+
146
+ let u = new THREE.Vector3();
147
+ if (face.longestEdgeLenSq > EPS) {
148
+ u.copy(face.longestEdgeDir);
149
+ u.addScaledVector(nrm, -u.dot(nrm));
150
+ if (u.lengthSq() < EPS) u = new THREE.Vector3();
151
+ }
152
+ if (u.lengthSq() < EPS) {
153
+ const tmp = Math.abs(nrm.x) < 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0);
154
+ u.crossVectors(tmp, nrm);
155
+ }
156
+ u.normalize();
157
+
158
+ const v = new THREE.Vector3().crossVectors(nrm, u).normalize();
159
+
160
+ face.n = nrm;
161
+ face.u = u;
162
+ face.v = v;
163
+ face.origin = origin;
164
+ }
165
+
166
+ return faces;
167
+ }
168
+
169
+ function buildAdjacency(bends) {
170
+ const adjacency = new Map();
171
+ for (const bend of bends) {
172
+ const a = bend.aFaceId;
173
+ const b = bend.bFaceId;
174
+ if (!adjacency.has(a)) adjacency.set(a, []);
175
+ if (!adjacency.has(b)) adjacency.set(b, []);
176
+ adjacency.get(a).push(bend);
177
+ adjacency.get(b).push(bend);
178
+ }
179
+ return adjacency;
180
+ }
181
+
182
+ function buildAxisRotationMatrix(axisPoint, axisDir, theta) {
183
+ const dir = axisDir.clone().normalize();
184
+ const rot = new THREE.Matrix4().makeRotationAxis(dir, theta);
185
+ const t1 = new THREE.Matrix4().makeTranslation(-axisPoint.x, -axisPoint.y, -axisPoint.z);
186
+ const t2 = new THREE.Matrix4().makeTranslation(axisPoint.x, axisPoint.y, axisPoint.z);
187
+ return new THREE.Matrix4().multiplyMatrices(t2, rot).multiply(t1);
188
+ }
189
+
190
+ function buildFaceAlignmentTransform(face, rootFace) {
191
+ const basisRoot = new THREE.Matrix4().makeBasis(rootFace.u, rootFace.v, rootFace.n);
192
+ const basisFace = new THREE.Matrix4().makeBasis(face.u, face.v, face.n);
193
+ const rotation = new THREE.Matrix4().multiplyMatrices(basisRoot, basisFace.clone().transpose());
194
+ const t1 = new THREE.Matrix4().makeTranslation(-face.origin.x, -face.origin.y, -face.origin.z);
195
+ const t2 = new THREE.Matrix4().makeTranslation(rootFace.origin.x, rootFace.origin.y, rootFace.origin.z);
196
+ return new THREE.Matrix4().multiplyMatrices(t2, rotation).multiply(t1);
197
+ }
198
+
199
+ function computeUnfoldTransforms(faceMap, adjacency, bends, options = {}) {
200
+ const faceIds = Array.from(faceMap.keys()).sort((a, b) => a - b);
201
+ if (!faceIds.length) return { faceTransforms: new Map(), rootFaceId: null };
202
+
203
+ const preferredRoot = options.rootFaceId;
204
+ const rootFaceId = faceMap.has(preferredRoot) ? preferredRoot : faceIds[0];
205
+ const rootFace = faceMap.get(rootFaceId);
206
+
207
+ const faceTransforms = new Map();
208
+ const pending = new Set(faceIds);
209
+ let firstRootHandled = false;
210
+
211
+ const queue = [];
212
+
213
+ while (pending.size) {
214
+ let componentRootId = null;
215
+ if (!firstRootHandled && pending.has(rootFaceId)) {
216
+ componentRootId = rootFaceId;
217
+ firstRootHandled = true;
218
+ } else {
219
+ for (const id of faceIds) {
220
+ if (pending.has(id)) { componentRootId = id; break; }
221
+ }
222
+ }
223
+ if (componentRootId == null) break;
224
+
225
+ const componentFace = faceMap.get(componentRootId);
226
+ const baseTransform = componentRootId === rootFaceId
227
+ ? new THREE.Matrix4().identity()
228
+ : buildFaceAlignmentTransform(componentFace, rootFace);
229
+
230
+ faceTransforms.set(componentRootId, baseTransform);
231
+ pending.delete(componentRootId);
232
+ queue.length = 0;
233
+ queue.push(componentRootId);
234
+
235
+ while (queue.length) {
236
+ const current = queue.shift();
237
+ const currentTransform = faceTransforms.get(current);
238
+ const edges = adjacency.get(current) || [];
239
+
240
+ for (const bend of edges) {
241
+ const neighbor = bend.aFaceId === current ? bend.bFaceId : bend.aFaceId;
242
+ if (!faceMap.has(neighbor)) continue;
243
+ if (faceTransforms.has(neighbor)) continue;
244
+
245
+ const theta = bend.aFaceId === current ? -bend.angleRad : bend.angleRad;
246
+ const rotation = buildAxisRotationMatrix(bend.axisPoint, bend.axisDir, theta);
247
+ const neighborTransform = new THREE.Matrix4().multiplyMatrices(currentTransform, rotation);
248
+ faceTransforms.set(neighbor, neighborTransform);
249
+ pending.delete(neighbor);
250
+ queue.push(neighbor);
251
+ }
252
+ }
253
+ }
254
+
255
+ return { faceTransforms, rootFaceId };
256
+ }
257
+
258
+ function projectToRootPlane(point3, rootOrigin, rootU, rootV) {
259
+ const delta = point3.clone().sub(rootOrigin);
260
+ return new THREE.Vector2(delta.dot(rootU), delta.dot(rootV));
261
+ }
262
+
263
+ function buildCutEdges(indexAttr, faceIdAttr, bendPairs, edgeExclusions) {
264
+ const triCount = (indexAttr.count / 3) | 0;
265
+ const idx = indexAttr.array;
266
+
267
+ const edgeFaces = new Map();
268
+ const faceEdgeCounts = new Map();
269
+ const excluded = edgeExclusions instanceof Set ? edgeExclusions : null;
270
+
271
+ for (let t = 0; t < triCount; t++) {
272
+ const faceId = faceIdAttr.getX(t);
273
+ const i0 = idx[t * 3 + 0];
274
+ const i1 = idx[t * 3 + 1];
275
+ const i2 = idx[t * 3 + 2];
276
+ const edges = [[i0, i1], [i1, i2], [i2, i0]];
277
+
278
+ for (const [a, b] of edges) {
279
+ if (excluded && excluded.has(makeEdgeKey(a, b))) continue;
280
+ const key = makeEdgeKey(a, b);
281
+ let faces = edgeFaces.get(key);
282
+ if (!faces) {
283
+ faces = new Set();
284
+ edgeFaces.set(key, faces);
285
+ }
286
+ faces.add(faceId);
287
+
288
+ let faceEdges = faceEdgeCounts.get(faceId);
289
+ if (!faceEdges) {
290
+ faceEdges = new Map();
291
+ faceEdgeCounts.set(faceId, faceEdges);
292
+ }
293
+ let rec = faceEdges.get(key);
294
+ if (!rec) {
295
+ rec = { count: 0, a, b };
296
+ faceEdges.set(key, rec);
297
+ }
298
+ rec.count += 1;
299
+ }
300
+ }
301
+
302
+ const cutEdges = [];
303
+ for (const [faceId, edgeMap] of faceEdgeCounts.entries()) {
304
+ for (const [key, rec] of edgeMap.entries()) {
305
+ if (rec.count !== 1) continue;
306
+ const faces = edgeFaces.get(key);
307
+ let isBendShared = false;
308
+ if (faces && faces.size > 1) {
309
+ for (const other of faces) {
310
+ if (other === faceId) continue;
311
+ const pairKey = makePairKey(faceId, other);
312
+ if (bendPairs.has(pairKey)) { isBendShared = true; break; }
313
+ }
314
+ }
315
+ if (!isBendShared) {
316
+ cutEdges.push({ faceId, a: rec.a, b: rec.b });
317
+ }
318
+ }
319
+ }
320
+ return cutEdges;
321
+ }
322
+
323
+ function buildLoopsFromCutEdges(cutEdges, getVertex2D, epsilon) {
324
+ const pointMap = new Map();
325
+ const segments = [];
326
+ const segmentKeys = new Set();
327
+
328
+ const snapPoint = (point) => {
329
+ const sx = snapValue(point.x, epsilon);
330
+ const sy = snapValue(point.y, epsilon);
331
+ const key = `${sx},${sy}`;
332
+ if (!pointMap.has(key)) {
333
+ pointMap.set(key, new THREE.Vector2(sx, sy));
334
+ }
335
+ return { key, point: pointMap.get(key) };
336
+ };
337
+
338
+ for (const edge of cutEdges) {
339
+ const p0 = getVertex2D(edge.faceId, edge.a);
340
+ const p1 = getVertex2D(edge.faceId, edge.b);
341
+ const s0 = snapPoint(p0);
342
+ const s1 = snapPoint(p1);
343
+ if (s0.key === s1.key) continue;
344
+ const segKey = makeEdgeKey(s0.key, s1.key);
345
+ if (segmentKeys.has(segKey)) continue;
346
+ segmentKeys.add(segKey);
347
+ segments.push({ k0: s0.key, k1: s1.key });
348
+ }
349
+
350
+ const adjacency = new Map();
351
+ for (const seg of segments) {
352
+ if (!adjacency.has(seg.k0)) adjacency.set(seg.k0, []);
353
+ if (!adjacency.has(seg.k1)) adjacency.set(seg.k1, []);
354
+ adjacency.get(seg.k0).push(seg.k1);
355
+ adjacency.get(seg.k1).push(seg.k0);
356
+ }
357
+ for (const list of adjacency.values()) {
358
+ list.sort();
359
+ }
360
+
361
+ const segByKey = new Map();
362
+ for (const seg of segments) {
363
+ segByKey.set(makeEdgeKey(seg.k0, seg.k1), seg);
364
+ }
365
+
366
+ const unused = new Set(segmentKeys);
367
+ const sortedKeys = Array.from(segmentKeys).sort();
368
+ const loops = [];
369
+
370
+ for (const segKey of sortedKeys) {
371
+ if (!unused.has(segKey)) continue;
372
+ const seg = segByKey.get(segKey);
373
+ if (!seg) continue;
374
+ unused.delete(segKey);
375
+
376
+ const start = seg.k0;
377
+ let prev = seg.k0;
378
+ let curr = seg.k1;
379
+ const loopKeys = [start, curr];
380
+
381
+ let guard = 0;
382
+ while (curr !== start && guard < segments.length + 10) {
383
+ guard += 1;
384
+ const neighbors = adjacency.get(curr) || [];
385
+ let next = null;
386
+ for (const neighbor of neighbors) {
387
+ if (neighbor === prev) continue;
388
+ const candidateKey = makeEdgeKey(curr, neighbor);
389
+ if (unused.has(candidateKey)) { next = neighbor; break; }
390
+ }
391
+ if (!next) break;
392
+ const usedKey = makeEdgeKey(curr, next);
393
+ unused.delete(usedKey);
394
+ prev = curr;
395
+ curr = next;
396
+ loopKeys.push(curr);
397
+ }
398
+
399
+ if (curr === start && loopKeys.length > 2) {
400
+ loopKeys.pop();
401
+ const points = loopKeys.map((key) => pointMap.get(key));
402
+ loops.push(points);
403
+ }
404
+ }
405
+
406
+ const outlines = [];
407
+ const holes = [];
408
+ for (const loop of loops) {
409
+ const area = signedArea(loop);
410
+ if (area >= 0) outlines.push(loop);
411
+ else holes.push(loop);
412
+ }
413
+
414
+ return { outlines, holes, loops, pointMap };
415
+ }
416
+
417
+ function signedArea(points) {
418
+ let area = 0;
419
+ const len = points.length;
420
+ for (let i = 0; i < len; i++) {
421
+ const p0 = points[i];
422
+ const p1 = points[(i + 1) % len];
423
+ area += (p0.x * p1.y) - (p1.x * p0.y);
424
+ }
425
+ return area * 0.5;
426
+ }
427
+
428
+ function clipSegmentToBox(p0, p1, min, max) {
429
+ const dx = p1.x - p0.x;
430
+ const dy = p1.y - p0.y;
431
+ let t0 = 0;
432
+ let t1 = 1;
433
+
434
+ const p = [-dx, dx, -dy, dy];
435
+ const q = [p0.x - min.x, max.x - p0.x, p0.y - min.y, max.y - p0.y];
436
+
437
+ for (let i = 0; i < 4; i++) {
438
+ if (Math.abs(p[i]) < EPS) {
439
+ if (q[i] < 0) return null;
440
+ } else {
441
+ const r = q[i] / p[i];
442
+ if (p[i] < 0) {
443
+ t0 = Math.max(t0, r);
444
+ } else {
445
+ t1 = Math.min(t1, r);
446
+ }
447
+ if (t0 > t1) return null;
448
+ }
449
+ }
450
+
451
+ return {
452
+ p0: new THREE.Vector2(p0.x + t0 * dx, p0.y + t0 * dy),
453
+ p1: new THREE.Vector2(p0.x + t1 * dx, p0.y + t1 * dy),
454
+ };
455
+ }
456
+
457
+ function buildBendLines(bends, faceTransforms, rootOrigin, rootU, rootV, options, faceBounds2D, fallbackBounds) {
458
+ const bendLines = [];
459
+ const clipExtent = Number.isFinite(options.clipExtent) ? options.clipExtent : DEFAULT_CLIP_EXTENT;
460
+
461
+ for (const bend of bends) {
462
+ const transformA = faceTransforms.get(bend.aFaceId);
463
+ if (!transformA) continue;
464
+
465
+ const axisPoint = bend.axisPoint.clone();
466
+ const axisDir = bend.axisDir.clone().normalize();
467
+ if (axisDir.lengthSq() < EPS) continue;
468
+
469
+ const p0 = axisPoint.clone().applyMatrix4(transformA);
470
+ const p1 = axisPoint.clone().add(axisDir).applyMatrix4(transformA);
471
+ const dir = p1.sub(p0).normalize();
472
+
473
+ const longA = p0.clone().addScaledVector(dir, -clipExtent);
474
+ const longB = p0.clone().addScaledVector(dir, clipExtent);
475
+
476
+ const p0_2d = projectToRootPlane(longA, rootOrigin, rootU, rootV);
477
+ const p1_2d = projectToRootPlane(longB, rootOrigin, rootU, rootV);
478
+
479
+ let bounds = null;
480
+ const boundsA = faceBounds2D.get(bend.aFaceId);
481
+ const boundsB = faceBounds2D.get(bend.bFaceId);
482
+ if (boundsA && boundsB) {
483
+ bounds = {
484
+ min: boundsA.min.clone().min(boundsB.min),
485
+ max: boundsA.max.clone().max(boundsB.max),
486
+ };
487
+ } else if (boundsA) {
488
+ bounds = { min: boundsA.min.clone(), max: boundsA.max.clone() };
489
+ } else if (boundsB) {
490
+ bounds = { min: boundsB.min.clone(), max: boundsB.max.clone() };
491
+ } else if (fallbackBounds) {
492
+ bounds = { min: fallbackBounds.min.clone(), max: fallbackBounds.max.clone() };
493
+ }
494
+
495
+ let clipped = { p0: p0_2d, p1: p1_2d };
496
+ if (bounds) {
497
+ const clip = clipSegmentToBox(p0_2d, p1_2d, bounds.min, bounds.max);
498
+ if (!clip) continue;
499
+ clipped = clip;
500
+ }
501
+
502
+ const thickness = Number.isFinite(bend.thickness) ? bend.thickness : 0;
503
+ const kFactor = Number.isFinite(bend.kFactor) ? bend.kFactor : 0;
504
+ const radius = Number.isFinite(bend.radius) ? bend.radius : 0;
505
+ const angle = Number.isFinite(bend.angleRad) ? bend.angleRad : 0;
506
+ const bendAllowance = Math.abs(angle) * (radius + kFactor * thickness);
507
+
508
+ bendLines.push({
509
+ bendId: bend.bendId,
510
+ aFaceId: bend.aFaceId,
511
+ bFaceId: bend.bFaceId,
512
+ p0: clipped.p0,
513
+ p1: clipped.p1,
514
+ angleRad: angle,
515
+ radius,
516
+ thickness,
517
+ kFactor,
518
+ upDown: bend.upDown,
519
+ bendAllowance,
520
+ });
521
+ }
522
+
523
+ return bendLines;
524
+ }
525
+
526
+ /**
527
+ * Unfold sheet metal geometry into a flat pattern.
528
+ * @param {THREE.BufferGeometry} geometry
529
+ * @param {Array<Bend>} bends
530
+ * @param {Object} options
531
+ * @param {number} [options.rootFaceId]
532
+ * @param {number} [options.epsilon]
533
+ * @param {number} [options.clipExtent]
534
+ * @param {Set<string>} [options.edgeExclusions]
535
+ * @returns {FlatPattern}
536
+ */
537
+ export function unfoldSheetMetal(geometry, bends = [], options = {}) {
538
+ if (!geometry || !(geometry instanceof THREE.BufferGeometry)) {
539
+ throw new Error('unfoldSheetMetal: geometry must be a THREE.BufferGeometry.');
540
+ }
541
+ if (!geometry.index) {
542
+ throw new Error('unfoldSheetMetal: geometry must be indexed triangles.');
543
+ }
544
+
545
+ const faceIdAttr = geometry.getAttribute('faceId');
546
+ if (!faceIdAttr) {
547
+ throw new Error('unfoldSheetMetal: geometry is missing faceId attribute (per-triangle).');
548
+ }
549
+
550
+ const triCount = (geometry.index.count / 3) | 0;
551
+ if (faceIdAttr.count !== triCount) {
552
+ throw new Error('unfoldSheetMetal: faceId attribute count must equal triangle count.');
553
+ }
554
+
555
+ const epsilon = Number.isFinite(options.epsilon) ? options.epsilon : DEFAULT_EPSILON;
556
+ const faceMap = buildFacePanels(geometry, faceIdAttr);
557
+ const adjacency = buildAdjacency(bends);
558
+
559
+ const bendPairs = new Set();
560
+ for (const bend of bends) {
561
+ bendPairs.add(makePairKey(bend.aFaceId, bend.bFaceId));
562
+ }
563
+
564
+ const { faceTransforms, rootFaceId } = computeUnfoldTransforms(faceMap, adjacency, bends, options);
565
+ const rootFace = rootFaceId != null ? faceMap.get(rootFaceId) : null;
566
+ if (!rootFace) {
567
+ return {
568
+ faceTransforms,
569
+ vertex2D: [],
570
+ outlines: [],
571
+ holes: [],
572
+ bendLines: [],
573
+ };
574
+ }
575
+
576
+ const rootOrigin = rootFace.origin.clone();
577
+ const rootU = rootFace.u.clone().normalize();
578
+ const rootV = rootFace.v.clone().normalize();
579
+
580
+ const position = geometry.getAttribute('position');
581
+ const posArray = position.array;
582
+ const idxArray = geometry.index.array;
583
+ const vertexCount = position.count;
584
+
585
+ const vertex2D = new Array(vertexCount);
586
+ const vertex2DByFace = new Map();
587
+ const faceBounds2D = new Map();
588
+
589
+ const temp3 = new THREE.Vector3();
590
+
591
+ const getVertex2D = (faceId, vertexIndex) => {
592
+ let map = vertex2DByFace.get(faceId);
593
+ if (!map) {
594
+ map = new Map();
595
+ vertex2DByFace.set(faceId, map);
596
+ }
597
+ if (map.has(vertexIndex)) return map.get(vertexIndex);
598
+
599
+ const transform = faceTransforms.get(faceId) || new THREE.Matrix4().identity();
600
+ temp3.fromArray(posArray, vertexIndex * 3).applyMatrix4(transform);
601
+ const p2 = projectToRootPlane(temp3, rootOrigin, rootU, rootV);
602
+
603
+ map.set(vertexIndex, p2);
604
+
605
+ if (!vertex2D[vertexIndex]) {
606
+ vertex2D[vertexIndex] = p2.clone();
607
+ }
608
+
609
+ const bounds = faceBounds2D.get(faceId);
610
+ faceBounds2D.set(faceId, updateBounds(bounds, p2));
611
+
612
+ return p2;
613
+ };
614
+
615
+ // Populate vertex2D for all triangle vertices (per-face mapping)
616
+ for (let t = 0; t < triCount; t++) {
617
+ const faceId = faceIdAttr.getX(t);
618
+ const i0 = idxArray[t * 3 + 0];
619
+ const i1 = idxArray[t * 3 + 1];
620
+ const i2 = idxArray[t * 3 + 2];
621
+ getVertex2D(faceId, i0);
622
+ getVertex2D(faceId, i1);
623
+ getVertex2D(faceId, i2);
624
+ }
625
+
626
+ const cutEdges = buildCutEdges(
627
+ geometry.index,
628
+ faceIdAttr,
629
+ bendPairs,
630
+ options.edgeExclusions,
631
+ );
632
+ const { outlines, holes, loops, pointMap } = buildLoopsFromCutEdges(cutEdges, getVertex2D, epsilon);
633
+
634
+ let overallBounds = null;
635
+ for (const loop of loops) {
636
+ for (const pt of loop) {
637
+ overallBounds = updateBounds(overallBounds, pt);
638
+ }
639
+ }
640
+ if (!overallBounds && pointMap.size) {
641
+ for (const pt of pointMap.values()) {
642
+ overallBounds = updateBounds(overallBounds, pt);
643
+ }
644
+ }
645
+
646
+ const bendLines = buildBendLines(
647
+ bends,
648
+ faceTransforms,
649
+ rootOrigin,
650
+ rootU,
651
+ rootV,
652
+ options,
653
+ faceBounds2D,
654
+ overallBounds
655
+ );
656
+
657
+ return {
658
+ faceTransforms,
659
+ vertex2D,
660
+ outlines,
661
+ holes,
662
+ bendLines,
663
+ };
664
+ }
665
+
666
+ function formatNumber(value, decimals) {
667
+ const n = Number(value);
668
+ if (!Number.isFinite(n)) return '0';
669
+ const fixed = n.toFixed(decimals);
670
+ return fixed.replace(/\.0+$/, '').replace(/(\.\d+?)0+$/, '$1');
671
+ }
672
+
673
+ function polylineToPath(points, close, transformPoint, decimals) {
674
+ if (!points.length) return '';
675
+ const first = transformPoint(points[0]);
676
+ let d = `M ${formatNumber(first.x, decimals)} ${formatNumber(first.y, decimals)}`;
677
+ for (let i = 1; i < points.length; i++) {
678
+ const p = transformPoint(points[i]);
679
+ d += ` L ${formatNumber(p.x, decimals)} ${formatNumber(p.y, decimals)}`;
680
+ }
681
+ if (close) d += ' Z';
682
+ return d;
683
+ }
684
+
685
+ function segmentsToPath(segments, transformPoint, decimals) {
686
+ let d = '';
687
+ for (const seg of segments) {
688
+ const p0 = transformPoint(seg.p0);
689
+ const p1 = transformPoint(seg.p1);
690
+ d += `M ${formatNumber(p0.x, decimals)} ${formatNumber(p0.y, decimals)} `;
691
+ d += `L ${formatNumber(p1.x, decimals)} ${formatNumber(p1.y, decimals)} `;
692
+ }
693
+ return d.trim();
694
+ }
695
+
696
+ /**
697
+ * Export flat pattern outlines + bend lines to an SVG string.
698
+ * @param {FlatPattern} flatPattern
699
+ * @param {Object} options
700
+ * @param {number} [options.scale]
701
+ * @param {number} [options.padding]
702
+ * @param {boolean} [options.includeBends]
703
+ * @param {boolean} [options.flipY]
704
+ * @param {number} [options.decimals]
705
+ * @returns {string}
706
+ */
707
+ export function exportFlatPatternToSVG(flatPattern, options = {}) {
708
+ const scale = Number.isFinite(options.scale) ? options.scale : 1;
709
+ const padding = Number.isFinite(options.padding) ? options.padding : 0;
710
+ const includeBends = options.includeBends !== false;
711
+ const flipY = options.flipY === true;
712
+ const decimals = Number.isFinite(options.decimals) ? options.decimals : 5;
713
+
714
+ const outlines = Array.isArray(flatPattern?.outlines) ? flatPattern.outlines : [];
715
+ const holes = Array.isArray(flatPattern?.holes) ? flatPattern.holes : [];
716
+ const bends = Array.isArray(flatPattern?.bendLines) ? flatPattern.bendLines : [];
717
+
718
+ const transformPoint = (p) => {
719
+ const x = p.x * scale;
720
+ const y = p.y * scale;
721
+ return new THREE.Vector2(x, flipY ? -y : y);
722
+ };
723
+
724
+ let bounds = null;
725
+ const addBounds = (pt) => { bounds = updateBounds(bounds, pt); };
726
+
727
+ for (const loop of outlines) {
728
+ for (const p of loop) addBounds(transformPoint(p));
729
+ }
730
+ for (const loop of holes) {
731
+ for (const p of loop) addBounds(transformPoint(p));
732
+ }
733
+ if (includeBends) {
734
+ for (const seg of bends) {
735
+ addBounds(transformPoint(seg.p0));
736
+ addBounds(transformPoint(seg.p1));
737
+ }
738
+ }
739
+
740
+ if (!bounds) {
741
+ return '<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"/>';
742
+ }
743
+
744
+ const minX = bounds.min.x - padding;
745
+ const minY = bounds.min.y - padding;
746
+ const maxX = bounds.max.x + padding;
747
+ const maxY = bounds.max.y + padding;
748
+ const width = maxX - minX;
749
+ const height = maxY - minY;
750
+
751
+ const outlinePath = outlines
752
+ .map((loop) => polylineToPath(loop, true, transformPoint, decimals))
753
+ .filter(Boolean)
754
+ .join(' ');
755
+ const holePath = holes
756
+ .map((loop) => polylineToPath(loop, true, transformPoint, decimals))
757
+ .filter(Boolean)
758
+ .join(' ');
759
+ const bendPath = includeBends ? segmentsToPath(bends, transformPoint, decimals) : '';
760
+
761
+ const paths = [];
762
+ if (outlinePath) paths.push(`<path d="${outlinePath}" fill="none" stroke="black"/>`);
763
+ if (holePath) paths.push(`<path d="${holePath}" fill="none" stroke="black"/>`);
764
+ if (bendPath) paths.push(`<path d="${bendPath}" fill="none" stroke="black"/>`);
765
+
766
+ return [
767
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${formatNumber(width, decimals)}" height="${formatNumber(height, decimals)}" viewBox="${formatNumber(minX, decimals)} ${formatNumber(minY, decimals)} ${formatNumber(width, decimals)} ${formatNumber(height, decimals)}">`,
768
+ ...paths,
769
+ '</svg>',
770
+ ].join('');
771
+ }
772
+
773
+ function buildLineGeometryFromPolylines(polylines) {
774
+ const positions = [];
775
+ for (const loop of polylines) {
776
+ if (!loop || loop.length < 2) continue;
777
+ for (let i = 0; i < loop.length; i++) {
778
+ const a = loop[i];
779
+ const b = loop[(i + 1) % loop.length];
780
+ positions.push(a.x, a.y, 0, b.x, b.y, 0);
781
+ }
782
+ }
783
+ const geometry = new THREE.BufferGeometry();
784
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
785
+ return geometry;
786
+ }
787
+
788
+ function buildLineGeometryFromSegments(segments) {
789
+ const positions = [];
790
+ for (const seg of segments) {
791
+ positions.push(seg.p0.x, seg.p0.y, 0, seg.p1.x, seg.p1.y, 0);
792
+ }
793
+ const geometry = new THREE.BufferGeometry();
794
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
795
+ return geometry;
796
+ }
797
+
798
+ /**
799
+ * Build debug line segments for cut edges and bend lines.
800
+ * @param {FlatPattern} flatPattern
801
+ * @returns {{cut: THREE.LineSegments, bends: THREE.LineSegments}}
802
+ */
803
+ export function buildDebugLineSegments(flatPattern) {
804
+ const outlines = Array.isArray(flatPattern?.outlines) ? flatPattern.outlines : [];
805
+ const holes = Array.isArray(flatPattern?.holes) ? flatPattern.holes : [];
806
+ const bends = Array.isArray(flatPattern?.bendLines) ? flatPattern.bendLines : [];
807
+
808
+ const cutGeometry = buildLineGeometryFromPolylines([...outlines, ...holes]);
809
+ const bendGeometry = buildLineGeometryFromSegments(bends);
810
+
811
+ return {
812
+ cut: new THREE.LineSegments(cutGeometry, new THREE.LineBasicMaterial()),
813
+ bends: new THREE.LineSegments(bendGeometry, new THREE.LineBasicMaterial()),
814
+ };
815
+ }
816
+
817
+ /*
818
+ Example usage (ES modules):
819
+
820
+ import * as THREE from 'three';
821
+ import { unfoldSheetMetal, exportFlatPatternToSVG } from './sheetMetalUnfold.js';
822
+
823
+ // Build a simple two-face folded sheet.
824
+ const geometry = new THREE.BufferGeometry();
825
+ const positions = new Float32Array([
826
+ // Base face (z = 0)
827
+ 0, 0, 0, // 0
828
+ 1, 0, 0, // 1
829
+ 1, 1, 0, // 2
830
+ 0, 1, 0, // 3
831
+ // Flange face (y = 1, folded up)
832
+ 1, 1, 1, // 4
833
+ 0, 1, 1, // 5
834
+ ]);
835
+ const indices = new Uint16Array([
836
+ 0, 1, 2, 0, 2, 3, // base (faceId 0)
837
+ 3, 2, 4, 3, 4, 5, // flange (faceId 1)
838
+ ]);
839
+ geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
840
+ geometry.setIndex(new THREE.BufferAttribute(indices, 1));
841
+
842
+ const faceIds = new Uint16Array(indices.length / 3);
843
+ faceIds[0] = 0; faceIds[1] = 0;
844
+ faceIds[2] = 1; faceIds[3] = 1;
845
+ geometry.setAttribute('faceId', new THREE.BufferAttribute(faceIds, 1));
846
+
847
+ const bends = [{
848
+ bendId: 'B1',
849
+ aFaceId: 0,
850
+ bFaceId: 1,
851
+ axisPoint: new THREE.Vector3(0, 1, 0),
852
+ axisDir: new THREE.Vector3(1, 0, 0),
853
+ angleRad: Math.PI / 2,
854
+ radius: 0.05,
855
+ thickness: 0.1,
856
+ kFactor: 0.4,
857
+ upDown: 'up',
858
+ }];
859
+
860
+ const flat = unfoldSheetMetal(geometry, bends, { rootFaceId: 0 });
861
+ console.log(exportFlatPatternToSVG(flat, { includeBends: true }));
862
+ */