brep-io-kernel 1.0.0-ci.9

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 (271) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +154 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/package.json +58 -0
  5. package/src/BREP/AssemblyComponent.js +42 -0
  6. package/src/BREP/BREP.js +43 -0
  7. package/src/BREP/BetterSolid.js +805 -0
  8. package/src/BREP/Edge.js +103 -0
  9. package/src/BREP/Extrude.js +403 -0
  10. package/src/BREP/Face.js +187 -0
  11. package/src/BREP/MeshRepairer.js +634 -0
  12. package/src/BREP/OffsetShellSolid.js +614 -0
  13. package/src/BREP/PointCloudWrap.js +302 -0
  14. package/src/BREP/Revolve.js +345 -0
  15. package/src/BREP/SolidMethods/authoring.js +112 -0
  16. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  17. package/src/BREP/SolidMethods/chamfer.js +122 -0
  18. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  19. package/src/BREP/SolidMethods/fillet.js +792 -0
  20. package/src/BREP/SolidMethods/index.js +72 -0
  21. package/src/BREP/SolidMethods/io.js +105 -0
  22. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  23. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  24. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  25. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  26. package/src/BREP/SolidMethods/metadata.js +106 -0
  27. package/src/BREP/SolidMethods/metrics.js +51 -0
  28. package/src/BREP/SolidMethods/transforms.js +361 -0
  29. package/src/BREP/SolidMethods/visualize.js +508 -0
  30. package/src/BREP/SolidShared.js +26 -0
  31. package/src/BREP/Sweep.js +1596 -0
  32. package/src/BREP/Tube.js +857 -0
  33. package/src/BREP/Vertex.js +43 -0
  34. package/src/BREP/applyBooleanOperation.js +704 -0
  35. package/src/BREP/boundsUtils.js +48 -0
  36. package/src/BREP/chamfer.js +551 -0
  37. package/src/BREP/edgePolylineUtils.js +85 -0
  38. package/src/BREP/fillets/common.js +388 -0
  39. package/src/BREP/fillets/fillet.js +1422 -0
  40. package/src/BREP/fillets/filletGeometry.js +15 -0
  41. package/src/BREP/fillets/inset.js +389 -0
  42. package/src/BREP/fillets/offsetHelper.js +143 -0
  43. package/src/BREP/fillets/outset.js +88 -0
  44. package/src/BREP/helix.js +193 -0
  45. package/src/BREP/meshToBrep.js +234 -0
  46. package/src/BREP/primitives.js +279 -0
  47. package/src/BREP/setupManifold.js +71 -0
  48. package/src/BREP/threadGeometry.js +1120 -0
  49. package/src/BREP/triangleUtils.js +8 -0
  50. package/src/BREP/triangulate.js +608 -0
  51. package/src/FeatureRegistry.js +183 -0
  52. package/src/PartHistory.js +1132 -0
  53. package/src/UI/AccordionWidget.js +292 -0
  54. package/src/UI/CADmaterials.js +850 -0
  55. package/src/UI/EnvMonacoEditor.js +522 -0
  56. package/src/UI/FloatingWindow.js +396 -0
  57. package/src/UI/HistoryWidget.js +457 -0
  58. package/src/UI/MainToolbar.js +131 -0
  59. package/src/UI/ModelLibraryView.js +194 -0
  60. package/src/UI/OrthoCameraIdle.js +206 -0
  61. package/src/UI/PluginsWidget.js +280 -0
  62. package/src/UI/SceneListing.js +606 -0
  63. package/src/UI/SelectionFilter.js +629 -0
  64. package/src/UI/ViewCube.js +389 -0
  65. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  66. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  67. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  68. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  69. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  70. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  71. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  72. package/src/UI/assembly/constraintPointUtils.js +64 -0
  73. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  74. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  75. package/src/UI/componentSelectorModal.js +240 -0
  76. package/src/UI/controls/CombinedTransformControls.js +386 -0
  77. package/src/UI/dialogs.js +351 -0
  78. package/src/UI/expressionsManager.js +100 -0
  79. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  80. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  81. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  82. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  83. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  84. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  85. package/src/UI/featureDialogWidgets/index.js +34 -0
  86. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  87. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  88. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  89. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  90. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  91. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  92. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  93. package/src/UI/featureDialogWidgets/utils.js +43 -0
  94. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  95. package/src/UI/featureDialogs.js +1414 -0
  96. package/src/UI/fileManagerWidget.js +615 -0
  97. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  98. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  99. package/src/UI/history/historyDisplayInfo.js +133 -0
  100. package/src/UI/mobile.js +28 -0
  101. package/src/UI/objectDump.js +442 -0
  102. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  103. package/src/UI/pmi/AnnotationHistory.js +353 -0
  104. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  105. package/src/UI/pmi/BaseAnnotation.js +269 -0
  106. package/src/UI/pmi/LabelOverlay.css +102 -0
  107. package/src/UI/pmi/LabelOverlay.js +191 -0
  108. package/src/UI/pmi/PMIMode.js +1550 -0
  109. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  110. package/src/UI/pmi/annUtils.js +729 -0
  111. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  112. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  113. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  114. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  115. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  116. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  117. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  118. package/src/UI/pmi/pmiStyle.js +44 -0
  119. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  120. package/src/UI/sketcher/dimensions.js +674 -0
  121. package/src/UI/sketcher/glyphs.js +236 -0
  122. package/src/UI/sketcher/highlights.js +60 -0
  123. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  124. package/src/UI/toolbarButtons/exportButton.js +609 -0
  125. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  126. package/src/UI/toolbarButtons/importButton.js +160 -0
  127. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  128. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  129. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  130. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  131. package/src/UI/toolbarButtons/saveButton.js +99 -0
  132. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  133. package/src/UI/toolbarButtons/testsButton.js +26 -0
  134. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  135. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  136. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  137. package/src/UI/triangleDebuggerWindow.js +945 -0
  138. package/src/UI/viewer.js +4228 -0
  139. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  140. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  141. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  142. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  143. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  144. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  145. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  146. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  147. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  148. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  149. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  150. package/src/core/entities/HistoryCollectionBase.js +72 -0
  151. package/src/core/entities/ListEntityBase.js +109 -0
  152. package/src/core/entities/schemaProcesser.js +121 -0
  153. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  154. package/src/exporters/sheetMetalUnfold.js +862 -0
  155. package/src/exporters/step.js +1135 -0
  156. package/src/exporters/threeMF.js +575 -0
  157. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  158. package/src/features/boolean/BooleanFeature.js +94 -0
  159. package/src/features/chamfer/ChamferFeature.js +116 -0
  160. package/src/features/datium/DatiumFeature.js +80 -0
  161. package/src/features/edgeFeatureUtils.js +41 -0
  162. package/src/features/extrude/ExtrudeFeature.js +143 -0
  163. package/src/features/fillet/FilletFeature.js +197 -0
  164. package/src/features/helix/HelixFeature.js +405 -0
  165. package/src/features/hole/HoleFeature.js +1050 -0
  166. package/src/features/hole/screwClearance.js +86 -0
  167. package/src/features/hole/threadDesignationCatalog.js +149 -0
  168. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  169. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  170. package/src/features/imageToFace/imageEditor.js +1270 -0
  171. package/src/features/imageToFace/traceUtils.js +971 -0
  172. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  173. package/src/features/loft/LoftFeature.js +605 -0
  174. package/src/features/mirror/MirrorFeature.js +151 -0
  175. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  176. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  177. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  178. package/src/features/pattern/PatternFeature.js +275 -0
  179. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  180. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  181. package/src/features/plane/PlaneFeature.js +154 -0
  182. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  183. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  184. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  185. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  186. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  187. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  188. package/src/features/remesh/RemeshFeature.js +97 -0
  189. package/src/features/revolve/RevolveFeature.js +111 -0
  190. package/src/features/selectionUtils.js +118 -0
  191. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  192. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  193. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  194. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  195. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  196. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  197. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  198. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  199. package/src/features/sheetMetal/profileUtils.js +25 -0
  200. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  201. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  202. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  203. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  204. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  205. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  206. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  207. package/src/features/sketch/SketchFeature.js +955 -0
  208. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  209. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  210. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  211. package/src/features/spline/SplineEditorSession.js +988 -0
  212. package/src/features/spline/SplineFeature.js +1388 -0
  213. package/src/features/spline/splineUtils.js +218 -0
  214. package/src/features/sweep/SweepFeature.js +110 -0
  215. package/src/features/transform/TransformFeature.js +152 -0
  216. package/src/features/tube/TubeFeature.js +635 -0
  217. package/src/fs.proxy.js +625 -0
  218. package/src/idbStorage.js +254 -0
  219. package/src/index.js +12 -0
  220. package/src/main.js +15 -0
  221. package/src/metadataManager.js +64 -0
  222. package/src/path.proxy.js +277 -0
  223. package/src/plugins/ghLoader.worker.js +151 -0
  224. package/src/plugins/pluginManager.js +286 -0
  225. package/src/pmi/PMIViewsManager.js +134 -0
  226. package/src/services/componentLibrary.js +198 -0
  227. package/src/tests/ConsoleCapture.js +189 -0
  228. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  229. package/src/tests/browserTests.js +597 -0
  230. package/src/tests/debugBoolean.js +225 -0
  231. package/src/tests/partFiles/badBoolean.json +957 -0
  232. package/src/tests/partFiles/extrudeTest.json +88 -0
  233. package/src/tests/partFiles/filletFail.json +58 -0
  234. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  235. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  236. package/src/tests/test_boolean_subtract.js +27 -0
  237. package/src/tests/test_chamfer.js +17 -0
  238. package/src/tests/test_extrudeFace.js +24 -0
  239. package/src/tests/test_fillet.js +17 -0
  240. package/src/tests/test_fillet_nonClosed.js +45 -0
  241. package/src/tests/test_filletsMoreDifficult.js +46 -0
  242. package/src/tests/test_history_features_basic.js +149 -0
  243. package/src/tests/test_hole.js +282 -0
  244. package/src/tests/test_mirror.js +16 -0
  245. package/src/tests/test_offsetShellGrouping.js +85 -0
  246. package/src/tests/test_plane.js +4 -0
  247. package/src/tests/test_primitiveCone.js +11 -0
  248. package/src/tests/test_primitiveCube.js +7 -0
  249. package/src/tests/test_primitiveCylinder.js +8 -0
  250. package/src/tests/test_primitivePyramid.js +9 -0
  251. package/src/tests/test_primitiveSphere.js +17 -0
  252. package/src/tests/test_primitiveTorus.js +21 -0
  253. package/src/tests/test_pushFace.js +126 -0
  254. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  255. package/src/tests/test_sheetMetal_features.js +80 -0
  256. package/src/tests/test_sketch_openLoop.js +45 -0
  257. package/src/tests/test_solidMetrics.js +58 -0
  258. package/src/tests/test_stlLoader.js +1889 -0
  259. package/src/tests/test_sweepFace.js +55 -0
  260. package/src/tests/test_tube.js +45 -0
  261. package/src/tests/test_tube_closedLoop.js +67 -0
  262. package/src/tests/tests.js +493 -0
  263. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  264. package/src/tools/dialogCapturePageFactory.js +227 -0
  265. package/src/tools/featureDialogCapturePage.js +47 -0
  266. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  267. package/src/utils/axisHelpers.js +99 -0
  268. package/src/utils/deepClone.js +69 -0
  269. package/src/utils/geometryTolerance.js +37 -0
  270. package/src/utils/normalizeTypeString.js +8 -0
  271. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,72 @@
