brep-io-kernel 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +144 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/dist-kernel/help/CONTRIBUTING.html +248 -0
  5. package/dist-kernel/help/LICENSE.html +248 -0
  6. package/dist-kernel/help/MODELING.png +0 -0
  7. package/dist-kernel/help/PMI.png +0 -0
  8. package/dist-kernel/help/SKETCH.png +0 -0
  9. package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
  10. package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
  11. package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
  12. package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
  13. package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
  14. package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
  15. package/dist-kernel/help/assembly-constraints__angle-constraint.html +248 -0
  16. package/dist-kernel/help/assembly-constraints__coincident-constraint.html +248 -0
  17. package/dist-kernel/help/assembly-constraints__distance-constraint.html +248 -0
  18. package/dist-kernel/help/assembly-constraints__fixed-constraint.html +248 -0
  19. package/dist-kernel/help/assembly-constraints__parallel-constraint.html +248 -0
  20. package/dist-kernel/help/assembly-constraints__solver.html +248 -0
  21. package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +248 -0
  22. package/dist-kernel/help/brep-api.html +263 -0
  23. package/dist-kernel/help/brep-kernel.html +258 -0
  24. package/dist-kernel/help/brep-model.html +248 -0
  25. package/dist-kernel/help/cylindrical-face-radius-embedding.html +290 -0
  26. package/dist-kernel/help/dialog-screenshots.html +248 -0
  27. package/dist-kernel/help/extruded-sketch-radius-embedding.html +336 -0
  28. package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
  29. package/dist-kernel/help/features__Boolean_dialog.png +0 -0
  30. package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
  31. package/dist-kernel/help/features__Datium_dialog.png +0 -0
  32. package/dist-kernel/help/features__Extrude_dialog.png +0 -0
  33. package/dist-kernel/help/features__Fillet_dialog.png +0 -0
  34. package/dist-kernel/help/features__Helix_dialog.png +0 -0
  35. package/dist-kernel/help/features__Hole_dialog.png +0 -0
  36. package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
  37. package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
  38. package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
  39. package/dist-kernel/help/features__Loft_dialog.png +0 -0
  40. package/dist-kernel/help/features__Mirror_dialog.png +0 -0
  41. package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
  42. package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
  43. package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
  44. package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
  45. package/dist-kernel/help/features__Pattern_dialog.png +0 -0
  46. package/dist-kernel/help/features__Plane_dialog.png +0 -0
  47. package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
  48. package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
  49. package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
  50. package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
  51. package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
  52. package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
  53. package/dist-kernel/help/features__Remesh_dialog.png +0 -0
  54. package/dist-kernel/help/features__Revolve_dialog.png +0 -0
  55. package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
  56. package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
  57. package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
  58. package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
  59. package/dist-kernel/help/features__Sketch_dialog.png +0 -0
  60. package/dist-kernel/help/features__Spline_dialog.png +0 -0
  61. package/dist-kernel/help/features__Sweep_dialog.png +0 -0
  62. package/dist-kernel/help/features__Transform_dialog.png +0 -0
  63. package/dist-kernel/help/features__Tube_dialog.png +0 -0
  64. package/dist-kernel/help/features__assembly-component.html +248 -0
  65. package/dist-kernel/help/features__boolean.html +248 -0
  66. package/dist-kernel/help/features__chamfer.html +248 -0
  67. package/dist-kernel/help/features__datium.html +248 -0
  68. package/dist-kernel/help/features__datum.html +248 -0
  69. package/dist-kernel/help/features__extrude.html +248 -0
  70. package/dist-kernel/help/features__fillet.html +248 -0
  71. package/dist-kernel/help/features__helix.html +248 -0
  72. package/dist-kernel/help/features__hole.html +248 -0
  73. package/dist-kernel/help/features__image-heightmap-solid.html +248 -0
  74. package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
  75. package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
  76. package/dist-kernel/help/features__image-to-face.html +248 -0
  77. package/dist-kernel/help/features__import-3d-model.html +248 -0
  78. package/dist-kernel/help/features__index.html +248 -0
  79. package/dist-kernel/help/features__loft.html +248 -0
  80. package/dist-kernel/help/features__mirror.html +248 -0
  81. package/dist-kernel/help/features__offset-shell.html +248 -0
  82. package/dist-kernel/help/features__pattern-linear.html +248 -0
  83. package/dist-kernel/help/features__pattern-radial.html +248 -0
  84. package/dist-kernel/help/features__pattern.html +248 -0
  85. package/dist-kernel/help/features__plane.html +248 -0
  86. package/dist-kernel/help/features__primitive-cone.html +248 -0
  87. package/dist-kernel/help/features__primitive-cube.html +248 -0
  88. package/dist-kernel/help/features__primitive-cylinder.html +248 -0
  89. package/dist-kernel/help/features__primitive-pyramid.html +248 -0
  90. package/dist-kernel/help/features__primitive-sphere.html +248 -0
  91. package/dist-kernel/help/features__primitive-torus.html +248 -0
  92. package/dist-kernel/help/features__remesh.html +248 -0
  93. package/dist-kernel/help/features__revolve.html +248 -0
  94. package/dist-kernel/help/features__sheet-metal-contour-flange.html +248 -0
  95. package/dist-kernel/help/features__sheet-metal-flange.html +248 -0
  96. package/dist-kernel/help/features__sheet-metal-tab.html +248 -0
  97. package/dist-kernel/help/features__sketch.html +248 -0
  98. package/dist-kernel/help/features__spline.html +248 -0
  99. package/dist-kernel/help/features__sweep.html +248 -0
  100. package/dist-kernel/help/features__transform.html +248 -0
  101. package/dist-kernel/help/features__tube.html +248 -0
  102. package/dist-kernel/help/file-formats.html +248 -0
  103. package/dist-kernel/help/getting-started.html +248 -0
  104. package/dist-kernel/help/highlights.html +248 -0
  105. package/dist-kernel/help/history-systems.html +248 -0
  106. package/dist-kernel/help/how-it-works.html +248 -0
  107. package/dist-kernel/help/index.html +862 -0
  108. package/dist-kernel/help/input-params-schema.html +363 -0
  109. package/dist-kernel/help/inspector-improvements.html +248 -0
  110. package/dist-kernel/help/inspector.html +248 -0
  111. package/dist-kernel/help/modes__modeling.html +248 -0
  112. package/dist-kernel/help/modes__pmi.html +248 -0
  113. package/dist-kernel/help/modes__sketch.html +248 -0
  114. package/dist-kernel/help/plugins.html +248 -0
  115. package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
  116. package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
  117. package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
  118. package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
  119. package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
  120. package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
  121. package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
  122. package/dist-kernel/help/pmi-annotations__angle-dimension.html +248 -0
  123. package/dist-kernel/help/pmi-annotations__explode-body.html +248 -0
  124. package/dist-kernel/help/pmi-annotations__hole-callout.html +248 -0
  125. package/dist-kernel/help/pmi-annotations__index.html +248 -0
  126. package/dist-kernel/help/pmi-annotations__leader.html +248 -0
  127. package/dist-kernel/help/pmi-annotations__linear-dimension.html +248 -0
  128. package/dist-kernel/help/pmi-annotations__note.html +248 -0
  129. package/dist-kernel/help/pmi-annotations__radial-dimension.html +248 -0
  130. package/dist-kernel/help/search-index.json +464 -0
  131. package/dist-kernel/help/simplified-radial-dimensions.html +298 -0
  132. package/dist-kernel/help/solid-methods.html +359 -0
  133. package/dist-kernel/help/table-of-contents.html +330 -0
  134. package/dist-kernel/help/ui-overview.html +248 -0
  135. package/dist-kernel/help/whats-new.html +248 -0
  136. package/package.json +54 -0
  137. package/src/BREP/AssemblyComponent.js +42 -0
  138. package/src/BREP/BREP.js +43 -0
  139. package/src/BREP/BetterSolid.js +805 -0
  140. package/src/BREP/Edge.js +103 -0
  141. package/src/BREP/Extrude.js +403 -0
  142. package/src/BREP/Face.js +187 -0
  143. package/src/BREP/MeshRepairer.js +634 -0
  144. package/src/BREP/OffsetShellSolid.js +614 -0
  145. package/src/BREP/PointCloudWrap.js +302 -0
  146. package/src/BREP/Revolve.js +345 -0
  147. package/src/BREP/SolidMethods/authoring.js +112 -0
  148. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  149. package/src/BREP/SolidMethods/chamfer.js +122 -0
  150. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  151. package/src/BREP/SolidMethods/fillet.js +792 -0
  152. package/src/BREP/SolidMethods/index.js +72 -0
  153. package/src/BREP/SolidMethods/io.js +105 -0
  154. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  155. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  156. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  157. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  158. package/src/BREP/SolidMethods/metadata.js +106 -0
  159. package/src/BREP/SolidMethods/metrics.js +51 -0
  160. package/src/BREP/SolidMethods/transforms.js +361 -0
  161. package/src/BREP/SolidMethods/visualize.js +508 -0
  162. package/src/BREP/SolidShared.js +26 -0
  163. package/src/BREP/Sweep.js +1596 -0
  164. package/src/BREP/Tube.js +857 -0
  165. package/src/BREP/Vertex.js +43 -0
  166. package/src/BREP/applyBooleanOperation.js +704 -0
  167. package/src/BREP/boundsUtils.js +48 -0
  168. package/src/BREP/chamfer.js +551 -0
  169. package/src/BREP/edgePolylineUtils.js +85 -0
  170. package/src/BREP/fillets/common.js +388 -0
  171. package/src/BREP/fillets/fillet.js +1422 -0
  172. package/src/BREP/fillets/filletGeometry.js +15 -0
  173. package/src/BREP/fillets/inset.js +389 -0
  174. package/src/BREP/fillets/offsetHelper.js +143 -0
  175. package/src/BREP/fillets/outset.js +88 -0
  176. package/src/BREP/helix.js +193 -0
  177. package/src/BREP/meshToBrep.js +234 -0
  178. package/src/BREP/primitives.js +279 -0
  179. package/src/BREP/setupManifold.js +71 -0
  180. package/src/BREP/threadGeometry.js +1120 -0
  181. package/src/BREP/triangleUtils.js +8 -0
  182. package/src/BREP/triangulate.js +608 -0
  183. package/src/FeatureRegistry.js +183 -0
  184. package/src/PartHistory.js +1132 -0
  185. package/src/UI/AccordionWidget.js +292 -0
  186. package/src/UI/CADmaterials.js +850 -0
  187. package/src/UI/EnvMonacoEditor.js +522 -0
  188. package/src/UI/FloatingWindow.js +396 -0
  189. package/src/UI/HistoryWidget.js +457 -0
  190. package/src/UI/MainToolbar.js +131 -0
  191. package/src/UI/ModelLibraryView.js +194 -0
  192. package/src/UI/OrthoCameraIdle.js +206 -0
  193. package/src/UI/PluginsWidget.js +280 -0
  194. package/src/UI/SceneListing.js +606 -0
  195. package/src/UI/SelectionFilter.js +629 -0
  196. package/src/UI/ViewCube.js +389 -0
  197. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  198. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  199. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  200. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  201. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  202. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  203. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  204. package/src/UI/assembly/constraintPointUtils.js +64 -0
  205. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  206. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  207. package/src/UI/componentSelectorModal.js +240 -0
  208. package/src/UI/controls/CombinedTransformControls.js +386 -0
  209. package/src/UI/dialogs.js +351 -0
  210. package/src/UI/expressionsManager.js +100 -0
  211. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  212. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  213. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  214. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  215. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  216. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  217. package/src/UI/featureDialogWidgets/index.js +34 -0
  218. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  219. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  220. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  221. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  222. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  223. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  224. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  225. package/src/UI/featureDialogWidgets/utils.js +43 -0
  226. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  227. package/src/UI/featureDialogs.js +1414 -0
  228. package/src/UI/fileManagerWidget.js +615 -0
  229. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  230. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  231. package/src/UI/history/historyDisplayInfo.js +133 -0
  232. package/src/UI/mobile.js +28 -0
  233. package/src/UI/objectDump.js +442 -0
  234. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  235. package/src/UI/pmi/AnnotationHistory.js +353 -0
  236. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  237. package/src/UI/pmi/BaseAnnotation.js +269 -0
  238. package/src/UI/pmi/LabelOverlay.css +102 -0
  239. package/src/UI/pmi/LabelOverlay.js +191 -0
  240. package/src/UI/pmi/PMIMode.js +1550 -0
  241. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  242. package/src/UI/pmi/annUtils.js +729 -0
  243. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  244. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  245. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  246. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  247. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  248. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  249. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  250. package/src/UI/pmi/pmiStyle.js +44 -0
  251. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  252. package/src/UI/sketcher/dimensions.js +674 -0
  253. package/src/UI/sketcher/glyphs.js +236 -0
  254. package/src/UI/sketcher/highlights.js +60 -0
  255. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  256. package/src/UI/toolbarButtons/exportButton.js +609 -0
  257. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  258. package/src/UI/toolbarButtons/importButton.js +160 -0
  259. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  260. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  261. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  262. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  263. package/src/UI/toolbarButtons/saveButton.js +99 -0
  264. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  265. package/src/UI/toolbarButtons/testsButton.js +26 -0
  266. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  267. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  268. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  269. package/src/UI/triangleDebuggerWindow.js +945 -0
  270. package/src/UI/viewer.js +4228 -0
  271. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  272. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  273. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  274. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  275. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  276. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  277. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  278. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  279. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  280. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  281. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  282. package/src/core/entities/HistoryCollectionBase.js +72 -0
  283. package/src/core/entities/ListEntityBase.js +109 -0
  284. package/src/core/entities/schemaProcesser.js +121 -0
  285. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  286. package/src/exporters/sheetMetalUnfold.js +862 -0
  287. package/src/exporters/step.js +1135 -0
  288. package/src/exporters/threeMF.js +575 -0
  289. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  290. package/src/features/boolean/BooleanFeature.js +94 -0
  291. package/src/features/chamfer/ChamferFeature.js +116 -0
  292. package/src/features/datium/DatiumFeature.js +80 -0
  293. package/src/features/edgeFeatureUtils.js +41 -0
  294. package/src/features/extrude/ExtrudeFeature.js +143 -0
  295. package/src/features/fillet/FilletFeature.js +197 -0
  296. package/src/features/helix/HelixFeature.js +405 -0
  297. package/src/features/hole/HoleFeature.js +1050 -0
  298. package/src/features/hole/screwClearance.js +86 -0
  299. package/src/features/hole/threadDesignationCatalog.js +149 -0
  300. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  301. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  302. package/src/features/imageToFace/imageEditor.js +1270 -0
  303. package/src/features/imageToFace/traceUtils.js +971 -0
  304. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  305. package/src/features/loft/LoftFeature.js +605 -0
  306. package/src/features/mirror/MirrorFeature.js +151 -0
  307. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  308. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  309. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  310. package/src/features/pattern/PatternFeature.js +275 -0
  311. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  312. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  313. package/src/features/plane/PlaneFeature.js +154 -0
  314. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  315. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  316. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  317. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  318. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  319. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  320. package/src/features/remesh/RemeshFeature.js +97 -0
  321. package/src/features/revolve/RevolveFeature.js +111 -0
  322. package/src/features/selectionUtils.js +118 -0
  323. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  324. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  325. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  326. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  327. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  328. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  329. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  330. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  331. package/src/features/sheetMetal/profileUtils.js +25 -0
  332. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  333. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  334. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  335. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  336. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  337. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  338. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  339. package/src/features/sketch/SketchFeature.js +955 -0
  340. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  341. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  342. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  343. package/src/features/spline/SplineEditorSession.js +988 -0
  344. package/src/features/spline/SplineFeature.js +1388 -0
  345. package/src/features/spline/splineUtils.js +218 -0
  346. package/src/features/sweep/SweepFeature.js +110 -0
  347. package/src/features/transform/TransformFeature.js +152 -0
  348. package/src/features/tube/TubeFeature.js +635 -0
  349. package/src/fs.proxy.js +625 -0
  350. package/src/idbStorage.js +254 -0
  351. package/src/index.js +12 -0
  352. package/src/main.js +15 -0
  353. package/src/metadataManager.js +64 -0
  354. package/src/path.proxy.js +277 -0
  355. package/src/plugins/ghLoader.worker.js +151 -0
  356. package/src/plugins/pluginManager.js +286 -0
  357. package/src/pmi/PMIViewsManager.js +134 -0
  358. package/src/services/componentLibrary.js +198 -0
  359. package/src/tests/ConsoleCapture.js +189 -0
  360. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  361. package/src/tests/browserTests.js +597 -0
  362. package/src/tests/debugBoolean.js +225 -0
  363. package/src/tests/partFiles/badBoolean.json +957 -0
  364. package/src/tests/partFiles/extrudeTest.json +88 -0
  365. package/src/tests/partFiles/filletFail.json +58 -0
  366. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  367. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  368. package/src/tests/test_boolean_subtract.js +27 -0
  369. package/src/tests/test_chamfer.js +17 -0
  370. package/src/tests/test_extrudeFace.js +24 -0
  371. package/src/tests/test_fillet.js +17 -0
  372. package/src/tests/test_fillet_nonClosed.js +45 -0
  373. package/src/tests/test_filletsMoreDifficult.js +46 -0
  374. package/src/tests/test_history_features_basic.js +149 -0
  375. package/src/tests/test_hole.js +282 -0
  376. package/src/tests/test_mirror.js +16 -0
  377. package/src/tests/test_offsetShellGrouping.js +85 -0
  378. package/src/tests/test_plane.js +4 -0
  379. package/src/tests/test_primitiveCone.js +11 -0
  380. package/src/tests/test_primitiveCube.js +7 -0
  381. package/src/tests/test_primitiveCylinder.js +8 -0
  382. package/src/tests/test_primitivePyramid.js +9 -0
  383. package/src/tests/test_primitiveSphere.js +17 -0
  384. package/src/tests/test_primitiveTorus.js +21 -0
  385. package/src/tests/test_pushFace.js +126 -0
  386. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  387. package/src/tests/test_sheetMetal_features.js +80 -0
  388. package/src/tests/test_sketch_openLoop.js +45 -0
  389. package/src/tests/test_solidMetrics.js +58 -0
  390. package/src/tests/test_stlLoader.js +1889 -0
  391. package/src/tests/test_sweepFace.js +55 -0
  392. package/src/tests/test_tube.js +45 -0
  393. package/src/tests/test_tube_closedLoop.js +67 -0
  394. package/src/tests/tests.js +493 -0
  395. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  396. package/src/tools/dialogCapturePageFactory.js +227 -0
  397. package/src/tools/featureDialogCapturePage.js +47 -0
  398. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  399. package/src/utils/axisHelpers.js +99 -0
  400. package/src/utils/deepClone.js +69 -0
  401. package/src/utils/geometryTolerance.js +37 -0
  402. package/src/utils/normalizeTypeString.js +8 -0
  403. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,103 @@
