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,945 @@
1
+ import * as THREE from 'three';
2
+ import { ArcballControls } from 'three/examples/jsm/controls/ArcballControls.js';
3
+ import { FloatingWindow } from './FloatingWindow.js';
4
+
5
+ const DEFAULT_BG = 0x0b0d10;
6
+
7
+ function ensureStyles() {
8
+ if (document.getElementById('triangle-debugger-styles')) return;
9
+ const style = document.createElement('style');
10
+ style.id = 'triangle-debugger-styles';
11
+ style.textContent = `
12
+ .tri-debugger {
13
+ height: 100%;
14
+ width: 100%;
15
+ display: grid;
16
+ grid-template-columns: 320px 1fr;
17
+ gap: 10px;
18
+ padding: 6px;
19
+ box-sizing: border-box;
20
+ background: #0b0d10;
21
+ color: #e5e7eb;
22
+ font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
23
+ }
24
+ .tri-debugger__sidebar {
25
+ background: #0f141a;
26
+ border: 1px solid #1e2430;
27
+ border-radius: 12px;
28
+ padding: 10px;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: 8px;
32
+ box-shadow: 0 10px 24px rgba(0,0,0,0.22);
33
+ min-height: 0;
34
+ }
35
+ .tri-debugger__solid {
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 4px;
39
+ }
40
+ .tri-debugger__solid-name {
41
+ font-weight: 700;
42
+ letter-spacing: 0.2px;
43
+ color: #e5e7eb;
44
+ }
45
+ .tri-debugger__solid-meta {
46
+ color: #9aa4b2;
47
+ font-size: 11px;
48
+ }
49
+ .tri-debugger__search {
50
+ width: 100%;
51
+ box-sizing: border-box;
52
+ background: #0b0f14;
53
+ border: 1px solid #1e2430;
54
+ color: #e5e7eb;
55
+ border-radius: 8px;
56
+ padding: 6px 8px;
57
+ }
58
+ .tri-debugger__list {
59
+ flex: 1 1 auto;
60
+ overflow: auto;
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 6px;
64
+ min-height: 0;
65
+ }
66
+ .tri-debugger__row {
67
+ width: 100%;
68
+ border: 1px solid #1e2430;
69
+ background: #121821;
70
+ color: #e5e7eb;
71
+ border-radius: 10px;
72
+ padding: 8px 10px;
73
+ text-align: left;
74
+ cursor: pointer;
75
+ transition: border-color .12s ease, background .12s ease, transform .08s ease;
76
+ }
77
+ .tri-debugger__row:hover { border-color: #7aa2f7; background: #162033; transform: translateY(-1px); }
78
+ .tri-debugger__row.is-selected { border-color: #7aa2f7; background: rgba(122,162,247,0.12); box-shadow: 0 4px 14px rgba(0,0,0,0.24); }
79
+ .tri-debugger__row-title { font-weight: 700; display: flex; gap: 6px; align-items: center; }
80
+ .tri-debugger__row-face { color: #9aa4b2; font-weight: 600; }
81
+ .tri-debugger__row-meta { color: #9aa4b2; font-size: 11px; margin-top: 2px; }
82
+ .tri-debugger__empty { color: #9aa4b2; font-style: italic; padding: 6px 2px; }
83
+ .tri-debugger__filters {
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 8px;
87
+ }
88
+ .tri-debugger__toggle {
89
+ display: inline-flex;
90
+ align-items: center;
91
+ gap: 6px;
92
+ color: #9aa4b2;
93
+ font-size: 11px;
94
+ user-select: none;
95
+ }
96
+ .tri-debugger__toggle input {
97
+ accent-color: #7aa2f7;
98
+ }
99
+
100
+ .tri-debugger__main { display: flex; flex-direction: column; gap: 10px; min-height: 0; }
101
+ .tri-debugger__viewport {
102
+ position: relative;
103
+ flex: 1 1 auto;
104
+ background: #0b0d10;
105
+ border: 1px solid #1e2430;
106
+ border-radius: 12px;
107
+ overflow: hidden;
108
+ min-height: 280px;
109
+ box-shadow: 0 10px 32px rgba(0,0,0,0.28);
110
+ }
111
+ .tri-debugger__canvas-host {
112
+ position: absolute;
113
+ inset: 0;
114
+ }
115
+ .tri-debugger__status {
116
+ position: absolute;
117
+ top: 10px;
118
+ left: 12px;
119
+ padding: 6px 10px;
120
+ background: rgba(15,20,26,0.9);
121
+ border: 1px solid #1e2430;
122
+ border-radius: 8px;
123
+ color: #9aa4b2;
124
+ z-index: 2;
125
+ pointer-events: none;
126
+ }
127
+ .tri-debugger__info {
128
+ background: #0f141a;
129
+ border: 1px solid #1e2430;
130
+ border-radius: 12px;
131
+ padding: 10px;
132
+ display: grid;
133
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
134
+ gap: 10px 12px;
135
+ box-sizing: border-box;
136
+ }
137
+ .tri-debugger__info h4 {
138
+ margin: 0;
139
+ font-size: 12px;
140
+ color: #9aa4b2;
141
+ letter-spacing: 0.3px;
142
+ }
143
+ .tri-debugger__info .value {
144
+ font-weight: 700;
145
+ color: #e5e7eb;
146
+ margin-top: 2px;
147
+ word-break: break-word;
148
+ }
149
+ .tri-debugger__badges {
150
+ display: flex;
151
+ flex-wrap: wrap;
152
+ gap: 6px;
153
+ }
154
+ .tri-debugger__badge {
155
+ border-radius: 8px;
156
+ padding: 4px 6px;
157
+ background: #162033;
158
+ border: 1px solid #1e2430;
159
+ font-size: 11px;
160
+ color: #e5e7eb;
161
+ cursor: pointer;
162
+ }
163
+ .tri-debugger__badge.is-selected {
164
+ border-color: #7aa2f7;
165
+ background: rgba(122,162,247,0.12);
166
+ }
167
+ @media (max-width: 1100px) {
168
+ .tri-debugger { grid-template-columns: 280px 1fr; }
169
+ }
170
+ @media (max-width: 900px) {
171
+ .tri-debugger { grid-template-columns: 1fr; grid-template-rows: 240px 1fr; }
172
+ .tri-debugger__sidebar { min-height: 220px; }
173
+ }
174
+ `;
175
+ document.head.appendChild(style);
176
+ }
177
+
178
+ const round = (n) => {
179
+ const v = Number(n);
180
+ if (!Number.isFinite(v)) return 0;
181
+ if (Math.abs(v) < 1e-12) return 0;
182
+ return Number(v.toFixed(6));
183
+ };
184
+
185
+ export class TriangleDebuggerWindow {
186
+ constructor({ viewer } = {}) {
187
+ this.viewer = viewer || null;
188
+ this.window = null;
189
+ this.root = null;
190
+ this.content = null;
191
+ this.listEl = null;
192
+ this.infoEl = null;
193
+ this.canvasHost = null;
194
+ this.statusEl = null;
195
+ this.filterInput = null;
196
+ this.solidNameEl = null;
197
+ this.solidMetaEl = null;
198
+
199
+ this.renderer = null;
200
+ this.scene = null;
201
+ this.camera = null;
202
+ this.controls = null;
203
+
204
+ this.baseMesh = null;
205
+ this.edgeLines = null;
206
+ this.highlightMesh = null;
207
+ this.adjacentMesh = null;
208
+
209
+ this.triangles = [];
210
+ this._listButtons = new Map();
211
+ this._filterText = '';
212
+ this._filterHighValence = false;
213
+ this._selectedIndex = null;
214
+ this._resizeObserver = null;
215
+ this._raf = null;
216
+ this._currentTarget = null;
217
+ this._orthoSize = 4;
218
+ this._raycaster = new THREE.Raycaster();
219
+ this._pointer = new THREE.Vector2();
220
+ this._onCanvasPointerDown = (ev) => this._pickTriangle(ev);
221
+ this._onWindowResize = () => this._onResize();
222
+ this._onInfoClick = (ev) => this._handleInfoClick(ev);
223
+ this._selectedEdgeKey = null;
224
+ }
225
+
226
+ isOpen() {
227
+ return !!(this.root && this.root.style.display !== 'none');
228
+ }
229
+
230
+ close() {
231
+ if (this.root) this.root.style.display = 'none';
232
+ this._stopRenderLoop();
233
+ }
234
+
235
+ openFor(target) {
236
+ this._currentTarget = target || null;
237
+ this._ensureWindow();
238
+ this._startRenderLoop();
239
+ if (this.root) this.root.style.display = 'flex';
240
+ this._bringToFront();
241
+ const solid = this._extractSolid(target);
242
+ if (!solid) {
243
+ this._setStatus('Select a Solid to debug.');
244
+ this._clearGeometry();
245
+ return;
246
+ }
247
+ this._setStatus('');
248
+ this._loadSolid(solid);
249
+ }
250
+
251
+ refreshTarget(target) {
252
+ this._currentTarget = target || null;
253
+ if (!this.isOpen()) return;
254
+ this.openFor(target);
255
+ }
256
+
257
+ _bringToFront() {
258
+ try { this.window?.bringToFront?.(); } catch { }
259
+ }
260
+
261
+ _ensureWindow() {
262
+ if (this.root) return;
263
+ ensureStyles();
264
+ const fw = new FloatingWindow({
265
+ title: 'Triangle Debugger',
266
+ width: 1120,
267
+ height: 740,
268
+ right: 12,
269
+ top: 40,
270
+ shaded: false,
271
+ onClose: () => this.close(),
272
+ });
273
+
274
+ const btnFit = document.createElement('button');
275
+ btnFit.className = 'fw-btn';
276
+ btnFit.textContent = 'Fit view';
277
+ btnFit.addEventListener('click', () => this._fitCamera());
278
+ fw.addHeaderAction(btnFit);
279
+
280
+ const btnClear = document.createElement('button');
281
+ btnClear.className = 'fw-btn';
282
+ btnClear.textContent = 'Clear';
283
+ btnClear.addEventListener('click', () => this._clearGeometry());
284
+ fw.addHeaderAction(btnClear);
285
+
286
+ const content = document.createElement('div');
287
+ content.className = 'tri-debugger';
288
+ content.style.width = '100%';
289
+ content.style.height = '100%';
290
+ fw.content.appendChild(content);
291
+
292
+ const sidebar = document.createElement('div');
293
+ sidebar.className = 'tri-debugger__sidebar';
294
+ const solidBox = document.createElement('div');
295
+ solidBox.className = 'tri-debugger__solid';
296
+ this.solidNameEl = document.createElement('div');
297
+ this.solidNameEl.className = 'tri-debugger__solid-name';
298
+ this.solidNameEl.textContent = 'No solid selected';
299
+ this.solidMetaEl = document.createElement('div');
300
+ this.solidMetaEl.className = 'tri-debugger__solid-meta';
301
+ this.solidMetaEl.textContent = '-';
302
+ solidBox.append(this.solidNameEl, this.solidMetaEl);
303
+
304
+ const filterRow = document.createElement('div');
305
+ filterRow.className = 'tri-debugger__filters';
306
+ this.filterInput = document.createElement('input');
307
+ this.filterInput.className = 'tri-debugger__search';
308
+ this.filterInput.placeholder = 'Filter by face or triangle #';
309
+ this.filterInput.addEventListener('input', () => {
310
+ this._filterText = (this.filterInput.value || '').trim().toLowerCase();
311
+ this._populateList();
312
+ });
313
+ const toggleWrap = document.createElement('label');
314
+ toggleWrap.className = 'tri-debugger__toggle';
315
+ const toggle = document.createElement('input');
316
+ toggle.type = 'checkbox';
317
+ toggle.addEventListener('change', () => {
318
+ this._filterHighValence = !!toggle.checked;
319
+ this._populateList();
320
+ });
321
+ const toggleText = document.createElement('span');
322
+ toggleText.textContent = 'Only edges with >2 adjacents';
323
+ toggleWrap.append(toggle, toggleText);
324
+ filterRow.append(this.filterInput, toggleWrap);
325
+
326
+ this.listEl = document.createElement('div');
327
+ this.listEl.className = 'tri-debugger__list';
328
+
329
+ sidebar.append(solidBox, filterRow, this.listEl);
330
+
331
+ const main = document.createElement('div');
332
+ main.className = 'tri-debugger__main';
333
+
334
+ const viewport = document.createElement('div');
335
+ viewport.className = 'tri-debugger__viewport';
336
+ this.statusEl = document.createElement('div');
337
+ this.statusEl.className = 'tri-debugger__status';
338
+ this.statusEl.textContent = 'Select a Solid to debug.';
339
+
340
+ this.canvasHost = document.createElement('div');
341
+ this.canvasHost.className = 'tri-debugger__canvas-host';
342
+ viewport.append(this.canvasHost, this.statusEl);
343
+
344
+ this.infoEl = document.createElement('div');
345
+ this.infoEl.className = 'tri-debugger__info';
346
+ this.infoEl.innerHTML = '<div class="tri-debugger__empty">Select a triangle to see details.</div>';
347
+ this.infoEl.addEventListener('click', this._onInfoClick);
348
+
349
+ main.append(viewport, this.infoEl);
350
+ content.append(sidebar, main);
351
+
352
+ this.window = fw;
353
+ this.root = fw.root;
354
+ this.content = content;
355
+
356
+ this._initThree();
357
+ try { window.addEventListener('resize', this._onWindowResize, { passive: true }); } catch { }
358
+ this._startRenderLoop();
359
+ }
360
+
361
+ _initThree() {
362
+ this.scene = new THREE.Scene();
363
+ this.scene.background = new THREE.Color(DEFAULT_BG);
364
+ const ambient = new THREE.AmbientLight(0xffffff, 0.6);
365
+ const dir1 = new THREE.DirectionalLight(0xffffff, 0.75);
366
+ dir1.position.set(3, 4, 3);
367
+ const dir2 = new THREE.DirectionalLight(0xffffff, 0.45);
368
+ dir2.position.set(-3, -2, 2);
369
+ this.scene.add(ambient, dir1, dir2);
370
+
371
+ this.camera = new THREE.OrthographicCamera(-4, 4, 4, -4, 0.001, 10000);
372
+ this.camera.position.set(6, 5, 6);
373
+
374
+ this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
375
+ this.renderer.setPixelRatio(window.devicePixelRatio || 1);
376
+ this.renderer.setSize(100, 100, false);
377
+ this.renderer.setClearColor(new THREE.Color(DEFAULT_BG), 1);
378
+ if (this.canvasHost) this.canvasHost.appendChild(this.renderer.domElement);
379
+ this.renderer.domElement.addEventListener('pointerdown', this._onCanvasPointerDown, { capture: true });
380
+
381
+ this.controls = new ArcballControls(this.camera, this.renderer.domElement, this.scene);
382
+ try { this.controls.setGizmosVisible(false); } catch { }
383
+ this.controls.addEventListener('change', () => this._renderOnce());
384
+
385
+ if (window.ResizeObserver && this.canvasHost) {
386
+ this._resizeObserver = new ResizeObserver(() => this._onResize());
387
+ this._resizeObserver.observe(this.canvasHost);
388
+ }
389
+ this._onResize();
390
+ }
391
+
392
+ _onResize() {
393
+ if (!this.canvasHost || !this.renderer || !this.camera) return;
394
+ const rect = this.canvasHost.getBoundingClientRect();
395
+ const width = Math.max(50, rect.width || 0);
396
+ const height = Math.max(50, rect.height || 0);
397
+ this.renderer.setSize(width, height, false);
398
+ this._applyOrthoFrustum(width / height);
399
+ this._renderOnce();
400
+ }
401
+
402
+ _startRenderLoop() {
403
+ if (this._raf) return;
404
+ const loop = () => {
405
+ this._raf = window.requestAnimationFrame(loop);
406
+ if (this.controls) this.controls.update();
407
+ this._renderOnce();
408
+ };
409
+ this._raf = window.requestAnimationFrame(loop);
410
+ }
411
+
412
+ _stopRenderLoop() {
413
+ if (this._raf) window.cancelAnimationFrame(this._raf);
414
+ this._raf = null;
415
+ }
416
+
417
+ _renderOnce() {
418
+ if (!this.renderer || !this.scene || !this.camera) return;
419
+ try { this.renderer.render(this.scene, this.camera); } catch { }
420
+ }
421
+
422
+ _applyOrthoFrustum(aspect = 1) {
423
+ if (!this.camera || !(this.camera instanceof THREE.OrthographicCamera)) return;
424
+ const size = Math.max(0.001, this._orthoSize || 4);
425
+ const a = Math.max(0.001, aspect || 1);
426
+ this.camera.left = -size * a;
427
+ this.camera.right = size * a;
428
+ this.camera.top = size;
429
+ this.camera.bottom = -size;
430
+ this.camera.updateProjectionMatrix();
431
+ }
432
+
433
+ _pickTriangle(ev) {
434
+ if (!this.baseMesh || !this.renderer || !this.camera) return;
435
+ const rect = this.renderer.domElement.getBoundingClientRect();
436
+ const x = ((ev.clientX - rect.left) / rect.width) * 2 - 1;
437
+ const y = -((ev.clientY - rect.top) / rect.height) * 2 + 1;
438
+ this._pointer.set(x, y);
439
+ this._raycaster.setFromCamera(this._pointer, this.camera);
440
+ const hits = this._raycaster.intersectObject(this.baseMesh, false);
441
+ if (!hits.length) return;
442
+ const idx = hits[0]?.faceIndex;
443
+ if (!Number.isFinite(idx)) return;
444
+ const triIdx = Math.max(0, Math.min(this.triangles.length - 1, idx | 0));
445
+ if (!this.triangles[triIdx]) return;
446
+ ev.preventDefault();
447
+ ev.stopPropagation();
448
+ this._selectTriangle(triIdx);
449
+ }
450
+
451
+ _setStatus(msg) {
452
+ if (this.statusEl) this.statusEl.textContent = msg || '';
453
+ }
454
+
455
+ _extractSolid(target) {
456
+ if (!target) return null;
457
+ const isSolid = (obj) => obj && (String(obj.type || '').toUpperCase() === 'SOLID');
458
+ if (isSolid(target)) return target;
459
+ if (target.parentSolid && isSolid(target.parentSolid)) return target.parentSolid;
460
+ if (target.userData && target.userData.parentSolid && isSolid(target.userData.parentSolid)) return target.userData.parentSolid;
461
+ let cur = target.parent || null;
462
+ while (cur) {
463
+ if (isSolid(cur)) return cur;
464
+ if (cur.parentSolid && isSolid(cur.parentSolid)) return cur.parentSolid;
465
+ cur = cur.parent || null;
466
+ }
467
+ return null;
468
+ }
469
+
470
+ _clearGeometry(showPlaceholder = true) {
471
+ if (this.baseMesh) {
472
+ try { this.scene.remove(this.baseMesh); } catch { }
473
+ try { this.baseMesh.geometry?.dispose(); } catch { }
474
+ try {
475
+ const mat = this.baseMesh.material;
476
+ if (Array.isArray(mat)) mat.forEach(m => m?.dispose && m.dispose());
477
+ else if (mat && typeof mat.dispose === 'function') mat.dispose();
478
+ } catch { }
479
+ }
480
+ if (this.edgeLines) {
481
+ try { this.scene.remove(this.edgeLines); } catch { }
482
+ try { this.edgeLines.geometry?.dispose(); } catch { }
483
+ try { this.edgeLines.material?.dispose?.(); } catch { }
484
+ }
485
+ this.baseMesh = null;
486
+ this.edgeLines = null;
487
+ this.triangles = [];
488
+ this._listButtons.clear();
489
+ this._selectedIndex = null;
490
+ this._selectedEdgeKey = null;
491
+ if (this.listEl) {
492
+ this.listEl.innerHTML = '';
493
+ if (showPlaceholder) {
494
+ const empty = document.createElement('div');
495
+ empty.className = 'tri-debugger__empty';
496
+ empty.textContent = 'No triangles loaded.';
497
+ this.listEl.appendChild(empty);
498
+ }
499
+ }
500
+ if (this.infoEl && showPlaceholder) {
501
+ this.infoEl.innerHTML = '<div class="tri-debugger__empty">Select a triangle to see details.</div>';
502
+ }
503
+ if (this.highlightMesh) this.highlightMesh.visible = false;
504
+ if (this.adjacentMesh) this.adjacentMesh.visible = false;
505
+ this._renderOnce();
506
+ }
507
+
508
+ _loadSolid(solid) {
509
+ if (!solid || typeof solid.getMesh !== 'function') {
510
+ this._setStatus('Selected item is not a Solid.');
511
+ this._clearGeometry();
512
+ return;
513
+ }
514
+ this._filterText = '';
515
+ if (this.filterInput) this.filterInput.value = '';
516
+ this._clearGeometry(false);
517
+ let mesh = null;
518
+ try {
519
+ mesh = solid.getMesh();
520
+ const vp = mesh?.vertProperties || [];
521
+ const tv = mesh?.triVerts || [];
522
+ const faceIDs = (mesh?.faceID && mesh.faceID.length === (tv.length / 3)) ? mesh.faceID : null;
523
+ const triCount = (tv.length / 3) | 0;
524
+ const fallbackIDs = (!faceIDs && Array.isArray(solid._triIDs) && solid._triIDs.length === triCount) ? solid._triIDs : null;
525
+ if (!triCount) {
526
+ this._setStatus('Solid has no triangles.');
527
+ this._clearGeometry();
528
+ return;
529
+ }
530
+
531
+ const idToFace = new Map();
532
+ try { if (solid._idToFaceName && solid._idToFaceName.forEach) solid._idToFaceName.forEach((name, id) => idToFace.set(id, name)); } catch { }
533
+ const faceNameFor = (id, idx) => {
534
+ if (idToFace.has(id)) return idToFace.get(id);
535
+ if (id !== undefined && id !== null) return `Face ${id}`;
536
+ return `Face ${idx}`;
537
+ };
538
+
539
+ const positions = new Float32Array(triCount * 9);
540
+ const triangles = new Array(triCount);
541
+ const edgeToTris = new Map();
542
+ const posEdgeToTris = new Map();
543
+ let pw = 0;
544
+
545
+ const edgeKeyFromPoints = (a, b) => {
546
+ const pa = [round(a[0]), round(a[1]), round(a[2])];
547
+ const pb = [round(b[0]), round(b[1]), round(b[2])];
548
+ const sa = pa.join(',');
549
+ const sb = pb.join(',');
550
+ return sa < sb ? `${sa}|${sb}` : `${sb}|${sa}`;
551
+ };
552
+
553
+ for (let t = 0; t < triCount; t++) {
554
+ const base = t * 3;
555
+ const i0 = tv[base + 0] | 0;
556
+ const i1 = tv[base + 1] | 0;
557
+ const i2 = tv[base + 2] | 0;
558
+ const p0 = [vp[i0 * 3 + 0], vp[i0 * 3 + 1], vp[i0 * 3 + 2]];
559
+ const p1 = [vp[i1 * 3 + 0], vp[i1 * 3 + 1], vp[i1 * 3 + 2]];
560
+ const p2 = [vp[i2 * 3 + 0], vp[i2 * 3 + 1], vp[i2 * 3 + 2]];
561
+ const ux = p1[0] - p0[0], uy = p1[1] - p0[1], uz = p1[2] - p0[2];
562
+ const vx = p2[0] - p0[0], vy = p2[1] - p0[1], vz = p2[2] - p0[2];
563
+ const nx = uy * vz - uz * vy;
564
+ const ny = uz * vx - ux * vz;
565
+ const nz = ux * vy - uy * vx;
566
+ const nlen = Math.hypot(nx, ny, nz) || 1;
567
+ const area = 0.5 * nlen;
568
+ const normal = [round(nx / nlen), round(ny / nlen), round(nz / nlen)];
569
+ const centroid = [round((p0[0] + p1[0] + p2[0]) / 3), round((p0[1] + p1[1] + p2[1]) / 3), round((p0[2] + p1[2] + p2[2]) / 3)];
570
+ const fid = faceIDs ? faceIDs[t] : (fallbackIDs ? fallbackIDs[t] : undefined);
571
+ const faceName = faceNameFor(fid, t);
572
+
573
+ triangles[t] = {
574
+ index: t,
575
+ faceName,
576
+ indices: [i0, i1, i2],
577
+ p1: p0, p2: p1, p3: p2,
578
+ normal,
579
+ area: round(area),
580
+ centroid,
581
+ adjacent: new Set(),
582
+ hasCrowdedEdge: false,
583
+ edgeAdjacencies: [],
584
+ _edgeDefs: [],
585
+ };
586
+
587
+ positions[pw++] = p0[0]; positions[pw++] = p0[1]; positions[pw++] = p0[2];
588
+ positions[pw++] = p1[0]; positions[pw++] = p1[1]; positions[pw++] = p1[2];
589
+ positions[pw++] = p2[0]; positions[pw++] = p2[1]; positions[pw++] = p2[2];
590
+
591
+ const edges = [
592
+ { verts: [i0, i1], pts: [p0, p1] },
593
+ { verts: [i1, i2], pts: [p1, p2] },
594
+ { verts: [i2, i0], pts: [p2, p0] },
595
+ ];
596
+ for (const edge of edges) {
597
+ const [a0, b0] = edge.verts;
598
+ const a = Math.min(a0, b0);
599
+ const b = Math.max(a0, b0);
600
+ const key = `${a}|${b}`;
601
+ const posKey = edgeKeyFromPoints(edge.pts[0], edge.pts[1]);
602
+ let arr = edgeToTris.get(key);
603
+ if (!arr) { arr = []; edgeToTris.set(key, arr); }
604
+ arr.push(t);
605
+ let arrPos = posEdgeToTris.get(posKey);
606
+ if (!arrPos) { arrPos = []; posEdgeToTris.set(posKey, arrPos); }
607
+ arrPos.push(t);
608
+ triangles[t]._edgeDefs.push({ verts: [a0, b0], keyIndex: key, keyPos: posKey });
609
+ }
610
+ }
611
+
612
+ const addAdjacencyFromMap = (map) => {
613
+ for (const [, arr] of map.entries()) {
614
+ if (!arr || arr.length < 2) continue;
615
+ const isCrowded = arr.length > 2;
616
+ for (let i = 0; i < arr.length; i++) {
617
+ const ti = arr[i];
618
+ const tri = triangles[ti];
619
+ if (!tri) continue;
620
+ for (let j = 0; j < arr.length; j++) {
621
+ if (i === j) continue;
622
+ tri.adjacent.add(arr[j]);
623
+ }
624
+ if (isCrowded) tri.hasCrowdedEdge = true;
625
+ }
626
+ }
627
+ };
628
+
629
+ addAdjacencyFromMap(edgeToTris);
630
+ addAdjacencyFromMap(posEdgeToTris);
631
+
632
+ // Build per-triangle edge adjacency detail (triangles that share each edge, index or position keyed)
633
+ for (const tri of triangles) {
634
+ const detailMap = new Map();
635
+ for (const def of tri._edgeDefs) {
636
+ const { verts, keyIndex, keyPos } = def;
637
+ const addEntry = (key, neighborList) => {
638
+ if (!neighborList || neighborList.length < 2) return;
639
+ let entry = detailMap.get(key);
640
+ if (!entry) {
641
+ entry = { key, verts, neighbors: new Set(), crowded: neighborList.length > 2 };
642
+ detailMap.set(key, entry);
643
+ }
644
+ for (const n of neighborList) {
645
+ if (Number.isInteger(n) && n !== tri.index) entry.neighbors.add(n);
646
+ }
647
+ if (neighborList.length > 2) entry.crowded = true;
648
+ };
649
+ addEntry(keyIndex, edgeToTris.get(keyIndex));
650
+ addEntry(keyPos, posEdgeToTris.get(keyPos));
651
+ }
652
+ tri.edgeAdjacencies = Array.from(detailMap.values()).map(e => ({
653
+ key: e.key,
654
+ verts: e.verts,
655
+ neighbors: Array.from(e.neighbors),
656
+ crowded: !!e.crowded,
657
+ }));
658
+ tri._edgeDefs = null;
659
+ }
660
+ for (const tri of triangles) tri.adjacent = Array.from(tri.adjacent);
661
+
662
+ const geom = new THREE.BufferGeometry();
663
+ geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
664
+ geom.computeVertexNormals();
665
+ geom.computeBoundingBox();
666
+ geom.computeBoundingSphere();
667
+
668
+ const mat = new THREE.MeshBasicMaterial({
669
+ color: 0x2a3545,
670
+ wireframe: true,
671
+ transparent: true,
672
+ opacity: 0.65,
673
+ side: THREE.DoubleSide,
674
+ depthWrite: false,
675
+ });
676
+ this.baseMesh = new THREE.Mesh(geom, mat);
677
+ this.baseMesh.renderOrder = 1;
678
+ this.scene.add(this.baseMesh);
679
+
680
+ try {
681
+ const edgesGeom = new THREE.EdgesGeometry(geom, 15);
682
+ const edgesMat = new THREE.LineBasicMaterial({ color: 0x304050, transparent: true, opacity: 0.45 });
683
+ this.edgeLines = new THREE.LineSegments(edgesGeom, edgesMat);
684
+ this.edgeLines.renderOrder = 2;
685
+ this.scene.add(this.edgeLines);
686
+ } catch { }
687
+
688
+ if (!this.highlightMesh) {
689
+ const hGeom = new THREE.BufferGeometry();
690
+ hGeom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(9), 3));
691
+ this.highlightMesh = new THREE.Mesh(hGeom, new THREE.MeshBasicMaterial({
692
+ color: 0xffc857,
693
+ transparent: true,
694
+ opacity: 0.9,
695
+ side: THREE.DoubleSide,
696
+ depthTest: false,
697
+ depthWrite: false,
698
+ }));
699
+ this.highlightMesh.renderOrder = 3;
700
+ this.highlightMesh.visible = false;
701
+ this.scene.add(this.highlightMesh);
702
+ }
703
+ if (!this.adjacentMesh) {
704
+ this.adjacentMesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial({
705
+ color: 0x4cc9f0,
706
+ transparent: true,
707
+ opacity: 0.35,
708
+ side: THREE.DoubleSide,
709
+ depthTest: false,
710
+ depthWrite: false,
711
+ }));
712
+ this.adjacentMesh.renderOrder = 2.5;
713
+ this.adjacentMesh.visible = false;
714
+ this.scene.add(this.adjacentMesh);
715
+ }
716
+
717
+ this.triangles = triangles;
718
+ const faceCount = new Set(triangles.map(t => t.faceName)).size;
719
+ if (this.solidNameEl) this.solidNameEl.textContent = solid.name || 'Solid';
720
+ if (this.solidMetaEl) this.solidMetaEl.textContent = `${triCount} triangles | ${faceCount} faces`;
721
+
722
+ this._populateList();
723
+ this._fitCamera(geom);
724
+ if (triangles.length) this._selectTriangle(0);
725
+ this._renderOnce();
726
+ } catch (e) {
727
+ console.warn('[TriangleDebugger] Failed to load solid:', e);
728
+ this._setStatus('Failed to build debug view.');
729
+ this._clearGeometry();
730
+ } finally {
731
+ try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch { }
732
+ }
733
+ }
734
+
735
+ _populateList() {
736
+ if (!this.listEl) return;
737
+ this.listEl.innerHTML = '';
738
+ this._listButtons.clear();
739
+ const filter = this._filterText;
740
+ let rendered = 0;
741
+ const frag = document.createDocumentFragment();
742
+ for (const tri of this.triangles) {
743
+ const label = `#${tri.index} ${tri.faceName || ''}`.toLowerCase();
744
+ if (filter && !label.includes(filter)) continue;
745
+ if (this._filterHighValence && !tri.hasCrowdedEdge) continue;
746
+ rendered++;
747
+ const btn = document.createElement('button');
748
+ btn.className = 'tri-debugger__row';
749
+ btn.dataset.index = String(tri.index);
750
+ const adjText = `Adj ${tri.adjacent?.length ?? 0}${tri.hasCrowdedEdge ? ' • crowd' : ''}`;
751
+ btn.innerHTML = `<div class="tri-debugger__row-title">#${tri.index}<span class="tri-debugger__row-face">${tri.faceName || 'face'}</span></div>
752
+ <div class="tri-debugger__row-meta">Area ${tri.area ?? 0} | Normal (${tri.normal.join(', ')}) | ${adjText}</div>`;
753
+ btn.addEventListener('click', () => this._selectTriangle(tri.index));
754
+ frag.appendChild(btn);
755
+ this._listButtons.set(tri.index, btn);
756
+ }
757
+ if (rendered === 0) {
758
+ const empty = document.createElement('div');
759
+ empty.className = 'tri-debugger__empty';
760
+ empty.textContent = filter ? 'No triangles match this filter.' : 'No triangles.';
761
+ this.listEl.appendChild(empty);
762
+ } else {
763
+ this.listEl.appendChild(frag);
764
+ }
765
+ this._highlightListSelection();
766
+ }
767
+
768
+ _highlightListSelection() {
769
+ for (const [idx, btn] of this._listButtons.entries()) {
770
+ btn.classList.toggle('is-selected', idx === this._selectedIndex);
771
+ }
772
+ }
773
+
774
+ _selectTriangle(index) {
775
+ if (!this.triangles || !this.triangles.length) return;
776
+ const tri = this.triangles[index];
777
+ if (!tri) return;
778
+ this._selectedIndex = index;
779
+ this._selectedEdgeKey = null;
780
+ this._highlightListSelection();
781
+ this._updateHighlight(tri);
782
+ this._updateAdjacentHighlight(tri);
783
+ this._renderInfo(tri);
784
+ this._renderOnce();
785
+ // Scroll into view if needed
786
+ const btn = this._listButtons.get(index);
787
+ if (btn && typeof btn.scrollIntoView === 'function') {
788
+ try { btn.scrollIntoView({ block: 'nearest', behavior: 'auto' }); } catch { }
789
+ }
790
+ }
791
+
792
+ _updateHighlight(tri) {
793
+ if (!this.highlightMesh) return;
794
+ const g = this.highlightMesh.geometry;
795
+ const attr = g.getAttribute('position');
796
+ if (!attr || attr.count < 3) {
797
+ g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(9), 3));
798
+ }
799
+ const pos = g.getAttribute('position');
800
+ const pts = [tri.p1, tri.p2, tri.p3];
801
+ let w = 0;
802
+ for (const p of pts) {
803
+ pos.array[w++] = p[0]; pos.array[w++] = p[1]; pos.array[w++] = p[2];
804
+ }
805
+ pos.needsUpdate = true;
806
+ g.computeVertexNormals();
807
+ this.highlightMesh.visible = true;
808
+ }
809
+
810
+ _updateAdjacentHighlight(tri) {
811
+ if (!this.adjacentMesh) return;
812
+ const adj = (tri.adjacent || []).map(i => this.triangles[i]).filter(Boolean);
813
+ if (!adj.length) {
814
+ this.adjacentMesh.visible = false;
815
+ return;
816
+ }
817
+ const arr = new Float32Array(adj.length * 9);
818
+ let w = 0;
819
+ for (const t of adj) {
820
+ arr[w++] = t.p1[0]; arr[w++] = t.p1[1]; arr[w++] = t.p1[2];
821
+ arr[w++] = t.p2[0]; arr[w++] = t.p2[1]; arr[w++] = t.p2[2];
822
+ arr[w++] = t.p3[0]; arr[w++] = t.p3[1]; arr[w++] = t.p3[2];
823
+ }
824
+ const geom = new THREE.BufferGeometry();
825
+ geom.setAttribute('position', new THREE.BufferAttribute(arr, 3));
826
+ geom.computeVertexNormals();
827
+ if (this.adjacentMesh.geometry) {
828
+ try { this.adjacentMesh.geometry.dispose(); } catch { }
829
+ }
830
+ this.adjacentMesh.geometry = geom;
831
+ this.adjacentMesh.visible = true;
832
+ }
833
+
834
+ _renderInfo(tri) {
835
+ if (!this.infoEl || !tri) return;
836
+ const adjBadges = (tri.adjacent || []).map((idx) => {
837
+ const face = this.triangles[idx]?.faceName || 'face';
838
+ const sel = idx === this._selectedIndex;
839
+ return `<span class="tri-debugger__badge${sel ? ' is-selected' : ''}" data-adj-index="${idx}">#${idx} | ${face}</span>`;
840
+ }).join('') || '<span class="tri-debugger__badge">None</span>';
841
+ const edges = Array.isArray(tri.edgeAdjacencies) ? tri.edgeAdjacencies : [];
842
+ const edgeBadges = edges.map((e) => {
843
+ const sel = this._selectedEdgeKey === e.key;
844
+ const count = e.neighbors?.length ?? 0;
845
+ const tag = e.crowded ? ' • crowd' : '';
846
+ return `<span class="tri-debugger__badge${sel ? ' is-selected' : ''}" data-edge-key="${e.key}">${e.verts[0]}-${e.verts[1]} | ${count} adj${tag}</span>`;
847
+ }).join('') || '<span class="tri-debugger__badge">None</span>';
848
+ const selEdge = edges.find(e => e.key === this._selectedEdgeKey) || null;
849
+ const edgeNeighbors = selEdge ? selEdge.neighbors || [] : [];
850
+ const edgeNeighborBadges = edgeNeighbors.length
851
+ ? edgeNeighbors.map(idx => {
852
+ const face = this.triangles[idx]?.faceName || 'face';
853
+ return `<span class="tri-debugger__badge" data-adj-index="${idx}">#${idx} | ${face}</span>`;
854
+ }).join('')
855
+ : '<span class="tri-debugger__badge">Select an edge</span>';
856
+ this.infoEl.innerHTML = `
857
+ <div>
858
+ <h4>Triangle</h4>
859
+ <div class="value">#${tri.index}</div>
860
+ </div>
861
+ <div>
862
+ <h4>Face</h4>
863
+ <div class="value">${tri.faceName || '-'}</div>
864
+ </div>
865
+ <div>
866
+ <h4>Indices</h4>
867
+ <div class="value">${tri.indices.join(', ')}</div>
868
+ </div>
869
+ <div>
870
+ <h4>Area</h4>
871
+ <div class="value">${tri.area}</div>
872
+ </div>
873
+ <div>
874
+ <h4>Normal</h4>
875
+ <div class="value">(${tri.normal.join(', ')})</div>
876
+ </div>
877
+ <div>
878
+ <h4>Centroid</h4>
879
+ <div class="value">(${tri.centroid.join(', ')})</div>
880
+ </div>
881
+ <div style="grid-column: 1 / -1;">
882
+ <h4>Adjacent triangles</h4>
883
+ <div class="tri-debugger__badges">${adjBadges}</div>
884
+ </div>
885
+ <div>
886
+ <h4>Edges</h4>
887
+ <div class="tri-debugger__badges">${edgeBadges}</div>
888
+ </div>
889
+ <div>
890
+ <h4>Edge neighbors</h4>
891
+ <div class="tri-debugger__badges">${edgeNeighborBadges}</div>
892
+ </div>
893
+ `;
894
+ }
895
+
896
+ _fitCamera(geom = null) {
897
+ if (!this.camera || !this.controls) return;
898
+ const g = geom || this.baseMesh?.geometry;
899
+ if (!g) return;
900
+ try { if (!g.boundingSphere) g.computeBoundingSphere(); } catch { }
901
+ const sphere = g.boundingSphere;
902
+ if (!sphere) return;
903
+ const { center, radius } = sphere;
904
+ const safeRadius = Math.max(0.001, radius);
905
+ const aspect = (() => {
906
+ const rect = this.renderer?.domElement?.getBoundingClientRect();
907
+ return rect && rect.height > 0 ? (rect.width / rect.height) : 1;
908
+ })();
909
+ const size = safeRadius * 1.6;
910
+ this._orthoSize = Math.max(size, 0.001);
911
+ this._applyOrthoFrustum(aspect);
912
+
913
+ const dir = new THREE.Vector3(1, 0.8, 1).normalize();
914
+ const dist = safeRadius * 4;
915
+ const pos = dir.multiplyScalar(dist).add(center);
916
+ this.camera.position.copy(pos);
917
+ this.controls.target.copy(center);
918
+ this.camera.near = Math.max(0.001, dist - safeRadius * 6);
919
+ this.camera.far = dist + safeRadius * 6;
920
+ this.camera.updateProjectionMatrix();
921
+ this.controls.update();
922
+ this._renderOnce();
923
+ }
924
+
925
+ _handleInfoClick(ev) {
926
+ const target = ev.target;
927
+ if (!target) return;
928
+ const edge = target.closest && target.closest('[data-edge-key]');
929
+ if (edge) {
930
+ const key = edge.dataset.edgeKey || null;
931
+ this._selectedEdgeKey = key;
932
+ const tri = this.triangles[this._selectedIndex];
933
+ if (tri) this._renderInfo(tri);
934
+ return;
935
+ }
936
+ const badge = target.closest && target.closest('[data-adj-index]');
937
+ if (!badge) return;
938
+ const idx = Number(badge.dataset.adjIndex);
939
+ if (!Number.isFinite(idx)) return;
940
+ if (!this.triangles[idx]) return;
941
+ ev.preventDefault();
942
+ ev.stopPropagation();
943
+ this._selectTriangle(idx);
944
+ }
945
+ }