1
+ export { constructorImpl, clone, free } from "./lifecycle.js";
2
+ export {
3
+ _key,
4
+ _getPointIndex,
5
+ _getOrCreateID,
6
+ addTriangle,
7
+ addAuxEdge,
8
+ addCenterline,
9
+ } from "./authoring.js";
10
+ export {
11
+ setFaceMetadata,
12
+ getFaceMetadata,
13
+ getFaceNames,
14
+ renameFace,
15
+ _combineFaceMetadata,
16
+ setEdgeMetadata,
17
+ getEdgeMetadata,
18
+ _combineEdgeMetadata,
19
+ } from "./metadata.js";
20
+ export {
21
+ bakeTransform,
22
+ bakeTRS,
23
+ offsetFace,
24
+ mirrorAcrossPlane,
25
+ pushFace,
26
+ } from "./transforms.js";
27
+ export {
28
+ _manifoldize,
29
+ setEpsilon,
30
+ _weldVerticesByEpsilon,
31
+ fixTriangleWindingsByAdjacency,
32
+ _isCoherentlyOrientedManifold,
33
+ invertNormals,
34
+ } from "./manifoldOps.js";
35
+ export {
36
+ removeSmallIslands,
37
+ removeSmallInternalIslands,
38
+ removeOppositeSingleEdgeFaces,
39
+ removeTinyBoundaryTriangles,
40
+ collapseTinyTriangles,
41
+ cleanupTinyFaceIslands,
42
+ remesh,
43
+ splitSelfIntersectingTriangles,
44
+ removeDegenerateTriangles,
45
+ removeInternalTriangles,
46
+ removeInternalTrianglesByRaycast,
47
+ removeInternalTrianglesByWinding,
48
+ mergeTinyFaces,
49
+ } from "./meshCleanup.js";
50
+ export {
51
+ getMesh,
52
+ _ensureFaceIndex,
53
+ getFace,
54
+ getFaces,
55
+ getBoundaryEdgePolylines,
56
+ } from "./meshQueries.js";
57
+ export {
58
+ union,
59
+ subtract,
60
+ intersect,
61
+ difference,
62
+ _combineIdMaps,
63
+ _expandTriIDsFromMesh as _expandTriIDsFromMeshStatic,
64
+ _fromManifold as _fromManifoldStatic,
65
+ setTolerance,
66
+ simplify,
67
+ } from "./booleanOps.js";
68
+ export { toSTL, writeSTL, toSTEP, writeSTEP } from "./io.js";
69
+ export { volume, surfaceArea, getTriangleCount } from "./metrics.js";
70
+ export { visualize } from "./visualize.js";
71
+ export { fillet } from "./fillet.js";
72
+ export { chamfer } from "./chamfer.js";
@@ -0,0 +1,105 @@
1
+ import { generateSTEP } from '../../exporters/step.js';
2
+
3
+ /**
4
+ * Export helpers (STL + STEP output).
5
+ */
6
+
7
+ export function toSTL(name = "solid", precision = 6) {
8
+ const mesh = this.getMesh();
9
+ const { vertProperties, triVerts } = mesh;
10
+
11
+ const fmt = (n) => Number.isFinite(n) ? n.toFixed(precision) : "0";
12
+ const parts = [];
13
+ parts.push(`solid ${name}`);
14
+
15
+ const triCount = (triVerts.length / 3) | 0;
16
+ for (let t = 0; t < triCount; t++) {
17
+ const i0 = triVerts[t * 3 + 0];
18
+ const i1 = triVerts[t * 3 + 1];
19
+ const i2 = triVerts[t * 3 + 2];
20
+
21
+ const p0 = [
22
+ vertProperties[i0 * 3 + 0],
23
+ vertProperties[i0 * 3 + 1],
24
+ vertProperties[i0 * 3 + 2],
25
+ ];
26
+ const p1 = [
27
+ vertProperties[i1 * 3 + 0],
28
+ vertProperties[i1 * 3 + 1],
29
+ vertProperties[i1 * 3 + 2],
30
+ ];
31
+ const p2 = [
32
+ vertProperties[i2 * 3 + 0],
33
+ vertProperties[i2 * 3 + 1],
34
+ vertProperties[i2 * 3 + 2],
35
+ ];
36
+
37
+ const ux = p1[0] - p0[0];
38
+ const uy = p1[1] - p0[1];
39
+ const uz = p1[2] - p0[2];
40
+ const vx = p2[0] - p0[0];
41
+ const vy = p2[1] - p0[1];
42
+ const vz = p2[2] - p0[2];
43
+ let nx = uy * vz - uz * vy;
44
+ let ny = uz * vx - ux * vz;
45
+ let nz = ux * vy - uy * vx;
46
+ const nl = Math.hypot(nx, ny, nz) || 1;
47
+ nx /= nl; ny /= nl; nz /= nl;
48
+
49
+ parts.push(` facet normal ${fmt(nx)} ${fmt(ny)} ${fmt(nz)}`);
50
+ parts.push(` outer loop`);
51
+ parts.push(` vertex ${fmt(p0[0])} ${fmt(p0[1])} ${fmt(p0[2])}`);
52
+ parts.push(` vertex ${fmt(p1[0])} ${fmt(p1[1])} ${fmt(p1[2])}`);
53
+ parts.push(` vertex ${fmt(p2[0])} ${fmt(p2[1])} ${fmt(p2[2])}`);
54
+ parts.push(` endloop`);
55
+ parts.push(` endfacet`);
56
+ }
57
+
58
+ parts.push(`endsolid ${name}`);
59
+ try { return parts.join("\n"); } finally { try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch { } }
60
+ }
61
+
62
+ export async function writeSTL(filePath, name = "solid", precision = 6) {
63
+ if (typeof window !== "undefined") {
64
+ throw new Error("writeSTL is only available in Node.js environments");
65
+ }
66
+ const { writeFile } = await import('node:fs/promises');
67
+ const stl = this.toSTL(name, precision);
68
+ await writeFile(filePath, stl, 'utf8');
69
+ return filePath;
70
+ }
71
+
72
+ /**
73
+ * Generate a triangulated STEP (faceted BREP) string for this solid.
74
+ * @param {string} [name=this.name||'part']
75
+ * @param {{unit?: string, precision?: number, scale?: number, applyWorldTransform?: boolean, useTessellatedFaces?: boolean}} [options]
76
+ * @returns {string}
77
+ */
78
+ export function toSTEP(name = undefined, options = {}) {
79
+ const opts = (options && typeof options === 'object') ? options : {};
80
+ const unit = opts.unit || 'millimeter';
81
+ const precision = Number.isFinite(opts.precision) ? opts.precision : 6;
82
+ const scale = Number.isFinite(opts.scale) ? opts.scale : 1;
83
+ const applyWorldTransform = opts.applyWorldTransform !== false;
84
+ const baseName = name || this?.name || 'part';
85
+ const stepOpts = { ...opts, name: baseName, unit, precision, scale, applyWorldTransform };
86
+ const { data } = generateSTEP([this], stepOpts);
87
+ return data;
88
+ }
89
+
90
+ /**
91
+ * Write a triangulated STEP file to disk (Node.js only).
92
+ * @param {string} filePath
93
+ * @param {string} [name=this.name||'part']
94
+ * @param {{unit?: string, precision?: number, scale?: number, applyWorldTransform?: boolean, useTessellatedFaces?: boolean}} [options]
95
+ * @returns {Promise<string>} resolves with file path
96
+ */
97
+ export async function writeSTEP(filePath, name = undefined, options = {}) {
98
+ if (typeof window !== "undefined") {
99
+ throw new Error("writeSTEP is only available in Node.js environments");
100
+ }
101
+ const { writeFile } = await import('node:fs/promises');
102
+ const step = this.toSTEP(name, options);
103
+ await writeFile(filePath, step, 'utf8');
104
+ return filePath;
105
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Solid lifecycle helpers: constructor, cloning, resource cleanup.
3
+ */
4
+ export function constructorImpl() {
5
+ // Geometry data (MeshGL layout, but we build incrementally in JS arrays)
6
+ this._numProp = 3; // x,y,z
7
+ this._vertProperties = []; // flat [x0,y0,z0, x1,y1,z1, ...]
8
+ this._triVerts = []; // flat [i0,i1,i2, i3,i4,i5, ...]
9
+ this._triIDs = []; // per-triangle Manifold ID (mapped from faceName)
10
+
11
+ // Vertex uniquing
12
+ this._vertKeyToIndex = new Map(); // "x,y,z" -> index
13
+
14
+ // Face name <-> Manifold ID
15
+ this._faceNameToID = new Map();
16
+ this._idToFaceName = new Map();
17
+
18
+ // Face and edge metadata storage
19
+ this._faceMetadata = new Map(); // faceName -> metadata object
20
+ this._edgeMetadata = new Map(); // edgeName -> metadata object
21
+
22
+ // Laziness & caching
23
+ this._dirty = true; // arrays changed and manifold needs rebuild
24
+ this._manifold = null; // cached Manifold object built from arrays
25
+ this._faceIndex = null; // lazy cache: id -> [triIndices]
26
+ this._epsilon = 0; // optional vertex weld tolerance (off by default)
27
+ this._freeTimer = null; // handle for scheduled wasm cleanup
28
+
29
+ this.type = 'SOLID';
30
+ this.renderOrder = 1;
31
+ // Custom auxiliary edges (e.g., centerlines) to visualize with this solid
32
+ // Each item: { name?:string, points:[[x,y,z],...], closedLoop?:boolean, polylineWorld?:boolean, materialKey?:'OVERLAY'|'BASE', centerline?:boolean }
33
+ this._auxEdges = [];
34
+ }
35
+
36
+ /**
37
+ * Create a lightweight clone of this Solid that copies geometry arrays
38
+ * and face maps, but not children or any THREE resources.
39
+ */
40
+ export function clone() {
41
+ const Solid = this.constructor;
42
+ const s = new Solid();
43
+ s._numProp = this._numProp;
44
+ s._vertProperties = this._vertProperties.slice();
45
+ s._triVerts = this._triVerts.slice();
46
+ s._triIDs = this._triIDs.slice();
47
+ s._vertKeyToIndex = new Map();
48
+ for (let i = 0; i < s._vertProperties.length; i += 3) {
49
+ const x = s._vertProperties[i];
50
+ const y = s._vertProperties[i + 1];
51
+ const z = s._vertProperties[i + 2];
52
+ s._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
53
+ }
54
+ // Copy face name maps
55
+ try {
56
+ s._idToFaceName = new Map(this._idToFaceName);
57
+ s._faceNameToID = new Map(this._faceNameToID);
58
+ } catch (_) { /* ignore */ }
59
+ try { s._faceMetadata = new Map(this._faceMetadata); } catch (_) { s._faceMetadata = new Map(); }
60
+ try { s._edgeMetadata = new Map(this._edgeMetadata); } catch (_) { s._edgeMetadata = new Map(); }
61
+ // Copy auxiliary edges (deep copy points)
62
+ try {
63
+ s._auxEdges = Array.isArray(this._auxEdges)
64
+ ? this._auxEdges.map(e => ({
65
+ name: e?.name,
66
+ closedLoop: !!e?.closedLoop,
67
+ polylineWorld: !!e?.polylineWorld,
68
+ materialKey: e?.materialKey,
69
+ centerline: !!e?.centerline,
70
+ points: Array.isArray(e?.points) ? e.points.map(p => Array.isArray(p) ? [p[0], p[1], p[2]] : p) : [],
71
+ }))
72
+ : [];
73
+ } catch { s._auxEdges = []; }
74
+ s._dirty = true;
75
+ s._manifold = null;
76
+ s._faceIndex = null;
77
+ s.type = 'SOLID';
78
+ s.renderOrder = this.renderOrder;
79
+ return s;
80
+ }
81
+
82
+ /**
83
+ * Free wasm resources associated with this Solid.
84
+ *
85
+ * Disposes the underlying Manifold instance (if any) to prevent
86
+ * accumulating wasm memory across rebuilds. After calling free(),
87
+ * the Solid remains usable - any subsequent call that needs the
88
+ * manifold will trigger a fresh _manifoldize().
89
+ */
90
+ export function free() {
91
+ try {
92
+ // Clear any pending auto-free timer first
93
+ try { if (this._freeTimer) { clearTimeout(this._freeTimer); } } catch (_) { }
94
+ this._freeTimer = null;
95
+ if (this._manifold) {
96
+ try { if (typeof this._manifold.delete === 'function') this._manifold.delete(); } catch (_) { }
97
+ this._manifold = null;
98
+ }
99
+ this._dirty = true;
100
+ this._faceIndex = null;
101
+ } catch (_) { /* noop */ }
102
+ return this;
103
+ }
@@ -0,0 +1,375 @@
1
+ import { Manifold, ManifoldMesh, debugMode } from "../SolidShared.js";
2
+
3
+ /**
4
+ * Manifold lifecycle helpers: rebuild, welding, orientation fixes.
5
+ */
6
+
7
+ /**
8
+ * Build (or rebuild) the Manifold from our MeshGL arrays.
9
+ * Uses faceID per triangle so face names survive CSG operations.
10
+ */
11
+ export function _manifoldize() {
12
+ // Measure timing for manifoldization (cache hits vs rebuilds)
13
+ const nowMs = () => (typeof performance !== 'undefined' && performance?.now ? performance.now() : Date.now());
14
+ const __t0 = nowMs();
15
+ // Reset the auto-free timer: always schedule cleanup 60s after last use
16
+ try { if (this._freeTimer) { clearTimeout(this._freeTimer); } } catch { }
17
+ try {
18
+ this._freeTimer = setTimeout(() => {
19
+ try { this.free(); } catch { }
20
+ }, 60 * 1000);
21
+ } catch { }
22
+ if (!this._dirty && this._manifold) {
23
+ const __t1 = nowMs();
24
+ try { if (debugMode) console.log(`[Solid] _manifoldize cache-hit in ${Math.round(__t1 - __t0)} ms`); } catch { }
25
+ return this._manifold;
26
+ }
27
+ let __logged = false;
28
+ const __logDone = (ok = true) => {
29
+ if (__logged) return; __logged = true;
30
+ const __t1 = nowMs();
31
+ const triCountDbg = (this?._triVerts?.length || 0) / 3 | 0;
32
+ const vertCountDbg = (this?._vertProperties?.length || 0) / 3 | 0;
33
+ try {
34
+ if (debugMode) console.log(`[Solid] _manifoldize ${ok ? 'built' : 'failed'} in ${Math.round(__t1 - __t0)} ms (tris=${triCountDbg}, verts=${vertCountDbg})`);
35
+ } catch { }
36
+ };
37
+ try {
38
+ // Ensure consistent orientation before building a Manifold
39
+ this.fixTriangleWindingsByAdjacency();
40
+ // Ensure outward orientation (positive signed volume). If negative, flip all tris.
41
+ const signedVolume = (() => {
42
+ const vp = this._vertProperties;
43
+ let vol6 = 0; // 6 * volume
44
+ for (let t = 0; t < this._triVerts.length; t += 3) {
45
+ const i0 = this._triVerts[t], i1 = this._triVerts[t + 1], i2 = this._triVerts[t + 2];
46
+ const x0 = vp[i0 * 3], y0 = vp[i0 * 3 + 1], z0 = vp[i0 * 3 + 2];
47
+ const x1 = vp[i1 * 3], y1 = vp[i1 * 3 + 1], z1 = vp[i1 * 3 + 2];
48
+ const x2 = vp[i2 * 3], y2 = vp[i2 * 3 + 1], z2 = vp[i2 * 3 + 2];
49
+ // triple product p0 · (p1 × p2)
50
+ vol6 += x0 * (y1 * z2 - z1 * y2) - y0 * (x1 * z2 - z1 * x2) + z0 * (x1 * y2 - y1 * x2);
51
+ }
52
+ return vol6 / 6.0;
53
+ })();
54
+ if (signedVolume < 0) {
55
+ for (let t = 0; t < this._triVerts.length; t += 3) {
56
+ // swap indices 1 and 2 to flip triangle
57
+ const tmp = this._triVerts[t + 1];
58
+ this._triVerts[t + 1] = this._triVerts[t + 2];
59
+ this._triVerts[t + 2] = tmp;
60
+ }
61
+ }
62
+
63
+ const triCount = (this._triVerts.length / 3) | 0;
64
+ const triVerts = new Uint32Array(this._triVerts);
65
+ const faceID = new Uint32Array(triCount);
66
+ for (let t = 0; t < triCount; t++) faceID[t] = this._triIDs[t];
67
+
68
+ const mesh = new ManifoldMesh({
69
+ numProp: this._numProp,
70
+ vertProperties: new Float32Array(this._vertProperties),
71
+ triVerts,
72
+ faceID,
73
+ });
74
+
75
+ // Fill mergeFromVert/mergeToVert; positions and indices stay intact.
76
+ mesh.merge();
77
+
78
+ try {
79
+ this._manifold = new Manifold(mesh);
80
+ } catch (err) {
81
+ // If this Solid is a FilletSolid (identified by presence of edgeToFillet),
82
+ // emit a structured JSON log with diagnostic context for debugging.
83
+ try {
84
+ if (this && Object.prototype.hasOwnProperty.call(this, 'edgeToFillet')) {
85
+ const triCountInfo = (this._triVerts?.length || 0) / 3 | 0;
86
+ const vertCountInfo = (this._vertProperties?.length || 0) / 3 | 0;
87
+ const faces = [];
88
+ try {
89
+ if (this.edgeToFillet && Array.isArray(this.edgeToFillet.faces)) {
90
+ for (const f of this.edgeToFillet.faces) if (f && f.name) faces.push(f.name);
91
+ }
92
+ } catch { }
93
+ const failure = {
94
+ type: 'FilletSolidManifoldFailure',
95
+ message: (err && (err.message || String(err))) || 'unknown',
96
+ params: {
97
+ radius: this.radius,
98
+ arcSegments: this.arcSegments,
99
+ sampleCount: this.sampleCount,
100
+ sideMode: this.sideMode,
101
+ inflate: this.inflate,
102
+ sideStripSubdiv: this.sideStripSubdiv,
103
+ seamInsetScale: this.seamInsetScale,
104
+ projectStripsOpenEdges: this.projectStripsOpenEdges,
105
+ forceSeamInset: this.forceSeamInset,
106
+ },
107
+ edge: {
108
+ name: this.edgeToFillet?.name || null,
109
+ closedLoop: !!(this.edgeToFillet?.closedLoop || this.edgeToFillet?.userData?.closedLoop),
110
+ faces,
111
+ },
112
+ counts: {
113
+ vertices: vertCountInfo,
114
+ triangles: triCountInfo,
115
+ faceLabels: (this._faceNameToID && typeof this._faceNameToID.size === 'number') ? this._faceNameToID.size : undefined,
116
+ },
117
+ };
118
+ try { console.error(JSON.stringify(failure)); } catch { console.error('[FilletSolidManifoldFailure]', failure.message); }
119
+ }
120
+ } catch { }
121
+ __logDone(false);
122
+ throw err;
123
+ }
124
+ finally {
125
+ try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch { }
126
+ }
127
+ this._dirty = false;
128
+ this._faceIndex = null; // will rebuild on demand
129
+ __logDone(true);
130
+ return this._manifold;
131
+ } finally {
132
+ // In case of unexpected control flow, ensure we log once with best-effort status.
133
+ const ok = !!(this && this._manifold) && this._dirty === false;
134
+ __logDone(ok);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Set vertex weld epsilon and optionally weld existing vertices and
140
+ * remove degenerate triangles. Epsilon <= 0 disables welding.
141
+ */
142
+ export function setEpsilon(epsilon = 0) {
143
+ this._epsilon = Number(epsilon) || 0;
144
+ if (this._epsilon > 0) {
145
+ this._weldVerticesByEpsilon(this._epsilon);
146
+ }
147
+ // After adjusting vertices, attempt to correct triangle winding.
148
+ this.fixTriangleWindingsByAdjacency();
149
+ return this;
150
+ }
151
+
152
+ export function _weldVerticesByEpsilon(eps) {
153
+ const vp = this._vertProperties;
154
+ const nv = (vp.length / 3) | 0;
155
+ if (nv === 0) return;
156
+
157
+ const toCell = (x) => Math.round(x / eps);
158
+ const cellMap = new Map(); // cellKey -> representative vert index
159
+ const repOf = new Uint32Array(nv);
160
+ for (let i = 0; i < nv; i++) repOf[i] = i;
161
+
162
+ // Find representative for each vertex by grid hashing
163
+ for (let i = 0; i < nv; i++) {
164
+ const x = vp[i * 3 + 0];
165
+ const y = vp[i * 3 + 1];
166
+ const z = vp[i * 3 + 2];
167
+ const cx = toCell(x), cy = toCell(y), cz = toCell(z);
168
+ const key = `${cx},${cy},${cz}`;
169
+ const rep = cellMap.get(key);
170
+ if (rep === undefined) {
171
+ cellMap.set(key, i);
172
+ repOf[i] = i;
173
+ } else {
174
+ repOf[i] = rep;
175
+ }
176
+ }
177
+
178
+ // Remap triangles to representative indices and drop degenerate/zero-area
179
+ const newTriVerts = [];
180
+ const newTriIDs = [];
181
+ const used = new Uint8Array(nv); // mark used reps
182
+ const area2Thresh = 0; // strict degenerate check
183
+ for (let t = 0; t < this._triVerts.length; t += 3) {
184
+ const a = repOf[this._triVerts[t + 0]];
185
+ const b = repOf[this._triVerts[t + 1]];
186
+ const c = repOf[this._triVerts[t + 2]];
187
+ if (a === b || b === c || c === a) continue; // collapsed
188
+ // Compute area^2 to filter near-degenerates
189
+ const ax = vp[a * 3], ay = vp[a * 3 + 1], az = vp[a * 3 + 2];
190
+ const bx = vp[b * 3], by = vp[b * 3 + 1], bz = vp[b * 3 + 2];
191
+ const cx = vp[c * 3], cy = vp[c * 3 + 1], cz = vp[c * 3 + 2];
192
+ const ux = bx - ax, uy = by - ay, uz = bz - az;
193
+ const vx = cx - ax, vy = cy - ay, vz = cz - az;
194
+ const nx = uy * vz - uz * vy;
195
+ const ny = uz * vx - ux * vz;
196
+ const nz = ux * vy - uy * vx;
197
+ const area2 = nx * nx + ny * ny + nz * nz;
198
+ if (area2 <= area2Thresh) continue;
199
+ const triIdx = (t / 3) | 0;
200
+ newTriVerts.push(a, b, c);
201
+ newTriIDs.push(this._triIDs[triIdx]);
202
+ used[a] = 1; used[b] = 1; used[c] = 1;
203
+ }
204
+
205
+ // If nothing changed, bail
206
+ if (newTriVerts.length === this._triVerts.length && newTriIDs.length === this._triIDs.length) return;
207
+
208
+ // Build compacted vertex buffer and remap indices
209
+ const oldToNew = new Int32Array(nv);
210
+ for (let i = 0; i < nv; i++) oldToNew[i] = -1;
211
+ const newVerts = [];
212
+ let write = 0;
213
+ for (let i = 0; i < nv; i++) {
214
+ if (!used[i]) continue;
215
+ oldToNew[i] = write++;
216
+ newVerts.push(vp[i * 3 + 0], vp[i * 3 + 1], vp[i * 3 + 2]);
217
+ }
218
+ for (let k = 0; k < newTriVerts.length; k++) {
219
+ newTriVerts[k] = oldToNew[newTriVerts[k]];
220
+ }
221
+
222
+ // Commit
223
+ this._vertProperties = newVerts;
224
+ this._triVerts = newTriVerts;
225
+ this._triIDs = newTriIDs;
226
+ this._vertKeyToIndex = new Map();
227
+ for (let i = 0; i < this._vertProperties.length; i += 3) {
228
+ const x = this._vertProperties[i], y = this._vertProperties[i + 1], z = this._vertProperties[i + 2];
229
+ this._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
230
+ }
231
+ this._dirty = true;
232
+ this._faceIndex = null;
233
+ this._manifoldize();
234
+ return this;
235
+ }
236
+
237
+ /**
238
+ * Ensures all triangles have consistent winding by making sure
239
+ * shared edges are oriented oppositely between adjacent triangles.
240
+ */
241
+ export function fixTriangleWindingsByAdjacency() {
242
+ if (this._isCoherentlyOrientedManifold()) return;
243
+ const triCount = (this._triVerts.length / 3) | 0;
244
+ if (triCount === 0) return;
245
+
246
+ const tris = new Array(triCount);
247
+ for (let t = 0; t < triCount; t++) {
248
+ const base = t * 3;
249
+ tris[t] = [
250
+ this._triVerts[base + 0],
251
+ this._triVerts[base + 1],
252
+ this._triVerts[base + 2],
253
+ ];
254
+ }
255
+
256
+ const undirected = new Map();
257
+ const numVerts = (this._vertProperties.length / 3) | 0;
258
+ const NV = BigInt(numVerts);
259
+ const ukey = (a, b) => {
260
+ const A = BigInt(a);
261
+ const B = BigInt(b);
262
+ return A < B ? A * NV + B : B * NV + A;
263
+ };
264
+ for (let ti = 0; ti < tris.length; ti++) {
265
+ const tri = tris[ti];
266
+ for (let e = 0; e < 3; e++) {
267
+ const a = tri[e];
268
+ const b = tri[(e + 1) % 3];
269
+ const k = ukey(a, b);
270
+ let arr = undirected.get(k);
271
+ if (!arr) {
272
+ arr = [];
273
+ undirected.set(k, arr);
274
+ }
275
+ arr.push({ tri: ti, a, b }); // oriented edge as appears in the triangle
276
+ }
277
+ }
278
+
279
+ const visited = new Array(triCount).fill(false);
280
+ const stack = [];
281
+
282
+ for (let seed = 0; seed < triCount; seed++) {
283
+ if (visited[seed]) continue;
284
+ visited[seed] = true;
285
+ stack.push(seed);
286
+
287
+ while (stack.length) {
288
+ const t = stack.pop();
289
+ const tri = tris[t];
290
+ for (let e = 0; e < 3; e++) {
291
+ const a = tri[e];
292
+ const b = tri[(e + 1) % 3];
293
+ const k = ukey(a, b);
294
+ const adj = undirected.get(k);
295
+ if (!adj || adj.length < 2) continue; // boundary or non-manifold; skip
296
+
297
+ for (const entry of adj) {
298
+ const n = entry.tri;
299
+ if (n === t || visited[n]) continue;
300
+
301
+ const nTri = tris[n];
302
+ if (entry.a === a && entry.b === b) {
303
+ [nTri[1], nTri[2]] = [nTri[2], nTri[1]];
304
+ }
305
+
306
+ visited[n] = true;
307
+ stack.push(n);
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ this._triVerts.length = 0;
314
+ for (const tri of tris) {
315
+ this._triVerts.push(tri[0], tri[1], tri[2]);
316
+ }
317
+
318
+ this._dirty = true;
319
+ this._faceIndex = null;
320
+ return this;
321
+ }
322
+
323
+ // Return true if every undirected edge is shared by exactly 2 triangles
324
+ // and their directed usages are opposite.
325
+ export function _isCoherentlyOrientedManifold() {
326
+ const triCount = (this._triVerts.length / 3) | 0;
327
+ if (triCount === 0) return false;
328
+ const numVerts = (this._vertProperties.length / 3) | 0;
329
+ const NV = BigInt(numVerts);
330
+ const ukey = (a, b) => {
331
+ const A = BigInt(a);
332
+ const B = BigInt(b);
333
+ return A < B ? A * NV + B : B * NV + A;
334
+ };
335
+ const edgeMap = new Map();
336
+ for (let t = 0; t < triCount; t++) {
337
+ const b = t * 3;
338
+ const i0 = this._triVerts[b + 0];
339
+ const i1 = this._triVerts[b + 1];
340
+ const i2 = this._triVerts[b + 2];
341
+ const e = [
342
+ [i0, i1],
343
+ [i1, i2],
344
+ [i2, i0],
345
+ ];
346
+ for (let k = 0; k < 3; k++) {
347
+ const a = e[k][0];
348
+ const b2 = e[k][1];
349
+ const key = ukey(a, b2);
350
+ let arr = edgeMap.get(key);
351
+ if (!arr) { arr = []; edgeMap.set(key, arr); }
352
+ arr.push({ a, b: b2 });
353
+ }
354
+ }
355
+ for (const arr of edgeMap.values()) {
356
+ if (arr.length !== 2) return false; // boundary or non-manifold
357
+ const e0 = arr[0], e1 = arr[1];
358
+ if (!(e0.a === e1.b && e0.b === e1.a)) return false; // not opposite orientation
359
+ }
360
+ return true;
361
+ }
362
+
363
+ export function invertNormals() {
364
+ for (let t = 0; t < this._triVerts.length; t += 3) {
365
+ const tmp = this._triVerts[t + 1];
366
+ this._triVerts[t + 1] = this._triVerts[t + 2];
367
+ this._triVerts[t + 2] = tmp;
368
+ }
369
+
370
+ this._dirty = true;
371
+ this._faceIndex = null;
372
+ this._manifoldize();
373
+ return this;
374
+ }
375
+