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,218 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+ const THREE = BREP.THREE;
3
+
4
+ export const DEFAULT_RESOLUTION = 24;
5
+
6
+ const ensurePoint = (point, fallbackId, fallbackPosition, index = 0) => {
7
+ const positionSource = Array.isArray(point?.position)
8
+ ? point.position
9
+ : Array.isArray(fallbackPosition)
10
+ ? fallbackPosition
11
+ : [0, 0, 0];
12
+ const position = [
13
+ Number(positionSource[0]) || 0,
14
+ Number(positionSource[1]) || 0,
15
+ Number(positionSource[2]) || 0,
16
+ ];
17
+
18
+ // Ensure forward and backward extension distances
19
+ const forwardDistance = typeof point?.forwardDistance === "number"
20
+ ? Math.max(0, Number(point.forwardDistance))
21
+ : 1.0; // Default forward distance
22
+
23
+ const backwardDistance = typeof point?.backwardDistance === "number"
24
+ ? Math.max(0, Number(point.backwardDistance))
25
+ : 1.0; // Default backward distance
26
+
27
+ const flipDirection = typeof point?.flipDirection === "boolean"
28
+ ? point.flipDirection
29
+ : false;
30
+
31
+ // Store complete transformation matrix (position + rotation)
32
+ // Default to identity rotation (pointing along X-axis)
33
+ const rotation = Array.isArray(point?.rotation) && point.rotation.length === 9
34
+ ? point.rotation.slice() // Copy existing rotation matrix
35
+ : [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Default identity rotation (X-axis forward)
36
+
37
+ return {
38
+ id: String(point?.id ?? fallbackId ?? `pt-${Math.random().toString(36).slice(2)}`),
39
+ position,
40
+ rotation, // 3x3 rotation matrix stored as flat array
41
+ forwardDistance,
42
+ backwardDistance,
43
+ flipDirection,
44
+ };
45
+ };
46
+
47
+ export function normalizeSplineData(rawSpline) {
48
+ let spline = rawSpline;
49
+ if (!spline || typeof spline !== "object") {
50
+ spline = null;
51
+ }
52
+
53
+ let points = Array.isArray(spline?.points) ? spline.points : null;
54
+ if (!points || points.length < 2) {
55
+ points = [
56
+ {
57
+ id: "p0",
58
+ position: [0, 0, 0],
59
+ rotation: [1, 0, 0, 0, 1, 0, 0, 0, 1], // Identity matrix
60
+ forwardDistance: 1.0,
61
+ backwardDistance: 1.0,
62
+ flipDirection: false
63
+ },
64
+ {
65
+ id: "p1",
66
+ position: [5, 0, 0],
67
+ rotation: [1, 0, 0, 0, 1, 0, 0, 0, 1], // Identity matrix
68
+ forwardDistance: 1.0,
69
+ backwardDistance: 1.0,
70
+ flipDirection: false
71
+ },
72
+ ];
73
+ }
74
+
75
+ const normalizedPoints = points.map((pt, index) =>
76
+ ensurePoint(pt, `p${index}`, null, index)
77
+ );
78
+
79
+ return {
80
+ points: normalizedPoints,
81
+ };
82
+ }
83
+
84
+ const hermitePoint = (p0, p1, t0, t1, t) => {
85
+ const t2 = t * t;
86
+ const t3 = t2 * t;
87
+ const h00 = 2 * t3 - 3 * t2 + 1;
88
+ const h10 = t3 - 2 * t2 + t;
89
+ const h01 = -2 * t3 + 3 * t2;
90
+ const h11 = t3 - t2;
91
+
92
+ const out = new THREE.Vector3();
93
+ out
94
+ .addScaledVector(p0, h00)
95
+ .addScaledVector(t0, h10)
96
+ .addScaledVector(p1, h01)
97
+ .addScaledVector(t1, h11);
98
+ return out;
99
+ };
100
+
101
+ export function buildHermitePolyline(spline, resolution = DEFAULT_RESOLUTION, bendRadius = 1.0) {
102
+ const pointsData = Array.isArray(spline?.points) ? spline.points : [];
103
+
104
+ if (pointsData.length < 2) {
105
+ return { positions: [], polyline: [] };
106
+ }
107
+
108
+ const samplesPerSegment = Math.max(4, Math.floor(resolution));
109
+ const positions = [];
110
+ const polyline = [];
111
+
112
+ // Clamp bend radius to reasonable range
113
+ const clampedBendRadius = Math.max(0.1, Math.min(5.0, Number(bendRadius) || 1.0));
114
+
115
+ // Helper function to calculate extension point
116
+ const calculateExtensionPoint = (anchor, pointData, isForward) => {
117
+ const rotation = pointData.rotation || [1, 0, 0, 0, 1, 0, 0, 0, 1];
118
+ let direction = new THREE.Vector3(rotation[0], rotation[1], rotation[2]);
119
+
120
+ if (pointData.flipDirection) {
121
+ direction.multiplyScalar(-1);
122
+ }
123
+
124
+ const distance = isForward ? pointData.forwardDistance : pointData.backwardDistance;
125
+ const extensionDir = isForward ? direction : direction.clone().multiplyScalar(-1);
126
+
127
+ return anchor.clone().add(extensionDir.multiplyScalar(distance));
128
+ };
129
+
130
+ // Helper function to add straight line segment with specified number of samples
131
+ const addStraightSegment = (start, end, samples) => {
132
+ for (let i = 0; i <= samples; i++) {
133
+ const t = i / samples;
134
+ const point = start.clone().lerp(end, t);
135
+ positions.push(point.x, point.y, point.z);
136
+ polyline.push([point.x, point.y, point.z]);
137
+ }
138
+ };
139
+
140
+ // Convert control points to THREE.Vector3
141
+ const anchors = pointsData.map(
142
+ (pt) =>
143
+ new THREE.Vector3(
144
+ Number(pt.position?.[0]) || 0,
145
+ Number(pt.position?.[1]) || 0,
146
+ Number(pt.position?.[2]) || 0,
147
+ )
148
+ );
149
+
150
+ // Build the complete spline with straight and curved segments
151
+ for (let i = 0; i < pointsData.length - 1; i++) {
152
+ const currentAnchor = anchors[i];
153
+ const nextAnchor = anchors[i + 1];
154
+ const currentData = pointsData[i];
155
+ const nextData = pointsData[i + 1];
156
+
157
+ // Calculate extension points
158
+ const currentForwardExt = calculateExtensionPoint(currentAnchor, currentData, true);
159
+ const nextBackwardExt = calculateExtensionPoint(nextAnchor, nextData, false);
160
+
161
+ // Use minimal samples for straight segments and maximum for curves
162
+ const straightSamples = Math.max(1, Math.floor(samplesPerSegment * 0.15)); // Only 15% for straight parts
163
+ const curvedSamples = Math.max(6, samplesPerSegment - (2 * straightSamples)); // Much more samples for smoother curves
164
+
165
+ // Segment 1: Straight line from current anchor to its forward extension point
166
+ if (i === 0) {
167
+ // For the first segment, include the starting point
168
+ addStraightSegment(currentAnchor, currentForwardExt, straightSamples);
169
+ } else {
170
+ // Skip the first point to avoid duplication
171
+ for (let j = 1; j <= straightSamples; j++) {
172
+ const t = j / straightSamples;
173
+ const point = currentAnchor.clone().lerp(currentForwardExt, t);
174
+ positions.push(point.x, point.y, point.z);
175
+ polyline.push([point.x, point.y, point.z]);
176
+ }
177
+ }
178
+
179
+ // Segment 2: Curved hermite interpolation from current forward extension to next backward extension
180
+ // Create tangent vectors for the curved segment
181
+ const currentExtDirection = currentForwardExt.clone().sub(currentAnchor).normalize();
182
+ const nextExtDirection = nextAnchor.clone().sub(nextBackwardExt).normalize();
183
+
184
+ // Calculate tangent scale based on user-controlled bend radius
185
+ const extDistance = currentForwardExt.distanceTo(nextBackwardExt);
186
+ const avgExtDistance = (currentData.forwardDistance + nextData.backwardDistance) * 0.5;
187
+
188
+ // Use the bend radius to control curve smoothness
189
+ // Lower bend radius = tighter curves, higher bend radius = smoother curves
190
+ const baseScale = Math.max(extDistance * 0.3, avgExtDistance * 0.5); // Much more conservative base
191
+ const tangentScale = baseScale * clampedBendRadius;
192
+
193
+ const t0 = currentExtDirection.multiplyScalar(tangentScale);
194
+ const t1 = nextExtDirection.multiplyScalar(tangentScale);
195
+
196
+ // Add the curved segment (skip first point to avoid duplication)
197
+ for (let j = 1; j <= curvedSamples; j++) {
198
+ const t = j / curvedSamples;
199
+ const point = hermitePoint(currentForwardExt, nextBackwardExt, t0, t1, t);
200
+ positions.push(point.x, point.y, point.z);
201
+ polyline.push([point.x, point.y, point.z]);
202
+ }
203
+
204
+ // Segment 3: Straight line from next backward extension to next anchor
205
+ for (let j = 1; j <= straightSamples; j++) {
206
+ const t = j / straightSamples;
207
+ const point = nextBackwardExt.clone().lerp(nextAnchor, t);
208
+ positions.push(point.x, point.y, point.z);
209
+ polyline.push([point.x, point.y, point.z]);
210
+ }
211
+ }
212
+
213
+ return { positions, polyline };
214
+ }
215
+
216
+ export function cloneSplineData(spline) {
217
+ return JSON.parse(JSON.stringify(spline || null));
218
+ }
@@ -0,0 +1,110 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+ import { selectionHasSketch } from "../selectionUtils.js";
3
+
4
+ const inputParamsSchema = {
5
+ id: {
6
+ type: "string",
7
+ default_value: null,
8
+ hint: "unique identifier for the sweep feature",
9
+ },
10
+ profile: {
11
+ type: "reference_selection",
12
+ selectionFilter: ["SKETCH", "FACE"],
13
+ multiple: false,
14
+ default_value: null,
15
+ hint: "Select the profile to sweep",
16
+ },
17
+ consumeProfileSketch: {
18
+ type: "boolean",
19
+ default_value: true,
20
+ hint: "Remove the referenced sketch after creating the sweep. Turn off to keep it in the scene.",
21
+ },
22
+ path: {
23
+ type: "reference_selection",
24
+ selectionFilter: ["EDGE"],
25
+ multiple: true,
26
+ default_value: null,
27
+ hint: "Select one or more edges to define the sweep path (connected edges are chained)",
28
+ },
29
+ orientationMode: {
30
+ type: "options",
31
+ options: ["translate", "pathAlign"],
32
+ default_value: "translate",
33
+ hint: "Sweep orientation mode: 'translate' (fixed) or 'pathAlign' (profile aligns and rotates with path)",
34
+ },
35
+ twistAngle: {
36
+ type: "number",
37
+ default_value: 0,
38
+ hint: "Twist angle for the sweep",
39
+ },
40
+ boolean: {
41
+ type: "boolean_operation",
42
+ default_value: { targets: [], operation: 'NONE' },
43
+ hint: "Optional boolean operation with selected solids"
44
+ }
45
+ };
46
+
47
+ export class SweepFeature {
48
+ static shortName = "SW";
49
+ static longName = "Sweep";
50
+ static inputParamsSchema = inputParamsSchema;
51
+
52
+ constructor() {
53
+ this.inputParams = {};
54
+ this.persistentData = {};
55
+ }
56
+
57
+ uiFieldsTest(context) {
58
+ const params = this.inputParams || context?.params || {};
59
+ const partHistory = context?.history || null;
60
+ return selectionHasSketch(params.profile, partHistory) ? [] : ["consumeProfileSketch"];
61
+ }
62
+
63
+ async run(partHistory) {
64
+ // actual code to create the sweep feature.
65
+ const { profile, path, twistAngle, orientationMode } = this.inputParams;
66
+
67
+ // Require a valid path edge; sweep now only follows a path
68
+ const pathArr = Array.isArray(path) ? path.filter(Boolean) : (path ? [path] : []);
69
+ if (!pathArr.length) {
70
+ throw new Error('Sweep requires a path edge selection. Please select an EDGE to sweep along.');
71
+ }
72
+
73
+ // Resolve profile object: accept FACE or SKETCH group object
74
+ const obj = Array.isArray(profile) ? (profile[0] || null) : (profile || null);
75
+ let faceObj = obj;
76
+ if (obj && obj.type === 'SKETCH') {
77
+ faceObj = obj.children.find(ch => ch.type === 'FACE') || obj.children.find(ch => ch.userData?.faceName);
78
+ }
79
+
80
+ const removed = [];
81
+ // if the face is a child of a sketch we need to remove the sketch from the scene
82
+ const consumeSketch = this.inputParams?.consumeProfileSketch !== false;
83
+ if (consumeSketch && faceObj && faceObj.type === 'FACE' && faceObj.parent && faceObj.parent.type === 'SKETCH') {
84
+ removed.push(faceObj.parent);
85
+ }
86
+
87
+ const twistAngleNum = Number(twistAngle);
88
+
89
+ // Create the sweep solid
90
+ const sweep = new BREP.Sweep({
91
+ face: faceObj,
92
+ sweepPathEdges: pathArr,
93
+ mode: (orientationMode === 'pathAlign') ? 'pathAlign' : 'translate',
94
+ twistAngle: Number.isFinite(twistAngleNum) ? twistAngleNum : 0,
95
+ name: this.inputParams.featureID
96
+ });
97
+
98
+ sweep.collapseTinyTriangles(0.1);
99
+ sweep.simplify(0.1);
100
+
101
+
102
+ // Build and show the solid. Let errors surface so we can debug if needed.
103
+ sweep.visualize();
104
+
105
+ // Apply optional boolean operation via shared helper
106
+ const effects = await BREP.applyBooleanOperation(partHistory || {}, sweep, this.inputParams.boolean, this.inputParams.featureID);
107
+ effects.removed = [...removed, ...effects.removed];
108
+ return effects;
109
+ }
110
+ }
@@ -0,0 +1,152 @@
1
+ import { BREP } from "../../BREP/BREP.js";
2
+ const THREE = BREP.THREE;
3
+
4
+ const inputParamsSchema = {
5
+ id: {
6
+ type: "string",
7
+ default_value: null,
8
+ hint: "unique identifier for the loft feature",
9
+ },
10
+ solids: {
11
+ type: "reference_selection",
12
+ selectionFilter: ["SOLID"],
13
+ multiple: true,
14
+ default_value: [],
15
+ hint: "Select solids to transform",
16
+ },
17
+ space: {
18
+ type: "options",
19
+ options: ["WORLD", "LOCAL"],
20
+ default_value: "WORLD",
21
+ hint: "Interpret translation/rotation in WORLD or LOCAL space",
22
+ },
23
+ pivot: {
24
+ type: "options",
25
+ options: ["ORIGIN", "BBOX_CENTER"],
26
+ default_value: "ORIGIN",
27
+ hint: "Pivot point for rotation/scale",
28
+ },
29
+ translate: {
30
+ type: "vec3",
31
+ default_value: [0, 0, 0],
32
+ hint: "Translation [x,y,z]",
33
+ },
34
+ rotateEulerDeg: {
35
+ type: "vec3",
36
+ default_value: [0, 0, 0],
37
+ hint: "Rotation in degrees [rx,ry,rz] about pivot (XYZ order)",
38
+ },
39
+ scale: {
40
+ type: "vec3",
41
+ default_value: [1, 1, 1],
42
+ hint: "Non-uniform scale [sx,sy,sz] about pivot",
43
+ step: 0.1,
44
+ uniformToggle: true,
45
+ uniformDefault: true,
46
+ uniformLockLabel: 'Uniform scale',
47
+ },
48
+ copy: {
49
+ type: "boolean",
50
+ default_value: false,
51
+ label: "Copy",
52
+ hint: "Create a new transformed copy; keep the original",
53
+ },
54
+ };
55
+
56
+ export class TransformFeature {
57
+ static shortName = "XFORM";
58
+ static longName = "Transform";
59
+ static inputParamsSchema = inputParamsSchema;
60
+
61
+ constructor() {
62
+ this.inputParams = {};
63
+ this.persistentData = {};
64
+ }
65
+
66
+ async run(partHistory) {
67
+ const solids = Array.isArray(this.inputParams.solids) ? this.inputParams.solids.filter(s => s && s.type === 'SOLID') : [];
68
+ if (!solids.length) return { added: [], removed: [] };
69
+
70
+ const space = (this.inputParams.space || 'WORLD').toUpperCase();
71
+ const pivot = (this.inputParams.pivot || 'ORIGIN').toUpperCase();
72
+ const t = toVec3(this.inputParams.translate, 0, 0, 0);
73
+ const rDeg = toVec3(this.inputParams.rotateEulerDeg, 0, 0, 0);
74
+ const s = toVec3(this.inputParams.scale, 1, 1, 1);
75
+ const copy = !!this.inputParams.copy;
76
+ const replace = !copy;
77
+
78
+ const out = [];
79
+ const removed = [];
80
+ for (const src of solids) {
81
+ const dst = src.clone();
82
+
83
+ // Compute pivot point in world
84
+ const pivotPoint = (pivot === 'BBOX_CENTER') ? bboxCenterWorld(src) : new THREE.Vector3(0, 0, 0);
85
+
86
+ // Build rotation and scale in requested space
87
+ const eulerRad = new THREE.Euler(
88
+ THREE.MathUtils.degToRad(rDeg.x),
89
+ THREE.MathUtils.degToRad(rDeg.y),
90
+ THREE.MathUtils.degToRad(rDeg.z),
91
+ 'XYZ'
92
+ );
93
+ const quat = new THREE.Quaternion().setFromEuler(eulerRad);
94
+ const scl = new THREE.Vector3(s.x, s.y, s.z);
95
+
96
+ // Construct matrix with pivot: T * Tr(pivot) * R * S * Tr(-pivot)
97
+ const M = new THREE.Matrix4();
98
+ const Tpivot = new THREE.Matrix4().makeTranslation(pivotPoint.x, pivotPoint.y, pivotPoint.z);
99
+ const Tnp = new THREE.Matrix4().makeTranslation(-pivotPoint.x, -pivotPoint.y, -pivotPoint.z);
100
+ const RS = new THREE.Matrix4().compose(new THREE.Vector3(0, 0, 0), quat, scl);
101
+ const Tmove = new THREE.Matrix4().makeTranslation(t.x, t.y, t.z);
102
+
103
+ if (space === 'LOCAL') {
104
+ // Translate in local space of src: convert local move to world by applying src's world matrix
105
+ const localDir = new THREE.Vector3(t.x, t.y, t.z).applyMatrix3(new THREE.Matrix3().setFromMatrix4(src.matrixWorld));
106
+ Tmove.setPosition(localDir);
107
+ }
108
+
109
+ M.multiply(Tmove).multiply(Tpivot).multiply(RS).multiply(Tnp);
110
+
111
+ dst.bakeTransform(M); // bake into geometry arrays
112
+ if (replace) {
113
+ // Keep the original name when replacing
114
+ dst.name = src.name || dst.name || 'Solid';
115
+ } else {
116
+ // Name copies with _COPY suffix
117
+ const base = src.name || 'Solid';
118
+ dst.name = `${base}_${this.inputParams.id}`;
119
+ }
120
+ dst.visualize();
121
+
122
+ if (replace) {
123
+ try { src.__removeFlag = true; } catch {}
124
+ removed.push(src);
125
+ }
126
+ out.push(dst);
127
+ }
128
+ return { added: out, removed };
129
+ }
130
+ }
131
+
132
+ function toVec3(v, dx, dy, dz) {
133
+ if (Array.isArray(v)) return new THREE.Vector3(v[0] ?? dx, v[1] ?? dy, v[2] ?? dz);
134
+ if (v && typeof v === 'object') return new THREE.Vector3(v.x ?? dx, v.y ?? dy, v.z ?? dz);
135
+ return new THREE.Vector3(dx, dy, dz);
136
+ }
137
+
138
+ function bboxCenterWorld(solid) {
139
+ const mesh = solid.getMesh();
140
+ try {
141
+ const vp = mesh.vertProperties;
142
+ if (!vp || vp.length < 3) return new THREE.Vector3(0, 0, 0);
143
+ const bbMin = new THREE.Vector3(+Infinity, +Infinity, +Infinity);
144
+ const bbMax = new THREE.Vector3(-Infinity, -Infinity, -Infinity);
145
+ for (let i = 0; i < vp.length; i += 3) {
146
+ const x = vp[i + 0], y = vp[i + 1], z = vp[i + 2];
147
+ bbMin.x = Math.min(bbMin.x, x); bbMin.y = Math.min(bbMin.y, y); bbMin.z = Math.min(bbMin.z, z);
148
+ bbMax.x = Math.max(bbMax.x, x); bbMax.y = Math.max(bbMax.y, y); bbMax.z = Math.max(bbMax.z, z);
149
+ }
150
+ return bbMin.add(bbMax).multiplyScalar(0.5);
151
+ } finally { try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch {} }
152
+ }