1
+ import * as THREE from "three";
2
+ import { CADmaterials } from "../UI/CADmaterials.js";
3
+ import { Line2 } from "three/examples/jsm/Addons.js";
4
+
5
+ export class Edge extends Line2 {
6
+ constructor(geometry) {
7
+ super(geometry, CADmaterials.EDGE.BASE);
8
+ this.faces = [];
9
+ this.name = null;
10
+ this.type = 'EDGE';
11
+ this.renderOrder = 2;
12
+ this.closedLoop = false;
13
+ }
14
+
15
+ // Total polyline length in world space
16
+ length() {
17
+ const tmpA = new THREE.Vector3();
18
+ const tmpB = new THREE.Vector3();
19
+ let total = 0;
20
+
21
+ // Prefer positions from visualize() payload
22
+ const pts = this.userData && Array.isArray(this.userData.polylineLocal)
23
+ ? this.userData.polylineLocal
24
+ : null;
25
+
26
+ const addSeg = (ax, ay, az, bx, by, bz) => {
27
+ tmpA.set(ax, ay, az).applyMatrix4(this.matrixWorld);
28
+ tmpB.set(bx, by, bz).applyMatrix4(this.matrixWorld);
29
+ total += tmpA.distanceTo(tmpB);
30
+ };
31
+
32
+ if (pts && pts.length >= 2) {
33
+ for (let i = 0; i < pts.length - 1; i++) {
34
+ const p = pts[i];
35
+ const q = pts[i + 1];
36
+ addSeg(p[0], p[1], p[2], q[0], q[1], q[2]);
37
+ }
38
+ return total;
39
+ }
40
+
41
+ // Fallback: read from geometry positions if available
42
+ const pos = this.geometry && this.geometry.getAttribute && this.geometry.getAttribute('position');
43
+ if (pos && pos.itemSize === 3 && pos.count >= 2) {
44
+ for (let i = 0; i < pos.count - 1; i++) {
45
+ addSeg(
46
+ pos.getX(i), pos.getY(i), pos.getZ(i),
47
+ pos.getX(i + 1), pos.getY(i + 1), pos.getZ(i + 1)
48
+ );
49
+ }
50
+ return total;
51
+ }
52
+
53
+ return 0;
54
+ }
55
+
56
+ points(applyWorld = true) {
57
+ // Return an array of {x,y,z} points along the polyline.
58
+ // Prefer polylineLocal from userData (installed by visualize), else fallback to geometry positions.
59
+ const tmp = new THREE.Vector3();
60
+ const out = [];
61
+
62
+ const pts = this.userData && Array.isArray(this.userData.polylineLocal)
63
+ ? this.userData.polylineLocal
64
+ : null;
65
+
66
+ if (pts && pts.length) {
67
+ for (let i = 0; i < pts.length; i++) {
68
+ const p = pts[i];
69
+ tmp.set(p[0], p[1], p[2]);
70
+ if (applyWorld) tmp.applyMatrix4(this.matrixWorld);
71
+ out.push({ x: tmp.x, y: tmp.y, z: tmp.z });
72
+ }
73
+ return out;
74
+ }
75
+
76
+ const pos = this.geometry && this.geometry.getAttribute && this.geometry.getAttribute('position');
77
+ if (pos && pos.itemSize === 3 && pos.count >= 1) {
78
+ for (let i = 0; i < pos.count; i++) {
79
+ tmp.set(pos.getX(i), pos.getY(i), pos.getZ(i));
80
+ if (applyWorld) tmp.applyMatrix4(this.matrixWorld);
81
+ out.push({ x: tmp.x, y: tmp.y, z: tmp.z });
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+
87
+
88
+ setMetadata(metadata) {
89
+ // call the approriate method in the parent solid
90
+ if (this.parentSolid && typeof this.parentSolid.setEdgeMetadata === 'function') {
91
+ this.parentSolid.setEdgeMetadata(this.name, metadata);
92
+ }
93
+ return this;
94
+ }
95
+
96
+ getMetadata() {
97
+ // call the approriate method in the parent solid
98
+ if (this.parentSolid && typeof this.parentSolid.getEdgeMetadata === 'function') {
99
+ return this.parentSolid.getEdgeMetadata(this.name);
100
+ }
101
+ return null;
102
+ }
103
+ }
@@ -0,0 +1,403 @@
1
+ import * as THREE from 'three';
2
+ import { Solid } from './BetterSolid.js';
3
+ import { getEdgePolylineWorld } from './edgePolylineUtils.js';
4
+ import { computeBoundsFromVertices } from './boundsUtils.js';
5
+
6
+ // Build a closed extruded solid from a single face by translating
7
+ // the face and sweeping exactly one side wall per original edge.
8
+ //
9
+ // Guarantees
10
+ // - One labeled side wall per input edge: `${edge.name||'EDGE'}_SW`.
11
+ // - Caps are a translation of the original face triangles: start is reversed,
12
+ // end is translated by `dirF`. If `dirB` is provided, start cap is placed at
13
+ // `face + dirB`; otherwise it uses the base face.
14
+ // - Never attempts to close an open edge polyline unless its endpoints are
15
+ // exactly identical. Consecutive duplicate points are removed.
16
+ // - Calls `setEpsilon(adaptive)` to weld vertices and drop degenerates.
17
+ export class ExtrudeSolid extends Solid {
18
+ /**
19
+ * @param {object} [opts]
20
+ * @param {import('./Face.js').Face} opts.face Source face to extrude
21
+ * @param {number|import('three').Vector3} [opts.distance=1] Extrusion distance or explicit vector
22
+ * @param {import('three').Vector3|null} [opts.dir=null] Optional direction vector override
23
+ * @param {number} [opts.distanceBack=0] Optional backward extrusion distance
24
+ * @param {string} [opts.name='Extrude'] Name of the resulting solid
25
+ * @param {string|null} [opts.sideFaceName=null] Optional override for all side-wall face names
26
+ */
27
+ constructor({ face, distance = 1, dir = null, distanceBack = 0, name = 'Extrude', sideFaceName = null } = {}) {
28
+ super();
29
+ this.name = name;
30
+ this.params = { face, distance, dir, distanceBack, sideFaceName };
31
+ this.generate();
32
+ }
33
+
34
+ generate() {
35
+ const { face, distance, dir, distanceBack } = this.params;
36
+ if (!face || !face.geometry) return this;
37
+
38
+ // Compute forward/backward translation vectors
39
+ let dirF = null; let dirB = null;
40
+ if (dir && dir.isVector3) {
41
+ dirF = dir.clone();
42
+ } else if (distance instanceof THREE.Vector3) {
43
+ dirF = distance.clone();
44
+ } else if (typeof distance === 'number') {
45
+ const n = (typeof face.getAverageNormal === 'function') ? face.getAverageNormal().clone() : new THREE.Vector3(0, 1, 0);
46
+ if (n.lengthSq() < 1e-20) n.set(0, 1, 0);
47
+ dirF = n.normalize().multiplyScalar(distance);
48
+ } else {
49
+ dirF = new THREE.Vector3(0, 1, 0);
50
+ }
51
+ const backMag = Math.abs(Number(distanceBack));
52
+ if (Number.isFinite(backMag) && backMag > 0) {
53
+ const n = dirF.clone();
54
+ if (n.lengthSq() < 1e-20) {
55
+ const nf = (typeof face.getAverageNormal === 'function') ? face.getAverageNormal().clone() : new THREE.Vector3(0, 1, 0);
56
+ dirB = nf.normalize().multiplyScalar(-backMag); // back is opposite of forward
57
+ } else {
58
+ dirB = n.normalize().multiplyScalar(-backMag); // back is opposite of forward
59
+ }
60
+ }
61
+
62
+ const featureTag = this.name ? `${this.name}:` : '';
63
+ const startName = `${featureTag}${face.name || 'Face'}_START`;
64
+ const endName = `${featureTag}${face.name || 'Face'}_END`;
65
+
66
+ // Helper: add two triangles for a quad using the better diagonal.
67
+ const addQuad = (faceName, A0, B0, B1, A1, isHole) => {
68
+ const v = (p, q) => new THREE.Vector3(q[0] - p[0], q[1] - p[1], q[2] - p[2]);
69
+ const areaTri = (a, b, c) => v(a, b).cross(v(a, c)).length();
70
+ const areaD1 = areaTri(A0, B0, B1) + areaTri(A0, B1, A1);
71
+ const areaD2 = areaTri(A0, B0, A1) + areaTri(B0, B1, A1);
72
+ const epsA = 1e-18;
73
+ if (!(areaD1 > epsA || areaD2 > epsA)) return;
74
+ if (areaD2 > areaD1) {
75
+ if (isHole) { this.addTriangle(faceName, A0, A1, B0); this.addTriangle(faceName, B0, A1, B1); }
76
+ else { this.addTriangle(faceName, A0, B0, A1); this.addTriangle(faceName, B0, B1, A1); }
77
+ } else {
78
+ if (isHole) { this.addTriangle(faceName, A0, B1, B0); this.addTriangle(faceName, A0, A1, B1); }
79
+ else { this.addTriangle(faceName, A0, B0, B1); this.addTriangle(faceName, A0, B1, A1); }
80
+ }
81
+ };
82
+
83
+ // Caps: copy face triangles in world space, translate end, reverse start.
84
+ const baseGeom = face.geometry;
85
+ const posAttr = baseGeom.getAttribute && baseGeom.getAttribute('position');
86
+ // Snap helpers to ensure side walls share exact vertices with caps
87
+ let __capWorld = null; // base cap vertices in world coords
88
+ let __snapStart = null; // Map rounded -> exact start cap vertex
89
+ let __snapEnd = null; // Map rounded -> exact end cap vertex
90
+ const __key = (p) => `${p[0].toFixed(7)},${p[1].toFixed(7)},${p[2].toFixed(7)}`;
91
+ const __makeSnapMap = (worldPts, offset) => {
92
+ if (!worldPts) return null;
93
+ const off = offset || new THREE.Vector3(0,0,0);
94
+ const m = new Map();
95
+ for (let i = 0; i < worldPts.length; i++) {
96
+ const w = worldPts[i];
97
+ const q = [w[0] + off.x, w[1] + off.y, w[2] + off.z];
98
+ m.set(__key(q), q);
99
+ }
100
+ return m;
101
+ };
102
+ const __snap = (map, p) => {
103
+ if (!map) return p;
104
+ const s = map.get(__key(p));
105
+ return s ? s : p;
106
+ };
107
+ if (posAttr) {
108
+ const idxAttr = baseGeom.getIndex && baseGeom.getIndex();
109
+ const v = new THREE.Vector3();
110
+ const world = new Array(posAttr.count);
111
+ for (let i = 0; i < posAttr.count; i++) {
112
+ v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(face.matrixWorld);
113
+ world[i] = [v.x, v.y, v.z];
114
+ }
115
+ __capWorld = world;
116
+ const emit = (i0, i1, i2) => {
117
+ const p0 = world[i0], p1 = world[i1], p2 = world[i2];
118
+ // Start cap
119
+ if (dirB) {
120
+ const b0 = [p0[0] + dirB.x, p0[1] + dirB.y, p0[2] + dirB.z];
121
+ const b1 = [p1[0] + dirB.x, p1[1] + dirB.y, p1[2] + dirB.z];
122
+ const b2 = [p2[0] + dirB.x, p2[1] + dirB.y, p2[2] + dirB.z];
123
+ this.addTriangle(startName, b0, b2, b1);
124
+ } else {
125
+ this.addTriangle(startName, p0, p2, p1);
126
+ }
127
+ // End cap
128
+ const q0 = [p0[0] + dirF.x, p0[1] + dirF.y, p0[2] + dirF.z];
129
+ const q1 = [p1[0] + dirF.x, p1[1] + dirF.y, p1[2] + dirF.z];
130
+ const q2 = [p2[0] + dirF.x, p2[1] + dirF.y, p2[2] + dirF.z];
131
+ this.addTriangle(endName, q0, q1, q2);
132
+ };
133
+ if (idxAttr) {
134
+ for (let t = 0; t < idxAttr.count; t += 3) emit(idxAttr.getX(t + 0) >>> 0, idxAttr.getX(t + 1) >>> 0, idxAttr.getX(t + 2) >>> 0);
135
+ } else {
136
+ const triCount = (posAttr.count / 3) | 0;
137
+ for (let t = 0; t < triCount; t++) emit(3 * t + 0, 3 * t + 1, 3 * t + 2);
138
+ }
139
+ }
140
+ // Build snap maps once caps are authored
141
+ if (__capWorld) {
142
+ __snapStart = __makeSnapMap(__capWorld, dirB ? dirB : new THREE.Vector3(0,0,0));
143
+ __snapEnd = __makeSnapMap(__capWorld, dirF);
144
+ }
145
+
146
+ // Prefer explicit boundary loops (outer + holes) to guarantee identical
147
+ // vertices with caps, but emit one side face per original sketch edge.
148
+ // Fallback to per-edge polylines if loops are unavailable.
149
+ const sideSegments = [];
150
+ const loops = face?.userData?.boundaryLoopsWorld;
151
+ const nearEq = (a, b, eps = 1e-6) => Math.abs(a - b) <= eps;
152
+ const samePt = (P, Q) => P && Q && nearEq(P[0], Q[0]) && nearEq(P[1], Q[1]) && nearEq(P[2], Q[2]);
153
+ const edgeGroups = face?.userData?.boundaryEdgeGroups;
154
+ if (Array.isArray(edgeGroups) && edgeGroups.length) {
155
+ for (const g of edgeGroups) {
156
+ const pts = Array.isArray(g?.pts) ? g.pts : [];
157
+ if (pts.length < 2) continue;
158
+ const base = g?.name || 'EDGE';
159
+ const swBase = String(base).endsWith('_SW') ? String(base) : `${base}_SW`;
160
+ const nmRaw = `${featureTag}${swBase}`;
161
+ const nm = this.params.sideFaceName ? this.params.sideFaceName : nmRaw;
162
+ const poly = pts.map((p) => (Array.isArray(p) ? [p[0], p[1], p[2]] : [p.x, p.y, p.z]));
163
+ sideSegments.push({ name: nm, poly, isHole: !!g?.isHole });
164
+ }
165
+ } else {
166
+ // Precompute world polylines for edges to allow segment->edge matching
167
+ const edges = Array.isArray(face?.edges) ? face.edges : [];
168
+ const edgePolys = edges.map((e) => getEdgePolylineWorld(e));
169
+
170
+ // Find edge index whose polyline contains consecutive points A->B (or B->A)
171
+ const matchEdge = (A, B) => {
172
+ for (let i = 0; i < edgePolys.length; i++) {
173
+ const poly = edgePolys[i];
174
+ if (!Array.isArray(poly) || poly.length < 2) continue;
175
+ for (let k = 0; k < poly.length - 1; k++) {
176
+ const p0 = poly[k], p1 = poly[k + 1];
177
+ if ((samePt(p0, A) && samePt(p1, B)) || (samePt(p0, B) && samePt(p1, A))) {
178
+ const e = edges[i];
179
+ const nm = `${featureTag}${e?.name || 'EDGE'}_SW`;
180
+ return { name: nm, isHole: !!(e && e.userData && e.userData.isHole) };
181
+ }
182
+ }
183
+ }
184
+ return null;
185
+ };
186
+
187
+ if (Array.isArray(loops) && loops.length) {
188
+ for (let li = 0; li < loops.length; li++) {
189
+ const l = loops[li];
190
+ if (!l || !Array.isArray(l.pts) || l.pts.length < 2) continue;
191
+ const pts = [];
192
+ for (const p of l.pts) { if (p && p.length >= 3) pts.push([p[0], p[1], p[2]]); }
193
+ // Dedup consecutive duplicates
194
+ for (let i = pts.length - 2; i >= 0; i--) { const a = pts[i], b = pts[i + 1]; if (samePt(a, b)) pts.splice(i + 1, 1); }
195
+ const M = pts.length;
196
+ if (M < 2) continue;
197
+
198
+ // Walk segments and group consecutive ones that map to the same edge name.
199
+ let currentName = null;
200
+ let currentPoly = [];
201
+ const flush = () => {
202
+ if (currentPoly.length >= 2) {
203
+ const nm = currentName || `${featureTag}${face.name || 'Face'}_${li}_SEG${sideSegments.length}_SW`;
204
+ sideSegments.push({ name: this.params.sideFaceName || nm, poly: currentPoly.slice(), isHole: !!l.isHole });
205
+ }
206
+ currentPoly = [];
207
+ };
208
+
209
+ const processSegment = (A, B) => {
210
+ const match = matchEdge(A, B);
211
+ const nm = match ? match.name : null;
212
+ const edgeHole = match ? match.isHole : !!l.isHole;
213
+ if (!currentName) {
214
+ currentName = nm;
215
+ currentPoly = [A, B];
216
+ } else if (nm === currentName) {
217
+ currentPoly.push(B);
218
+ } else {
219
+ flush();
220
+ currentName = nm;
221
+ currentPoly = [A, B];
222
+ }
223
+ // propagate hole flag to current group
224
+ if (sideSegments.length && currentPoly.length && edgeHole) {
225
+ sideSegments[sideSegments.length - 1].isHole = edgeHole;
226
+ }
227
+ };
228
+
229
+ for (let i = 0; i < M; i++) {
230
+ const A = pts[i];
231
+ const B = pts[(i + 1) % M];
232
+ processSegment(A, B);
233
+ }
234
+ flush();
235
+ }
236
+ } else {
237
+ for (let i = 0; i < edges.length; i++) {
238
+ const edge = edges[i];
239
+ const poly = edgePolys[i];
240
+ const nmRaw = `${featureTag}${edge?.name || 'EDGE'}_SW`;
241
+ const nm = this.params.sideFaceName ? this.params.sideFaceName : nmRaw;
242
+ sideSegments.push({ name: nm, poly, isHole: !!(edge && edge.userData && edge.userData.isHole) });
243
+ }
244
+ }
245
+ }
246
+
247
+ // One ribbon per segment/polyline
248
+ for (const { name, poly, isHole } of sideSegments) {
249
+ // Pre-count triangles already authored for this sidewall label so we
250
+ // can verify how many were added for this edge (should be 2 per segment).
251
+ const sideID = this._getOrCreateID(name);
252
+ const triCountByID = (id) => {
253
+ let c = 0; const ids = this._triIDs;
254
+ for (let i = 0; i < ids.length; i++) if (ids[i] === id) c++;
255
+ return c;
256
+ };
257
+ const beforeTris = triCountByID(sideID);
258
+ if (!Array.isArray(poly) || poly.length < 2) continue;
259
+ // Do NOT drop the final point even if it duplicates the start. We only
260
+ // generate quads between consecutive samples and never auto-close the
261
+ // polyline (no last->first segment). Keeping the duplicate ensures the
262
+ // last actual segment (second-to-last -> last) is emitted for closed
263
+ // edge polylines that repeat the first point at the end.
264
+ const n = poly.length;
265
+ const expectedSegments = Math.max(0, n - 1);
266
+ if (dirB) {
267
+ for (let i = 0; i < n - 1; i++) {
268
+ const a = poly[i];
269
+ const b = poly[i + 1];
270
+ let A0 = [a[0] + dirB.x, a[1] + dirB.y, a[2] + dirB.z];
271
+ let B0 = [b[0] + dirB.x, b[1] + dirB.y, b[2] + dirB.z];
272
+ let A1 = [a[0] + dirF.x, a[1] + dirF.y, a[2] + dirF.z];
273
+ let B1 = [b[0] + dirF.x, b[1] + dirF.y, b[2] + dirF.z];
274
+ A0 = __snap(__snapStart, A0); B0 = __snap(__snapStart, B0);
275
+ A1 = __snap(__snapEnd, A1); B1 = __snap(__snapEnd, B1);
276
+ addQuad(name, A0, B0, B1, A1, isHole);
277
+ }
278
+ // Closing handled upstream by emitting an explicit last->first segment when loops are available.
279
+ } else {
280
+ for (let i = 0; i < n - 1; i++) {
281
+ const a = poly[i];
282
+ const b = poly[i + 1];
283
+ let A0 = [a[0], a[1], a[2]];
284
+ let B0 = [b[0], b[1], b[2]];
285
+ let A1 = [a[0] + dirF.x, a[1] + dirF.y, a[2] + dirF.z];
286
+ let B1 = [b[0] + dirF.x, b[1] + dirF.y, b[2] + dirF.z];
287
+ A0 = __snap(__snapStart, A0); B0 = __snap(__snapStart, B0);
288
+ A1 = __snap(__snapEnd, A1); B1 = __snap(__snapEnd, B1);
289
+ addQuad(name, A0, B0, B1, A1, isHole);
290
+ }
291
+ // Closing handled upstream by emitting an explicit last->first segment when loops are available.
292
+ }
293
+
294
+ // Post-check: count how many triangles were added for this sidewall.
295
+ const afterTris = triCountByID(sideID);
296
+ const added = afterTris - beforeTris;
297
+ const expected = expectedSegments * 2;
298
+ if (added !== expected) {
299
+ const msg = `Extrude sidewall triangle mismatch for ${name}: segments=${expectedSegments}, triangles=${added} (expected ${expected})`;
300
+ try {
301
+ if (typeof window !== 'undefined' && typeof window.alert === 'function') window.alert(msg);
302
+ } catch {}
303
+ try { console.warn(msg); } catch {}
304
+ }
305
+ }
306
+
307
+ // Check for cylindrical face metadata from circular edges
308
+ try {
309
+ // Look for edges that might be circular/arc and add metadata
310
+ if (face && Array.isArray(face.edges)) {
311
+ for (const edge of face.edges) {
312
+ if (edge && edge.userData) {
313
+ const geomType = edge.userData.sketchGeomType;
314
+ let radius = null;
315
+ let center = null;
316
+
317
+ // Check if it's a circle
318
+ if (geomType === 'circle' &&
319
+ Array.isArray(edge.userData.circleCenter) &&
320
+ typeof edge.userData.circleRadius === 'number') {
321
+ radius = edge.userData.circleRadius;
322
+ center = edge.userData.circleCenter;
323
+ }
324
+ // Check if it's an arc (any arc creates cylindrical surface when extruded)
325
+ else if (geomType === 'arc' &&
326
+ Array.isArray(edge.userData.arcCenter) &&
327
+ typeof edge.userData.arcRadius === 'number') {
328
+ radius = edge.userData.arcRadius;
329
+ center = edge.userData.arcCenter;
330
+ }
331
+
332
+ // If we found a circular edge, add cylindrical face metadata
333
+ if (radius !== null && center !== null) {
334
+ const sidewallName = `${featureTag}${edge?.name || 'EDGE'}_SW`;
335
+
336
+ // Bring the sketch-space center into the solid's coordinate system
337
+ const transformMatrix = edge?.matrixWorld || face?.matrixWorld || null;
338
+ const centerVec = new THREE.Vector3(center[0], center[1], center[2]);
339
+ if (transformMatrix) centerVec.applyMatrix4(transformMatrix);
340
+
341
+ // Forward/back vectors describing the sweep in world space
342
+ const forwardVec = dirF ? dirF.clone() : new THREE.Vector3(0, 1, 0);
343
+ const backwardVec = dirB ? dirB.clone() : new THREE.Vector3(0, 0, 0);
344
+
345
+ // Axis endpoints (start/end of the cylindrical wall)
346
+ const startPoint = centerVec.clone().add(backwardVec);
347
+ const endPoint = centerVec.clone().add(forwardVec);
348
+ const axisVec = endPoint.clone().sub(startPoint);
349
+
350
+ let height = axisVec.length();
351
+ if (!Number.isFinite(height) || height <= 1e-9) {
352
+ height = forwardVec.length() + backwardVec.length();
353
+ }
354
+
355
+ let axisDir = axisVec.clone();
356
+ if (axisDir.lengthSq() > 1e-12) {
357
+ axisDir.normalize();
358
+ } else {
359
+ axisDir = forwardVec.clone();
360
+ if (axisDir.lengthSq() > 1e-12) axisDir.normalize();
361
+ else axisDir.set(0, 1, 0);
362
+ }
363
+
364
+ const axisCenter = startPoint.clone().addScaledVector(axisVec, 0.5);
365
+ if (!Number.isFinite(axisCenter.x) || !Number.isFinite(axisCenter.y) || !Number.isFinite(axisCenter.z)) {
366
+ axisCenter.copy(centerVec);
367
+ }
368
+
369
+ if (!Number.isFinite(axisDir.x) || !Number.isFinite(axisDir.y) || !Number.isFinite(axisDir.z)) {
370
+ axisDir.set(0, 1, 0);
371
+ }
372
+
373
+ this.setFaceMetadata(sidewallName, {
374
+ type: 'cylindrical',
375
+ radius: radius,
376
+ height: Number.isFinite(height) ? height : 0,
377
+ axis: [axisDir.x, axisDir.y, axisDir.z],
378
+ center: [axisCenter.x, axisCenter.y, axisCenter.z]
379
+ });
380
+ }
381
+ }
382
+ }
383
+ }
384
+ } catch (err) {
385
+ // Silently continue if metadata detection fails
386
+ }
387
+
388
+ // Adaptive weld epsilon so caps and sides share vertices exactly.
389
+ let eps = 1e-5;
390
+ if (this._vertProperties.length >= 6) {
391
+ const bounds = computeBoundsFromVertices(this._vertProperties);
392
+ const diag = (bounds && bounds.diag) ? bounds.diag : 1;
393
+ eps = Math.min(1e-4, Math.max(1e-7, diag * 1e-6));
394
+ }
395
+ this.setEpsilon(eps);
396
+ try { this.removeSmallIslands({ maxTriangles: 12, removeInternal: true, removeExternal: true }); } catch {}
397
+ return this;
398
+ }
399
+ }
400
+
401
+ export function extrudeFace({ face, distance = 1, dir = null, distanceBack = 0, name = 'Extrude' } = {}) {
402
+ return new ExtrudeSolid({ face, distance, dir, distanceBack, name });
403
+ }
@@ -0,0 +1,187 @@
1
+ import * as THREE from "three";
2
+ import { CADmaterials } from "../UI/CADmaterials.js";
3
+ import { computeTriangleArea } from "./triangleUtils.js";
4
+
5
+ export class Face extends THREE.Mesh {
6
+ constructor(geometry) {
7
+ super(geometry, CADmaterials.FACE.BASE);
8
+ this.edges = [];
9
+ this.name = null;
10
+ this.type = 'FACE';
11
+ this.renderOrder = 1;
12
+ this.parentSolid = null;
13
+ }
14
+
15
+ // Compute the average geometric normal of this face's triangles in world space.
16
+ // Weighted by triangle area via cross product magnitude.
17
+ getAverageNormal() {
18
+ const geom = this.geometry;
19
+ if (!geom) return new THREE.Vector3(0, 1, 0);
20
+ const pos = geom.getAttribute('position');
21
+ if (!pos || pos.itemSize !== 3 || pos.count < 3) return new THREE.Vector3(0, 1, 0);
22
+
23
+ const idx = geom.getIndex();
24
+ const a = new THREE.Vector3();
25
+ const b = new THREE.Vector3();
26
+ const c = new THREE.Vector3();
27
+ const ab = new THREE.Vector3();
28
+ const ac = new THREE.Vector3();
29
+ const accum = new THREE.Vector3();
30
+
31
+ const toWorld = (out, i) => {
32
+ out.set(pos.getX(i), pos.getY(i), pos.getZ(i)).applyMatrix4(this.matrixWorld);
33
+ return out;
34
+ };
35
+
36
+ if (idx) {
37
+ const triCount = (idx.count / 3) | 0;
38
+ for (let t = 0; t < triCount; t++) {
39
+ const i0 = idx.getX(3 * t + 0) >>> 0;
40
+ const i1 = idx.getX(3 * t + 1) >>> 0;
41
+ const i2 = idx.getX(3 * t + 2) >>> 0;
42
+ toWorld(a, i0); toWorld(b, i1); toWorld(c, i2);
43
+ ab.subVectors(b, a);
44
+ ac.subVectors(c, a);
45
+ accum.add(ac.cross(ab));
46
+ }
47
+ } else {
48
+ const triCount = (pos.count / 3) | 0;
49
+ for (let t = 0; t < triCount; t++) {
50
+ const i0 = 3 * t + 0;
51
+ const i1 = 3 * t + 1;
52
+ const i2 = 3 * t + 2;
53
+ toWorld(a, i0); toWorld(b, i1); toWorld(c, i2);
54
+ ab.subVectors(b, a);
55
+ ac.subVectors(c, a);
56
+ accum.add(ac.cross(ab));
57
+ }
58
+ }
59
+
60
+ if (accum.lengthSq() === 0) return new THREE.Vector3(0, 1, 0);
61
+ return accum.normalize();
62
+ }
63
+
64
+ // Sum triangle areas in world space
65
+ surfaceArea() {
66
+ const geom = this.geometry;
67
+ if (!geom) return 0;
68
+ const pos = geom.getAttribute && geom.getAttribute('position');
69
+ if (!pos || pos.itemSize !== 3) return 0;
70
+
71
+ const idx = geom.getIndex && geom.getIndex();
72
+ const a = new THREE.Vector3();
73
+ const b = new THREE.Vector3();
74
+ const c = new THREE.Vector3();
75
+ let area = 0;
76
+
77
+ const toWorld = (out, i) => out.set(pos.getX(i), pos.getY(i), pos.getZ(i)).applyMatrix4(this.matrixWorld);
78
+
79
+ if (idx) {
80
+ const triCount = (idx.count / 3) | 0;
81
+ for (let t = 0; t < triCount; t++) {
82
+ const i0 = idx.getX(3 * t + 0) >>> 0;
83
+ const i1 = idx.getX(3 * t + 1) >>> 0;
84
+ const i2 = idx.getX(3 * t + 2) >>> 0;
85
+ toWorld(a, i0); toWorld(b, i1); toWorld(c, i2);
86
+ area += computeTriangleArea(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z);
87
+ }
88
+ } else {
89
+ const triCount = (pos.count / 3) | 0;
90
+ for (let t = 0; t < triCount; t++) {
91
+ const i0 = 3 * t + 0;
92
+ const i1 = 3 * t + 1;
93
+ const i2 = 3 * t + 2;
94
+ toWorld(a, i0); toWorld(b, i1); toWorld(c, i2);
95
+ area += computeTriangleArea(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z);
96
+ }
97
+ }
98
+ return area;
99
+ }
100
+
101
+ async points() {
102
+ // return an array of point objects {x,y,z} in world space
103
+ const tmp = new THREE.Vector3();
104
+ const arr = [];
105
+ const pos = this.geometry && this.geometry.getAttribute && this.geometry.getAttribute('position');
106
+ if (pos && pos.itemSize === 3 && pos.count >= 2) {
107
+ for (let i = 0; i < pos.count; i++) {
108
+ tmp.set(pos.getX(i), pos.getY(i), pos.getZ(i));
109
+ if (applyWorld) tmp.applyMatrix4(this.matrixWorld);
110
+ arr.push({ x: tmp.x, y: tmp.y, z: tmp.z });
111
+ }
112
+ }
113
+ return arr;
114
+ }
115
+
116
+ setMetadata(metadata) {
117
+ // call the approriate method in the parent solid
118
+ if (this.parentSolid && typeof this.parentSolid.setFaceMetadata === 'function') {
119
+ this.parentSolid.setFaceMetadata(this.name, metadata);
120
+ }
121
+ return this;
122
+ }
123
+
124
+ getMetadata() {
125
+ // call the approriate method in the parent solid
126
+ if (this.parentSolid && typeof this.parentSolid.getFaceMetadata === 'function') {
127
+ return this.parentSolid.getFaceMetadata(this.name);
128
+ }
129
+ return null;
130
+ }
131
+
132
+ renameFace(newName) {
133
+ this.parentSolid.renameFace(this.name, newName);
134
+ }
135
+
136
+ /**
137
+ * Return neighboring face objects that share an edge with this face.
138
+ * Prefers the face's edges (populated after visualize); if unavailable, falls back to
139
+ * boundary polylines on the parent solid and resolves to face objects.
140
+ * @returns {Face[]} array of neighbor face objects (deduped, excluding this face)
141
+ */
142
+ getNeighbors() {
143
+ const self = this;
144
+ const name = self?.name || self?.userData?.faceName || null;
145
+ if (!name) return [];
146
+ const solid = self.parentSolid || self.userData?.parentSolid || null;
147
+ const neighbors = new Set();
148
+
149
+ const addFace = (f) => {
150
+ if (!f) return;
151
+ if (f === self) return;
152
+ neighbors.add(f);
153
+ };
154
+
155
+ // Primary: use edges already attached to this face
156
+ if (Array.isArray(self.edges)) {
157
+ for (const e of self.edges) {
158
+ if (!e || !Array.isArray(e.faces)) continue;
159
+ for (const f of e.faces) addFace(f);
160
+ }
161
+ }
162
+
163
+ // Fallback: use boundary polylines from the parent solid to resolve neighbor faces
164
+ if (neighbors.size === 0 && solid && typeof solid.getBoundaryEdgePolylines === 'function') {
165
+ const faceMap = new Map();
166
+ if (Array.isArray(solid.children)) {
167
+ for (const ch of solid.children) {
168
+ if (ch && ch.type === 'FACE') {
169
+ const n = ch.name || ch.userData?.faceName || null;
170
+ if (n) faceMap.set(n, ch);
171
+ }
172
+ }
173
+ }
174
+ try {
175
+ const boundaries = solid.getBoundaryEdgePolylines() || [];
176
+ for (const poly of boundaries) {
177
+ const a = poly?.faceA;
178
+ const b = poly?.faceB;
179
+ if (a === name && b) addFace(faceMap.get(b));
180
+ else if (b === name && a) addFace(faceMap.get(a));
181
+ }
182
+ } catch { /* ignore */ }
183
+ }
184
+
185
+ return Array.from(neighbors);
186
+ }
187
+ }