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,169 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+ import { resolveProfileFace } from "./profileUtils.js";
3
+ import { buildContourFlangeSolidFromParams } from "./SheetMetalContourFlangeFeature.js";
4
+ import { buildSheetMetalCutoutSolids } from "./SheetMetalCutoutFeature.js";
5
+ import { buildSheetMetalFlangeSolids } from "./SheetMetalFlangeFeature.js";
6
+ import { SHEET_METAL_NODE_TYPES } from "./sheetMetalTree.js";
7
+ import { resolvePlacementMode, toExtrudeDistances, tagTabFaceTypes } from "./sheetMetalTabUtils.js";
8
+ import { buildFaceFromProfileGroups } from "./sheetMetalProfileUtils.js";
9
+
10
+ export async function buildSheetMetalSolidFromTree(tree, context = {}) {
11
+ if (!tree || !Array.isArray(tree.nodes) || tree.nodes.length === 0) {
12
+ throw new Error("Sheet metal tree has no nodes to build.");
13
+ }
14
+
15
+ let current = null;
16
+ for (const node of tree.nodes) {
17
+ if (!node || !node.type) continue;
18
+ switch (node.type) {
19
+ case SHEET_METAL_NODE_TYPES.TAB:
20
+ current = buildTabSolid(node, context);
21
+ break;
22
+ case SHEET_METAL_NODE_TYPES.CONTOUR_FLANGE:
23
+ current = buildContourFlangeSolid(node, context);
24
+ break;
25
+ case SHEET_METAL_NODE_TYPES.FLANGE:
26
+ current = await applyFlangeNode(current, node, context);
27
+ break;
28
+ case SHEET_METAL_NODE_TYPES.CUTOUT:
29
+ current = await applyCutoutNode(current, node, context);
30
+ break;
31
+ default:
32
+ throw new Error(`Unsupported sheet metal node type: ${node.type}`);
33
+ }
34
+ }
35
+ if (!current) {
36
+ throw new Error("Sheet metal tree did not produce a solid.");
37
+ }
38
+ return current;
39
+ }
40
+
41
+ function buildTabSolid(node, context) {
42
+ const params = node.params || {};
43
+ let faceObj = resolveProfileFace(params.profileRef, context.partHistory);
44
+ if (!faceObj && params.profileGroups) {
45
+ faceObj = buildFaceFromProfileGroups(
46
+ params.profileGroups,
47
+ params.profileName || params.profileRef || "PROFILE",
48
+ params.profileEdges,
49
+ );
50
+ }
51
+ if (!faceObj) {
52
+ throw new Error("Sheet Metal Tab requires a valid FACE or SKETCH selection.");
53
+ }
54
+
55
+ const placement = resolvePlacementMode(params.placementMode, params.signedThickness);
56
+ const extrudeDistances = toExtrudeDistances(Math.abs(params.thickness), placement);
57
+
58
+ const sweep = new BREP.Sweep({
59
+ face: faceObj,
60
+ distance: extrudeDistances.distance,
61
+ distanceBack: extrudeDistances.distanceBack,
62
+ mode: "translate",
63
+ name: params.featureID || null,
64
+ omitBaseCap: false,
65
+ });
66
+ sweep.visualize();
67
+ tagTabFaceTypes(sweep);
68
+ return sweep;
69
+ }
70
+
71
+ function buildContourFlangeSolid(node, context) {
72
+ const params = node.params || {};
73
+ const buildParams = {
74
+ ...params,
75
+ path: params.pathRefs || params.path || params.profile,
76
+ };
77
+ const result = buildContourFlangeSolidFromParams(buildParams, context.partHistory);
78
+ return result.solid;
79
+ }
80
+
81
+ async function applyFlangeNode(current, node, context) {
82
+ if (!current) {
83
+ throw new Error("Sheet metal flange requires an existing solid.");
84
+ }
85
+ const params = node.params || {};
86
+ const faces = resolveFaceRefsOnSolid(current, params.faceRefs);
87
+ if (!faces.length) {
88
+ throw new Error("Sheet metal flange could not resolve any target faces on the current solid.");
89
+ }
90
+ const featureDefaults = {
91
+ longName: "Sheet Metal Flange",
92
+ logTag: "SheetMetalFlange",
93
+ baseType: params.baseType || "FLANGE",
94
+ defaultAngle: Number.isFinite(params.defaultAngle) ? params.defaultAngle : 90,
95
+ angleOverride: Number.isFinite(params.angleOverride) ? params.angleOverride : null,
96
+ defaultBendRadius: Number.isFinite(params.defaultBendRadius) ? params.defaultBendRadius : null,
97
+ };
98
+ const result = await buildSheetMetalFlangeSolids({
99
+ params,
100
+ partHistory: context.partHistory,
101
+ faces,
102
+ featureClass: featureDefaults,
103
+ applyMetadata: false,
104
+ });
105
+ return pickPrimarySolid(result?.added, current);
106
+ }
107
+
108
+ async function applyCutoutNode(current, node, context) {
109
+ if (!current) {
110
+ throw new Error("Sheet metal cutout requires an existing solid.");
111
+ }
112
+ ensureSolidVisualized(current);
113
+ const params = node.params || {};
114
+ const buildParams = {
115
+ ...params,
116
+ sheet: params.sheetRef || params.sheet,
117
+ profile: params.profileRef || params.profile,
118
+ };
119
+ const result = await buildSheetMetalCutoutSolids({
120
+ params: buildParams,
121
+ partHistory: context.partHistory,
122
+ sheetSolid: current,
123
+ applyMetadata: false,
124
+ });
125
+ return pickPrimarySolid(result?.added, current);
126
+ }
127
+
128
+ function ensureSolidVisualized(solid) {
129
+ try {
130
+ if (solid && typeof solid.visualize === "function") solid.visualize();
131
+ } catch { /* ignore */ }
132
+ }
133
+
134
+ function resolveFaceRefsOnSolid(solid, refs) {
135
+ const list = Array.isArray(refs) ? refs : (refs ? [refs] : []);
136
+ if (!solid || !list.length) return [];
137
+ ensureSolidVisualized(solid);
138
+ const out = [];
139
+ for (const ref of list) {
140
+ if (!ref) continue;
141
+ if (ref?.type === "FACE") {
142
+ out.push(ref);
143
+ continue;
144
+ }
145
+ const name = typeof ref === "string" ? ref : (ref?.name || ref?.id || null);
146
+ if (!name) continue;
147
+ let face = null;
148
+ if (typeof solid.getObjectByName === "function") {
149
+ face = solid.getObjectByName(name);
150
+ }
151
+ if (!face || face.type !== "FACE") {
152
+ const children = Array.isArray(solid.children) ? solid.children : [];
153
+ face = children.find((child) => child?.type === "FACE" && child?.name === name) || null;
154
+ }
155
+ if (face && face.type === "FACE") out.push(face);
156
+ }
157
+ return out;
158
+ }
159
+
160
+ function pickPrimarySolid(added, fallback) {
161
+ if (Array.isArray(added) && added.length) {
162
+ if (fallback?.name) {
163
+ const match = added.find((solid) => solid?.name === fallback.name);
164
+ if (match) return match;
165
+ }
166
+ return added[0];
167
+ }
168
+ return fallback;
169
+ }
@@ -0,0 +1,216 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+
3
+ export function cloneProfileGroups(faceObj) {
4
+ const groups = Array.isArray(faceObj?.userData?.profileGroups)
5
+ ? faceObj.userData.profileGroups
6
+ : null;
7
+ if (!groups || groups.length === 0) return null;
8
+
9
+ const clonePoint = (pt, dims) => {
10
+ if (!Array.isArray(pt)) return null;
11
+ const out = [];
12
+ for (let i = 0; i < dims; i++) {
13
+ const val = Number(pt[i]);
14
+ if (!Number.isFinite(val)) return null;
15
+ out.push(val);
16
+ }
17
+ return out;
18
+ };
19
+
20
+ const clonePointList = (list, dims) => {
21
+ if (!Array.isArray(list)) return [];
22
+ const out = [];
23
+ for (const pt of list) {
24
+ const next = clonePoint(pt, dims);
25
+ if (next) out.push(next);
26
+ }
27
+ return out;
28
+ };
29
+
30
+ const cloneNestedList = (list, dims) => {
31
+ if (!Array.isArray(list)) return [];
32
+ const out = [];
33
+ for (const entry of list) {
34
+ const next = clonePointList(entry, dims);
35
+ if (next.length) out.push(next);
36
+ }
37
+ return out;
38
+ };
39
+
40
+ const out = [];
41
+ for (const group of groups) {
42
+ const contour2D = clonePointList(group?.contour2D, 2);
43
+ const holes2D = cloneNestedList(group?.holes2D, 2);
44
+ const contourW = clonePointList(group?.contourW, 3);
45
+ const holesW = cloneNestedList(group?.holesW, 3);
46
+ if (contour2D.length < 3 || contourW.length !== contour2D.length) continue;
47
+ out.push({ contour2D, holes2D, contourW, holesW });
48
+ }
49
+
50
+ return out.length ? out : null;
51
+ }
52
+
53
+ export function collectProfileEdges(faceObj) {
54
+ const edges = Array.isArray(faceObj?.edges) ? faceObj.edges : null;
55
+ if (!edges || edges.length === 0) return null;
56
+ const out = [];
57
+ const THREE = BREP.THREE;
58
+ for (const edge of edges) {
59
+ const name = typeof edge?.name === "string" ? edge.name : null;
60
+ const polyline = Array.isArray(edge?.userData?.polylineLocal)
61
+ ? edge.userData.polylineLocal
62
+ : null;
63
+ if (!name || !polyline || polyline.length < 2) continue;
64
+ const pts = [];
65
+ const isWorld = edge?.userData?.polylineWorld === true;
66
+ for (const pt of polyline) {
67
+ if (!Array.isArray(pt) || pt.length < 3) continue;
68
+ let x = Number(pt[0]);
69
+ let y = Number(pt[1]);
70
+ let z = Number(pt[2]);
71
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) continue;
72
+ if (!isWorld && edge?.matrixWorld) {
73
+ const v = new THREE.Vector3(x, y, z).applyMatrix4(edge.matrixWorld);
74
+ x = v.x;
75
+ y = v.y;
76
+ z = v.z;
77
+ }
78
+ pts.push([x, y, z]);
79
+ }
80
+ if (pts.length < 2) continue;
81
+ out.push({
82
+ name,
83
+ polyline: pts,
84
+ polylineWorld: true,
85
+ });
86
+ }
87
+ return out.length ? out : null;
88
+ }
89
+
90
+ export function buildFaceFromProfileGroups(profileGroups, profileName, profileEdges) {
91
+ const groups = normalizeProfileGroups(profileGroups);
92
+ if (!groups.length) return null;
93
+ const THREE = BREP.THREE;
94
+ const triPositions = [];
95
+ const boundaryLoopsWorld = [];
96
+ const sanitizedGroups = [];
97
+
98
+ for (const group of groups) {
99
+ const contour2D = group.contour2D;
100
+ const holes2D = group.holes2D;
101
+ const contourW = group.contourW;
102
+ const holesW = group.holesW;
103
+ if (contour2D.length < 3 || contourW.length !== contour2D.length) continue;
104
+
105
+ const contourV2 = contour2D.map((p) => new THREE.Vector2(p[0], p[1]));
106
+ const holesV2 = holes2D.map((hole) => hole.map((p) => new THREE.Vector2(p[0], p[1])));
107
+ const tris = THREE.ShapeUtils.triangulateShape(contourV2, holesV2);
108
+ const allW = contourW.concat(...holesW);
109
+ for (const tri of tris) {
110
+ const a = allW[tri[0]];
111
+ const b = allW[tri[1]];
112
+ const c = allW[tri[2]];
113
+ if (!a || !b || !c) continue;
114
+ triPositions.push(a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]);
115
+ }
116
+
117
+ boundaryLoopsWorld.push({ pts: contourW.map((pt) => pt.slice()), isHole: false });
118
+ for (const hole of holesW) {
119
+ boundaryLoopsWorld.push({ pts: hole.map((pt) => pt.slice()), isHole: true });
120
+ }
121
+ sanitizedGroups.push({
122
+ contour2D: contour2D.map((pt) => pt.slice()),
123
+ holes2D: holes2D.map((hole) => hole.map((pt) => pt.slice())),
124
+ contourW: contourW.map((pt) => pt.slice()),
125
+ holesW: holesW.map((hole) => hole.map((pt) => pt.slice())),
126
+ });
127
+ }
128
+
129
+ if (!triPositions.length) return null;
130
+
131
+ const geom = new THREE.BufferGeometry();
132
+ geom.setAttribute("position", new THREE.Float32BufferAttribute(triPositions, 3));
133
+ geom.computeVertexNormals();
134
+ geom.computeBoundingSphere();
135
+
136
+ const face = new BREP.Face(geom);
137
+ face.name = (typeof profileName === "string" && profileName.trim()) ? profileName.trim() : "PROFILE";
138
+ face.userData = face.userData || {};
139
+ face.userData.faceName = face.name;
140
+ face.userData.boundaryLoopsWorld = boundaryLoopsWorld;
141
+ face.userData.profileGroups = sanitizedGroups;
142
+ const edges = buildProfileEdges(profileEdges);
143
+ if (edges.length) face.edges = edges;
144
+ return face;
145
+ }
146
+
147
+ function normalizeProfileGroups(raw) {
148
+ if (!Array.isArray(raw)) return [];
149
+ const out = [];
150
+ const toPoint = (pt, dims) => {
151
+ if (!Array.isArray(pt) || pt.length < dims) return null;
152
+ const vals = [];
153
+ for (let i = 0; i < dims; i++) {
154
+ const n = Number(pt[i]);
155
+ if (!Number.isFinite(n)) return null;
156
+ vals.push(n);
157
+ }
158
+ return vals;
159
+ };
160
+ const toPointList = (list, dims) => {
161
+ if (!Array.isArray(list)) return [];
162
+ const arr = [];
163
+ for (const pt of list) {
164
+ const next = toPoint(pt, dims);
165
+ if (next) arr.push(next);
166
+ }
167
+ return arr;
168
+ };
169
+ const toNestedList = (list, dims) => {
170
+ if (!Array.isArray(list)) return [];
171
+ const arr = [];
172
+ for (const entry of list) {
173
+ const next = toPointList(entry, dims);
174
+ if (next.length) arr.push(next);
175
+ }
176
+ return arr;
177
+ };
178
+ for (const group of raw) {
179
+ if (!group) continue;
180
+ const contour2D = toPointList(group.contour2D, 2);
181
+ const holes2D = toNestedList(group.holes2D, 2);
182
+ const contourW = toPointList(group.contourW, 3);
183
+ const holesW = toNestedList(group.holesW, 3);
184
+ if (contour2D.length < 3 || contourW.length !== contour2D.length) continue;
185
+ out.push({ contour2D, holes2D, contourW, holesW });
186
+ }
187
+ return out;
188
+ }
189
+
190
+ function buildProfileEdges(edgeDefs) {
191
+ if (!Array.isArray(edgeDefs)) return [];
192
+ const edges = [];
193
+ for (const def of edgeDefs) {
194
+ const name = typeof def?.name === "string" ? def.name : null;
195
+ const polyline = Array.isArray(def?.polyline) ? def.polyline : null;
196
+ if (!name || !polyline || polyline.length < 2) continue;
197
+ const pts = [];
198
+ for (const pt of polyline) {
199
+ if (!Array.isArray(pt) || pt.length < 3) continue;
200
+ const x = Number(pt[0]);
201
+ const y = Number(pt[1]);
202
+ const z = Number(pt[2]);
203
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) continue;
204
+ pts.push([x, y, z]);
205
+ }
206
+ if (pts.length < 2) continue;
207
+ edges.push({
208
+ name,
209
+ userData: {
210
+ polylineLocal: pts,
211
+ polylineWorld: true,
212
+ },
213
+ });
214
+ }
215
+ return edges;
216
+ }
@@ -0,0 +1,29 @@
1
+ import { setSheetMetalFaceTypeMetadata, SHEET_METAL_FACE_TYPES } from "./sheetMetalFaceTypes.js";
2
+
3
+ export function resolvePlacementMode(requested, signedThickness) {
4
+ const normalized = String(requested || "").toLowerCase();
5
+ if (normalized === "forward" || normalized === "reverse" || normalized === "midplane") {
6
+ return normalized;
7
+ }
8
+ return signedThickness < 0 ? "reverse" : "forward";
9
+ }
10
+
11
+ export function toExtrudeDistances(thickness, placementMode) {
12
+ if (placementMode === "reverse") return { distance: 0, distanceBack: thickness };
13
+ if (placementMode === "midplane") {
14
+ const half = thickness / 2;
15
+ return { distance: half, distanceBack: half };
16
+ }
17
+ return { distance: thickness, distanceBack: 0 };
18
+ }
19
+
20
+ export function tagTabFaceTypes(sweep) {
21
+ if (!sweep || typeof sweep.getFaceNames !== "function") return;
22
+ const faceNames = sweep.getFaceNames();
23
+ const startFaces = faceNames.filter((name) => name.endsWith("_START"));
24
+ const endFaces = faceNames.filter((name) => name.endsWith("_END"));
25
+ const thicknessFaces = faceNames.filter((name) => name.endsWith("_SW"));
26
+ setSheetMetalFaceTypeMetadata(sweep, startFaces, SHEET_METAL_FACE_TYPES.A);
27
+ setSheetMetalFaceTypeMetadata(sweep, endFaces, SHEET_METAL_FACE_TYPES.B);
28
+ setSheetMetalFaceTypeMetadata(sweep, thicknessFaces, SHEET_METAL_FACE_TYPES.THICKNESS);
29
+ }
@@ -0,0 +1,210 @@
1
+ import { normalizeSelectionList, normalizeSelectionName } from "../selectionUtils.js";
2
+
3
+ export const SHEET_METAL_NODE_TYPES = {
4
+ TAB: "TAB",
5
+ CONTOUR_FLANGE: "CONTOUR_FLANGE",
6
+ FLANGE: "FLANGE",
7
+ CUTOUT: "CUTOUT",
8
+ };
9
+
10
+ export function createSheetMetalTree(nodes = []) {
11
+ return {
12
+ version: 1,
13
+ nodes: Array.isArray(nodes) ? nodes.slice() : [],
14
+ };
15
+ }
16
+
17
+ export function cloneSheetMetalTree(tree) {
18
+ if (!tree || typeof tree !== "object") return createSheetMetalTree();
19
+ const cloneValue = (value) => {
20
+ if (Array.isArray(value)) return value.map(cloneValue);
21
+ if (value && typeof value === "object") {
22
+ const out = {};
23
+ for (const [key, val] of Object.entries(value)) {
24
+ out[key] = cloneValue(val);
25
+ }
26
+ return out;
27
+ }
28
+ return value;
29
+ };
30
+ const nodes = Array.isArray(tree.nodes)
31
+ ? tree.nodes.map((node) => ({
32
+ ...node,
33
+ params: node?.params ? cloneValue(node.params) : null,
34
+ }))
35
+ : [];
36
+ return {
37
+ version: tree.version ?? 1,
38
+ nodes,
39
+ };
40
+ }
41
+
42
+ export function appendSheetMetalNode(tree, node) {
43
+ const next = cloneSheetMetalTree(tree);
44
+ next.nodes.push(node);
45
+ return next;
46
+ }
47
+
48
+ export function createSheetMetalTabNode({
49
+ featureID,
50
+ profileRef,
51
+ profileName,
52
+ profileGroups,
53
+ profileEdges,
54
+ thickness,
55
+ placementMode,
56
+ bendRadius,
57
+ neutralFactor,
58
+ signedThickness,
59
+ consumeProfileSketch,
60
+ }) {
61
+ const resolvedProfile = normalizeSelectionName(
62
+ Array.isArray(profileRef) ? (profileRef[0] || null) : profileRef
63
+ );
64
+ const baseName = profileName || "TAB";
65
+ const tag = featureID ? `${featureID}` : `SM.TAB:${baseName}`;
66
+ return {
67
+ type: SHEET_METAL_NODE_TYPES.TAB,
68
+ id: tag,
69
+ params: {
70
+ featureID: featureID || null,
71
+ profileRef: resolvedProfile || null,
72
+ profileName: baseName,
73
+ profileGroups: Array.isArray(profileGroups) ? profileGroups : null,
74
+ profileEdges: Array.isArray(profileEdges) ? profileEdges : null,
75
+ thickness,
76
+ placementMode,
77
+ bendRadius,
78
+ neutralFactor,
79
+ signedThickness,
80
+ consumeProfileSketch,
81
+ },
82
+ };
83
+ }
84
+
85
+ export function createSheetMetalContourFlangeNode({
86
+ featureID,
87
+ pathRefs,
88
+ distance,
89
+ thickness,
90
+ signedThickness,
91
+ signedDistance,
92
+ reverseSheetSide,
93
+ sheetSide,
94
+ bendRadius,
95
+ neutralFactor,
96
+ consumePathSketch,
97
+ }) {
98
+ const refs = normalizeSelectionList(
99
+ Array.isArray(pathRefs) ? pathRefs : (pathRefs ? [pathRefs] : [])
100
+ );
101
+ const tag = featureID ? `${featureID}` : "SM.CF";
102
+ return {
103
+ type: SHEET_METAL_NODE_TYPES.CONTOUR_FLANGE,
104
+ id: tag,
105
+ params: {
106
+ featureID: featureID || null,
107
+ pathRefs: refs,
108
+ distance,
109
+ thickness,
110
+ signedThickness,
111
+ signedDistance,
112
+ reverseSheetSide,
113
+ sheetSide,
114
+ bendRadius,
115
+ neutralFactor,
116
+ consumePathSketch,
117
+ },
118
+ };
119
+ }
120
+
121
+ export function createSheetMetalFlangeNode({
122
+ featureID,
123
+ faceRefs,
124
+ useOppositeCenterline,
125
+ flangeLength,
126
+ flangeLengthReference,
127
+ angle,
128
+ inset,
129
+ reliefWidth,
130
+ bendRadius,
131
+ offset,
132
+ debugSkipUnion,
133
+ baseType,
134
+ defaultAngle,
135
+ angleOverride,
136
+ defaultBendRadius,
137
+ }) {
138
+ const refs = normalizeSelectionList(
139
+ Array.isArray(faceRefs) ? faceRefs : (faceRefs ? [faceRefs] : [])
140
+ );
141
+ const tag = featureID ? `${featureID}` : "SM.F";
142
+ return {
143
+ type: SHEET_METAL_NODE_TYPES.FLANGE,
144
+ id: tag,
145
+ params: {
146
+ featureID: featureID || null,
147
+ faceRefs: refs,
148
+ useOppositeCenterline,
149
+ flangeLength,
150
+ flangeLengthReference,
151
+ angle,
152
+ inset,
153
+ reliefWidth,
154
+ bendRadius,
155
+ offset,
156
+ debugSkipUnion,
157
+ baseType: baseType || null,
158
+ defaultAngle,
159
+ angleOverride,
160
+ defaultBendRadius,
161
+ },
162
+ };
163
+ }
164
+
165
+ export function createSheetMetalCutoutNode({
166
+ featureID,
167
+ sheetRef,
168
+ profileRef,
169
+ profileFaceName,
170
+ profileGroups,
171
+ profileEdges,
172
+ consumeProfileSketch,
173
+ forwardDistance,
174
+ backDistance,
175
+ keepTool,
176
+ debugCutter,
177
+ }) {
178
+ const sheetName = normalizeSelectionName(
179
+ Array.isArray(sheetRef) ? (sheetRef[0] || null) : sheetRef
180
+ );
181
+ const profileRefName = normalizeSelectionName(
182
+ Array.isArray(profileRef) ? (profileRef[0] || null) : profileRef
183
+ );
184
+ const tag = featureID ? `${featureID}` : "SM.CUTOUT";
185
+ return {
186
+ type: SHEET_METAL_NODE_TYPES.CUTOUT,
187
+ id: tag,
188
+ params: {
189
+ featureID: featureID || null,
190
+ sheetRef: sheetName || null,
191
+ profileRef: profileRefName || null,
192
+ profileName: profileFaceName || profileRefName || null,
193
+ profileGroups: Array.isArray(profileGroups) ? profileGroups : null,
194
+ profileEdges: Array.isArray(profileEdges) ? profileEdges : null,
195
+ consumeProfileSketch,
196
+ forwardDistance,
197
+ backDistance,
198
+ keepTool,
199
+ debugCutter,
200
+ },
201
+ };
202
+ }
203
+
204
+ export function getSheetMetalBaseNode(tree) {
205
+ if (!tree || !Array.isArray(tree.nodes)) return null;
206
+ return tree.nodes.find((node) => (
207
+ node?.type === SHEET_METAL_NODE_TYPES.TAB
208
+ || node?.type === SHEET_METAL_NODE_TYPES.CONTOUR_FLANGE
209
+ )) || null;
210
+ }