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,55 @@
1
+ export async function test_SweepFace(partHistory) {
2
+ const cone = await partHistory.newFeature("P.CO");
3
+ cone.inputParams.radiusTop = 3;
4
+ cone.inputParams.radiusBottom = .5;
5
+ cone.inputParams.height = 5.2;
6
+ cone.inputParams.resolution = 20;
7
+
8
+ // Build a simple sketch with a straight path edge of length 5 along +Z.
9
+ // We place the sketch on an XZ plane so the line from (0,0)→(0,5) maps to +Z in world.
10
+ const plane = await partHistory.newFeature("P");
11
+ plane.inputParams.orientation = "YZ";
12
+
13
+ const sketch = await partHistory.newFeature("S");
14
+ sketch.inputParams.sketchPlane = plane.inputParams.featureID;
15
+ // Define a minimal sketch: one path line (id:100) and a tiny closed loop so edges get added to the scene.
16
+ sketch.persistentData.sketch = {
17
+ points: [
18
+ { id: 0, x: 0, y: 0, fixed: true }, // ground
19
+ { id: 1, x: 8, y: 20, fixed: false }, // path end (+Z since XZ plane)
20
+ // tiny square to ensure a profile face is created (so edges are added to scene)
21
+ { id: 10, x: -0.5, y: -0.5, fixed: false },
22
+ { id: 11, x: 0.5, y: -0.5, fixed: false },
23
+ { id: 12, x: 0.5, y: 0.5, fixed: false },
24
+ { id: 13, x: -0.5, y: 0.5, fixed: false },
25
+ ],
26
+ geometries: [
27
+ // Path edge geometry (name will be G100)
28
+ { id: 100, type: "line", points: [0, 1], construction: false },
29
+ // Closed loop (small square) so the sketch emits edges and a face group
30
+ { id: 200, type: "line", points: [10, 11], construction: false },
31
+ { id: 201, type: "line", points: [11, 12], construction: false },
32
+ { id: 202, type: "line", points: [12, 13], construction: false },
33
+ { id: 203, type: "line", points: [13, 10], construction: false },
34
+ ],
35
+ constraints: [
36
+ { id: 0, type: "⏚", points: [0] }, // ground point 0
37
+ ],
38
+ };
39
+
40
+ // Create the path-based Sweep from the cone's top face, following the sketch edge G100.
41
+ const sweep = await partHistory.newFeature("SW");
42
+ sweep.inputParams.profile = `${cone.inputParams.featureID}_T`;
43
+ sweep.inputParams.path = [`${sketch.inputParams.featureID}:G100`]; // resolve to the sketch edge created above
44
+ sweep.inputParams.orientationMode = "translate"; // default, but make explicit
45
+
46
+ // perform a boolean operation between the 2 solids.
47
+ const boolean = await partHistory.newFeature("B");
48
+ boolean.inputParams.targetSolid = cone.inputParams.featureID;
49
+ boolean.inputParams.boolean = {
50
+ targets: [sweep.inputParams.featureID],
51
+ operation: "UNION",
52
+ };
53
+
54
+ return partHistory;
55
+ }
@@ -0,0 +1,45 @@
1
+ export async function test_tube(partHistory) {
2
+ const plane = await partHistory.newFeature("P");
3
+ plane.inputParams.orientation = "XY";
4
+
5
+ const sketch = await partHistory.newFeature("S");
6
+ sketch.inputParams.sketchPlane = plane.inputParams.featureID;
7
+ sketch.persistentData.sketch = {
8
+ points: [
9
+ { id: 0, x: 0, y: 0, fixed: true },
10
+ { id: 1, x: 0, y: 40, fixed: false },
11
+ { id: 2, x: 25, y: 40, fixed: false },
12
+ { id: 10, x: -2, y: -2, fixed: false },
13
+ { id: 11, x: 2, y: -2, fixed: false },
14
+ { id: 12, x: 2, y: 2, fixed: false },
15
+ { id: 13, x: -2, y: 2, fixed: false },
16
+ ],
17
+ geometries: [
18
+ { id: 200, type: "line", points: [0, 1], construction: false },
19
+ { id: 201, type: "line", points: [1, 2], construction: false },
20
+ { id: 300, type: "line", points: [10, 11], construction: false },
21
+ { id: 301, type: "line", points: [11, 12], construction: false },
22
+ { id: 302, type: "line", points: [12, 13], construction: false },
23
+ { id: 303, type: "line", points: [13, 10], construction: false },
24
+ ],
25
+ constraints: [
26
+ { id: 0, type: "⏚", points: [0] },
27
+ ],
28
+ };
29
+
30
+ const edgePrefix = `${sketch.inputParams.featureID}:`;
31
+
32
+ const solidTube = await partHistory.newFeature("TU");
33
+ solidTube.inputParams.path = [`${edgePrefix}G200`, `${edgePrefix}G201`];
34
+ solidTube.inputParams.radius = 4;
35
+ solidTube.inputParams.innerRadius = 0;
36
+ solidTube.inputParams.resolution = 32;
37
+
38
+ const hollowTube = await partHistory.newFeature("TU");
39
+ hollowTube.inputParams.path = [`${edgePrefix}G200`, `${edgePrefix}G201`];
40
+ hollowTube.inputParams.radius = 5;
41
+ hollowTube.inputParams.innerRadius = 2;
42
+ hollowTube.inputParams.resolution = 48;
43
+
44
+ return partHistory;
45
+ }
@@ -0,0 +1,67 @@
1
+ export async function test_tube_closedLoop(partHistory) {
2
+ const plane = await partHistory.newFeature("P");
3
+ plane.inputParams.orientation = "XY";
4
+
5
+ const sketch = await partHistory.newFeature("S");
6
+ sketch.inputParams.sketchPlane = plane.inputParams.featureID;
7
+
8
+ // Create a closed loop path (hexagon)
9
+ const numSides = 6;
10
+ const radius = 20;
11
+ const points = [];
12
+ const geometries = [];
13
+
14
+ // Generate hexagon points
15
+ for (let i = 0; i < numSides; i++) {
16
+ const angle = (i / numSides) * 2 * Math.PI;
17
+ points.push({
18
+ id: i,
19
+ x: Math.cos(angle) * radius,
20
+ y: Math.sin(angle) * radius,
21
+ fixed: false
22
+ });
23
+ }
24
+
25
+ // Create line segments between consecutive points
26
+ for (let i = 0; i < numSides; i++) {
27
+ const nextI = (i + 1) % numSides;
28
+ geometries.push({
29
+ id: 200 + i,
30
+ type: "line",
31
+ points: [i, nextI],
32
+ construction: false
33
+ });
34
+ }
35
+
36
+ sketch.persistentData.sketch = {
37
+ points: points,
38
+ geometries: geometries,
39
+ constraints: [
40
+ { id: 0, type: "⏚", points: [0] }, // Fix the first point
41
+ ],
42
+ };
43
+
44
+ const edgePrefix = `${sketch.inputParams.featureID}:`;
45
+
46
+ // Create a solid tube with the closed loop path
47
+ const solidTube = await partHistory.newFeature("TU");
48
+ solidTube.inputParams.path = geometries.map(g => `${edgePrefix}G${g.id}`);
49
+ solidTube.inputParams.radius = 3;
50
+ solidTube.inputParams.innerRadius = 0;
51
+ solidTube.inputParams.resolution = 24;
52
+
53
+ // Create a hollow tube with the closed loop path
54
+ const hollowTube = await partHistory.newFeature("TU");
55
+ hollowTube.inputParams.path = geometries.map(g => `${edgePrefix}G${g.id}`);
56
+ hollowTube.inputParams.radius = 4;
57
+ hollowTube.inputParams.innerRadius = 1;
58
+ hollowTube.inputParams.resolution = 24;
59
+ // Offset the hollow tube vertically so we can see both
60
+ hollowTube.inputParams.transform = {
61
+ position: [0, 0, 10],
62
+ rotation: [0, 0, 0],
63
+ scale: [1, 1, 1]
64
+ };
65
+
66
+ return partHistory;
67
+ }
@@ -0,0 +1,493 @@
1
+ import { posix as path } from '../path.proxy.js';
2
+ import { fs } from '../fs.proxy.js';
3
+ import { PartHistory } from "../PartHistory.js";
4
+ import { test_primitiveCube } from './test_primitiveCube.js';
5
+ import { test_solidMetrics, afterRun_solidMetrics } from './test_solidMetrics.js';
6
+ import { test_primitiveCylinder } from './test_primitiveCylinder.js';
7
+ import { test_plane } from './test_plane.js';
8
+ import { test_primitiveCone } from './test_primitiveCone.js';
9
+ import { test_primitiveTorus } from './test_primitiveTorus.js';
10
+ import { test_boolean_subtract } from './test_boolean_subtract.js';
11
+ import { test_primitiveSphere } from './test_primitiveSphere.js';
12
+ import { test_primitivePyramid } from './test_primitivePyramid.js';
13
+ import { test_stlLoader } from './test_stlLoader.js';
14
+ import { test_SweepFace } from './test_sweepFace.js';
15
+ import { test_ExtrudeFace } from './test_extrudeFace.js';
16
+ import { test_Fillet } from './test_fillet.js';
17
+ import { test_Chamfer } from './test_chamfer.js';
18
+ import { test_mirror } from './test_mirror.js';
19
+ import { test_fillets_more_dificult } from './test_filletsMoreDifficult.js';
20
+ import { test_tube } from './test_tube.js';
21
+ import { test_tube_closedLoop } from './test_tube_closedLoop.js';
22
+ import { test_offsetShellGrouping } from './test_offsetShellGrouping.js';
23
+ import { test_sheetMetal_tab, test_sheetMetal_flange, test_sheetMetal_hem, test_sheetMetal_cutout } from './test_sheetMetal_features.js';
24
+ import {
25
+ test_SheetMetalContourFlange_Basic,
26
+ test_SheetMetalContourFlange_StraightLine,
27
+ afterRun_SheetMetalContourFlange_Basic,
28
+ afterRun_SheetMetalContourFlange_StraightLine,
29
+ } from './test_sheetMetalContourFlange.js';
30
+ import { test_pushFace, afterRun_pushFace } from './test_pushFace.js';
31
+ import { test_sketch_openLoop, afterRun_sketch_openLoop } from './test_sketch_openLoop.js';
32
+ import { test_Fillet_NonClosed, afterRun_Fillet_NonClosed } from './test_fillet_nonClosed.js';
33
+ import { test_history_features_basic, afterRun_history_features_basic } from './test_history_features_basic.js';
34
+ import { generate3MF } from '../exporters/threeMF.js';
35
+ import { buildSheetMetalFlatPatternSvgs } from '../exporters/sheetMetalFlatPattern.js';
36
+ import {
37
+ test_hole_through,
38
+ afterRun_hole_through,
39
+ test_hole_thread_symbolic,
40
+ afterRun_hole_thread_symbolic,
41
+ test_hole_thread_modeled,
42
+ afterRun_hole_thread_modeled,
43
+ test_hole_countersink,
44
+ afterRun_hole_countersink,
45
+ test_hole_counterbore,
46
+ afterRun_hole_counterbore,
47
+ } from './test_hole.js';
48
+
49
+ const IS_NODE_RUNTIME = typeof process !== 'undefined' && process.versions && process.versions.node && typeof window === 'undefined';
50
+ const TEST_LOG_PATH = path.join('tests', 'test-run.log');
51
+
52
+
53
+ export const testFunctions = [
54
+ { test: test_plane, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
55
+ { test: test_primitiveCube, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
56
+ { test: test_primitivePyramid, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
57
+ { test: test_primitiveCylinder, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
58
+ { test: test_primitiveCone, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
59
+ { test: test_primitiveTorus, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
60
+ { test: test_primitiveSphere, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
61
+ { test: test_boolean_subtract, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
62
+ { test: test_stlLoader, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
63
+ { test: test_SweepFace, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
64
+ { test: test_tube, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
65
+ { test: test_tube_closedLoop, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
66
+ { test: test_sketch_openLoop, afterRun: afterRun_sketch_openLoop, printArtifacts: false, exportFaces: false, exportSolids: false, resetHistory: true },
67
+ { test: test_offsetShellGrouping, printArtifacts: false, exportFaces: false, exportSolids: false, resetHistory: true },
68
+ { test: test_sheetMetal_tab, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
69
+ { test: test_sheetMetal_flange, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
70
+ { test: test_sheetMetal_hem, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
71
+ { test: test_sheetMetal_cutout, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
72
+ { test: test_SheetMetalContourFlange_Basic, afterRun: afterRun_SheetMetalContourFlange_Basic, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
73
+ { test: test_SheetMetalContourFlange_StraightLine, afterRun: afterRun_SheetMetalContourFlange_StraightLine, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
74
+ { test: test_ExtrudeFace, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
75
+ { test: test_Fillet, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
76
+ { test: test_Fillet_NonClosed, afterRun: afterRun_Fillet_NonClosed, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
77
+ { test: test_fillets_more_dificult, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
78
+ { test: test_Chamfer, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
79
+ { test: test_hole_through, afterRun: afterRun_hole_through, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
80
+ { test: test_hole_countersink, afterRun: afterRun_hole_countersink, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
81
+ { test: test_hole_counterbore, afterRun: afterRun_hole_counterbore, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
82
+ { test: test_hole_thread_symbolic, afterRun: afterRun_hole_thread_symbolic, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
83
+ { test: test_hole_thread_modeled, afterRun: afterRun_hole_thread_modeled, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
84
+ { test: test_pushFace, afterRun: afterRun_pushFace, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
85
+ { test: test_mirror, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
86
+ { test: test_history_features_basic, afterRun: afterRun_history_features_basic, printArtifacts: false, exportFaces: true, exportSolids: true, resetHistory: true },
87
+ { test: test_solidMetrics, afterRun: afterRun_solidMetrics, printArtifacts: true, exportFaces: true, exportSolids: true, resetHistory: true },
88
+
89
+ ];
90
+
91
+ // Dynamically register tests to import part files from src/tests/partFiles (Node only)
92
+ async function registerPartFileTests() {
93
+ if (!(typeof process !== 'undefined' && process.versions && process.versions.node)) return;
94
+ try {
95
+ const partsDir = 'src/tests/partFiles';
96
+ // Use async API to avoid ESM sync-fs readiness issues
97
+ try {
98
+ const entries = await fs.promises.readdir(partsDir);
99
+ const files = entries.filter(f => typeof f === 'string' && f.toLowerCase().endsWith('.json'));
100
+ for (const file of files) {
101
+ const filePath = path.join(partsDir, file);
102
+ const baseName = String(file).replace(/\.[^.]+$/, '');
103
+ const safeName = baseName.replace(/[^a-zA-Z0-9._-]+/g, '_').substring(0, 100);
104
+ const testName = `import_part_${safeName}`;
105
+ const isSheetMetalHem = file.toLowerCase() === 'sheetmetalhem.brep.json';
106
+
107
+ const importTest = async function (partHistory) {
108
+ // Read file and load into PartHistory
109
+ const content = await fs.promises.readFile(filePath, 'utf8');
110
+ let payload = content;
111
+ try {
112
+ const obj = JSON.parse(content);
113
+ if (obj && typeof obj === 'object') {
114
+ if (Array.isArray(obj.features)) {
115
+ payload = JSON.stringify(obj);
116
+ } else if (obj.data) {
117
+ payload = (typeof obj.data === 'string') ? obj.data : JSON.stringify(obj.data);
118
+ }
119
+ }
120
+ } catch (_) {
121
+ // invalid JSON; let runSingleTest catch and report when fromJSON or runHistory runs
122
+ }
123
+ await partHistory.reset();
124
+ await partHistory.fromJSON(payload);
125
+ // runHistory will be called by runSingleTest()
126
+ };
127
+ try { Object.defineProperty(importTest, 'name', { value: testName, configurable: true }); } catch {}
128
+
129
+ const afterRun = isSheetMetalHem ? async function (partHistory) {
130
+ const solids = (partHistory.scene?.children || []).filter(o => o && o.type === 'SOLID' && typeof o.getMesh === 'function');
131
+ const svgEntries = buildSheetMetalFlatPatternSvgs(solids, {
132
+ metadataManager: partHistory?.metadataManager || null,
133
+ });
134
+ if (!svgEntries.length) {
135
+ console.warn(`[runTests] No flat pattern SVG produced for ${testName}`);
136
+ return;
137
+ }
138
+ const exportPath = `./tests/results/${testName}/`;
139
+ for (const entry of svgEntries) {
140
+ const safeEntryName = sanitizeFileName(entry.name || 'SHEET');
141
+ const outPath = path.join(exportPath, `${safeEntryName}_flat-pattern.svg`);
142
+ writeFile(outPath, entry.svg);
143
+ }
144
+ } : null;
145
+
146
+ testFunctions.push({
147
+ test: importTest,
148
+ afterRun,
149
+ printArtifacts: false,
150
+ exportFaces: true,
151
+ exportSolids: true,
152
+ resetHistory: true,
153
+ allowErrors: true,
154
+ _sourceFile: filePath,
155
+ });
156
+ }
157
+ } catch (e) {
158
+ // Directory may not exist; ignore silently in CI
159
+ }
160
+ } catch (e) {
161
+ console.warn('Failed to register part file import tests:', e?.message || e);
162
+ }
163
+ }
164
+
165
+
166
+
167
+ // call runTests automatically when executed under Node.js
168
+ if (IS_NODE_RUNTIME) {
169
+ runTests()
170
+ .then(() => {
171
+ // ensure CLI exits promptly once the suite finishes
172
+ process.exit(0);
173
+ })
174
+ .catch((err) => {
175
+ console.error(err);
176
+ process.exit(1);
177
+ });
178
+ }
179
+
180
+
181
+
182
+
183
+ export async function runTests(partHistory = new PartHistory(), callbackToRunBetweenTests = null) {
184
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
185
+ //await console.clear();
186
+ }
187
+
188
+ if (IS_NODE_RUNTIME) {
189
+ resetTestLog();
190
+ logTestEvent('Test run started');
191
+ }
192
+
193
+ // delete the ./tests/results directory in an asynchronous way
194
+ await fs.promises.rm('./tests/results', { recursive: true, force: true });
195
+
196
+ // Discover and register part-file import tests (Node only)
197
+ await registerPartFileTests();
198
+
199
+ for (const testFunction of testFunctions) {
200
+ const isLastTest = testFunction === testFunctions[testFunctions.length - 1];
201
+ await partHistory.reset();
202
+
203
+ if (testFunction.resetHistory) partHistory.features = [];
204
+
205
+ const testName = (testFunction?.test?.name && String(testFunction.test.name)) || 'unnamed_test';
206
+ if (IS_NODE_RUNTIME) logTestEvent(`Starting ${testName}`);
207
+
208
+ let handledError = null;
209
+ try {
210
+ handledError = await runSingleTest(testFunction, partHistory);
211
+ } catch (err) {
212
+ if (IS_NODE_RUNTIME) logTestEvent(`Test ${testName} failed: ${stringifyError(err)}`);
213
+ throw err;
214
+ }
215
+
216
+ if (IS_NODE_RUNTIME) {
217
+ if (handledError) logTestEvent(`Test ${testName} completed with handled error: ${stringifyError(handledError)}`);
218
+ else logTestEvent(`Test ${testName} completed successfully`);
219
+ }
220
+
221
+ if (typeof window !== "undefined") {
222
+ if (typeof callbackToRunBetweenTests === 'function') {
223
+ await callbackToRunBetweenTests(partHistory, isLastTest);
224
+ }
225
+ } else {
226
+ // run each test and export the results to a folder ./tests/results/<testFunction name>/
227
+ const exportName = testFunction.test.name;
228
+ const exportPath = `./tests/results/${exportName}/`;
229
+ // create the directory if it does not exist
230
+ if (!fs.existsSync(exportPath)) {
231
+ fs.mkdirSync(exportPath, { recursive: true });
232
+ }
233
+
234
+ // Collect SOLID nodes from the scene
235
+ const solids = (partHistory.scene?.children || []).filter(o => o && o.type === 'SOLID' && typeof o.toSTL === 'function');
236
+
237
+ // Export solids (triggered by either flag for convenience)
238
+ if (testFunction.exportSolids || testFunction.printArtifacts) {
239
+ solids.forEach((solid, idx) => {
240
+ const rawName = solid.name && String(solid.name).trim().length ? String(solid.name) : `solid_${idx}`;
241
+ const safeName = sanitizeFileName(rawName);
242
+ let stl = "";
243
+ try {
244
+ stl = solid.toSTL(safeName, 6);
245
+ } catch (e) {
246
+ console.warn(`[runTests] toSTL failed for solid ${rawName}:`, e?.message || e);
247
+ return;
248
+ }
249
+ const outPath = path.join(exportPath, `${safeName}.stl`);
250
+ writeFile(outPath, stl);
251
+ });
252
+ }
253
+
254
+ // Export faces per solid
255
+ if (testFunction.exportFaces) {
256
+ solids.forEach((solid, sidx) => {
257
+ const rawName = solid.name && String(solid.name).trim().length ? String(solid.name) : `solid_${sidx}`;
258
+ const safeSolid = sanitizeFileName(rawName);
259
+ let faces = [];
260
+ try {
261
+ faces = typeof solid.getFaces === 'function' ? solid.getFaces(false) : [];
262
+ } catch {
263
+ faces = [];
264
+ }
265
+ faces.forEach(({ faceName, triangles }, fIdx) => {
266
+ if (!triangles || triangles.length === 0) return;
267
+ const rawFace = faceName || `face_${fIdx}`;
268
+ const safeFace = sanitizeFileName(rawFace);
269
+ const stl = trianglesToAsciiSTL(`${safeSolid}_${safeFace}`, triangles);
270
+ const outPath = path.join(exportPath, `${safeSolid}_${safeFace}.stl`);
271
+ writeFile(outPath, stl);
272
+ });
273
+ });
274
+ }
275
+
276
+ // Export 3MF with embedded feature history
277
+ await export3mfArtifact({
278
+ partHistory,
279
+ exportName,
280
+ exportPath,
281
+ solids,
282
+ });
283
+
284
+ }
285
+ }
286
+
287
+ if (IS_NODE_RUNTIME) logTestEvent('Test run finished');
288
+ }
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+ export async function runSingleTest(testFunction, partHistory = new PartHistory()) {
300
+ let error = null;
301
+ try {
302
+ await testFunction.test(partHistory);
303
+ await partHistory.runHistory();
304
+ // Optional per-test post-run hook for validations/metrics
305
+ if (typeof testFunction.afterRun === 'function') {
306
+ try { await testFunction.afterRun(partHistory); } catch (e) { console.warn('afterRun failed:', e?.message || e); }
307
+ }
308
+ } catch (e) {
309
+ error = e;
310
+ if (testFunction.allowErrors) {
311
+ const name = (testFunction.test && testFunction.test.name) ? testFunction.test.name : 'unnamed_test';
312
+ const exportPath = `./tests/results/${name}/`;
313
+ try { if (!fs.existsSync(exportPath)) fs.mkdirSync(exportPath, { recursive: true }); } catch {}
314
+ const msg = `${e?.message || e}\n\n${e?.stack || ''}`;
315
+ try { writeFile(path.join(exportPath, 'error.txt'), msg); } catch {}
316
+ console.error(`Error in test ${name}:`, e);
317
+ } else {
318
+ // rethrow to fail fast for normal tests
319
+ throw e;
320
+ }
321
+ }
322
+ // sleep for 1 second to allow any async operations to complete
323
+ await new Promise(resolve => setTimeout(resolve, 1000));
324
+
325
+ return error;
326
+ }
327
+
328
+
329
+ // function to write a file. If the path dose not exist it should make the folders needed.
330
+ function writeFile(filePath, content) {
331
+ // imediatly return if running in the browser
332
+ if (typeof window !== "undefined") {
333
+ //console.warn(`writeFile is not supported in the browser.`);
334
+ return;
335
+ }
336
+
337
+ try {
338
+ const dir = path.dirname(filePath);
339
+ if (!fs.existsSync(dir)) {
340
+ fs.mkdirSync(dir, { recursive: true });
341
+ }
342
+ fs.writeFileSync(filePath, content, 'utf8');
343
+ } catch (error) {
344
+ console.log(`Error writing file ${filePath}:`, error);
345
+ }
346
+ }
347
+
348
+ function writeBinaryFile(filePath, content) {
349
+ if (typeof window !== "undefined") {
350
+ return;
351
+ }
352
+ try {
353
+ const dir = path.dirname(filePath);
354
+ if (!fs.existsSync(dir)) {
355
+ fs.mkdirSync(dir, { recursive: true });
356
+ }
357
+ fs.writeFileSync(filePath, content);
358
+ } catch (error) {
359
+ console.log(`Error writing file ${filePath}:`, error);
360
+ }
361
+ }
362
+
363
+ function appendToFile(filePath, content) {
364
+ if (!IS_NODE_RUNTIME) return;
365
+ try {
366
+ const dir = path.dirname(filePath);
367
+ if (!fs.existsSync(dir)) {
368
+ fs.mkdirSync(dir, { recursive: true });
369
+ }
370
+ fs.appendFileSync(filePath, content, 'utf8');
371
+ } catch (error) {
372
+ console.log(`Error appending file ${filePath}:`, error);
373
+ }
374
+ }
375
+
376
+ function resetTestLog() {
377
+ if (!IS_NODE_RUNTIME) return;
378
+ writeFile(TEST_LOG_PATH, '');
379
+ }
380
+
381
+ function logTestEvent(message) {
382
+ if (!IS_NODE_RUNTIME) return;
383
+ const timestamp = new Date().toISOString();
384
+ appendToFile(TEST_LOG_PATH, `[${timestamp}] ${message}\n`);
385
+ }
386
+
387
+ function stringifyError(err) {
388
+ if (!err) return 'Unknown error';
389
+ if (typeof err === 'string') return err;
390
+ const message = err?.message || err?.toString?.() || 'Unknown error';
391
+ return err?.stack ? `${message}\n${err.stack}` : message;
392
+ }
393
+
394
+ // ---------------- Local helpers for artifact export (Node only) ----------------
395
+
396
+ function sanitizeFileName(name) {
397
+ return String(name)
398
+ .replace(/[^a-zA-Z0-9._-]+/g, '_') // collapse invalid chars
399
+ .replace(/^_+|_+$/g, '') // trim leading/trailing underscores
400
+ .substring(0, 100) || 'artifact'; // cap length
401
+ }
402
+
403
+ function trianglesToAsciiSTL(name, tris) {
404
+ const fmt = (n) => Number.isFinite(n) ? (Math.abs(n) < 1e-18 ? '0' : n.toFixed(6)) : '0';
405
+ const out = [];
406
+ out.push(`solid ${name}`);
407
+ for (let i = 0; i < tris.length; i++) {
408
+ const t = tris[i];
409
+ const p0 = t.p1, p1 = t.p2, p2 = t.p3;
410
+ const ux = p1[0] - p0[0], uy = p1[1] - p0[1], uz = p1[2] - p0[2];
411
+ const vx = p2[0] - p0[0], vy = p2[1] - p0[1], vz = p2[2] - p0[2];
412
+ let nx = uy * vz - uz * vy;
413
+ let ny = uz * vx - ux * vz;
414
+ let nz = ux * vy - uy * vx;
415
+ const len = Math.hypot(nx, ny, nz) || 1;
416
+ nx /= len; ny /= len; nz /= len;
417
+ out.push(` facet normal ${fmt(nx)} ${fmt(ny)} ${fmt(nz)}`);
418
+ out.push(` outer loop`);
419
+ out.push(` vertex ${fmt(p0[0])} ${fmt(p0[1])} ${fmt(p0[2])}`);
420
+ out.push(` vertex ${fmt(p1[0])} ${fmt(p1[1])} ${fmt(p1[2])}`);
421
+ out.push(` vertex ${fmt(p2[0])} ${fmt(p2[1])} ${fmt(p2[2])}`);
422
+ out.push(` endloop`);
423
+ out.push(` endfacet`);
424
+ }
425
+ out.push(`endsolid ${name}`);
426
+ return out.join('\n');
427
+ }
428
+
429
+ async function export3mfArtifact({ partHistory, exportName, exportPath, solids }) {
430
+ let historyJson = null;
431
+ try {
432
+ const json = await partHistory?.toJSON?.();
433
+ if (json) historyJson = (typeof json === 'string') ? json : JSON.stringify(json);
434
+ } catch (e) {
435
+ console.warn(`[runTests] Failed to serialize feature history for ${exportName}:`, e?.message || e);
436
+ }
437
+
438
+ const additionalFiles = historyJson ? { 'Metadata/featureHistory.json': historyJson } : undefined;
439
+ const modelMetadata = historyJson ? { featureHistoryPath: '/Metadata/featureHistory.json' } : undefined;
440
+ const metadataManager = partHistory?.metadataManager || null;
441
+
442
+ const solidsForExport = [];
443
+ (solids || []).forEach((s, idx) => {
444
+ try {
445
+ const mesh = s?.getMesh?.();
446
+ const canExport = !!(mesh && mesh.vertProperties && mesh.triVerts);
447
+ if (mesh && typeof mesh.delete === 'function') {
448
+ try { mesh.delete(); } catch { }
449
+ }
450
+ if (canExport) {
451
+ solidsForExport.push(s);
452
+ } else {
453
+ const name = sanitizeFileName(s?.name || `solid_${idx}`);
454
+ console.warn(`[runTests] Skipping non-manifold solid for 3MF: ${name}`);
455
+ }
456
+ } catch {
457
+ const name = sanitizeFileName(s?.name || `solid_${idx}`);
458
+ console.warn(`[runTests] Skipping solid that failed to export for 3MF: ${name}`);
459
+ }
460
+ });
461
+
462
+ const safeName = sanitizeFileName(exportName || 'partHistory');
463
+ const outPath = path.join(exportPath, `${safeName}.3mf`);
464
+ try {
465
+ let data = null;
466
+ try {
467
+ data = await generate3MF(solidsForExport, {
468
+ unit: 'millimeter',
469
+ precision: 6,
470
+ scale: 1,
471
+ additionalFiles,
472
+ modelMetadata,
473
+ metadataManager,
474
+ });
475
+ } catch (e) {
476
+ data = await generate3MF([], {
477
+ unit: 'millimeter',
478
+ precision: 6,
479
+ scale: 1,
480
+ additionalFiles,
481
+ modelMetadata,
482
+ metadataManager,
483
+ });
484
+ }
485
+ if (data && data.length) {
486
+ writeBinaryFile(outPath, data);
487
+ } else {
488
+ console.warn(`[runTests] 3MF export returned empty payload for ${exportName}`);
489
+ }
490
+ } catch (e) {
491
+ console.warn(`[runTests] 3MF export failed for ${exportName}:`, e?.message || e);
492
+ }
493
+ }