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.
- package/LICENSE.md +32 -0
- package/README.md +144 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/dist-kernel/help/CONTRIBUTING.html +248 -0
- package/dist-kernel/help/LICENSE.html +248 -0
- package/dist-kernel/help/MODELING.png +0 -0
- package/dist-kernel/help/PMI.png +0 -0
- package/dist-kernel/help/SKETCH.png +0 -0
- package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints__angle-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__coincident-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__distance-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__fixed-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__parallel-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__solver.html +248 -0
- package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +248 -0
- package/dist-kernel/help/brep-api.html +263 -0
- package/dist-kernel/help/brep-kernel.html +258 -0
- package/dist-kernel/help/brep-model.html +248 -0
- package/dist-kernel/help/cylindrical-face-radius-embedding.html +290 -0
- package/dist-kernel/help/dialog-screenshots.html +248 -0
- package/dist-kernel/help/extruded-sketch-radius-embedding.html +336 -0
- package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
- package/dist-kernel/help/features__Boolean_dialog.png +0 -0
- package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
- package/dist-kernel/help/features__Datium_dialog.png +0 -0
- package/dist-kernel/help/features__Extrude_dialog.png +0 -0
- package/dist-kernel/help/features__Fillet_dialog.png +0 -0
- package/dist-kernel/help/features__Helix_dialog.png +0 -0
- package/dist-kernel/help/features__Hole_dialog.png +0 -0
- package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
- package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
- package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
- package/dist-kernel/help/features__Loft_dialog.png +0 -0
- package/dist-kernel/help/features__Mirror_dialog.png +0 -0
- package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
- package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_dialog.png +0 -0
- package/dist-kernel/help/features__Plane_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
- package/dist-kernel/help/features__Remesh_dialog.png +0 -0
- package/dist-kernel/help/features__Revolve_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
- package/dist-kernel/help/features__Sketch_dialog.png +0 -0
- package/dist-kernel/help/features__Spline_dialog.png +0 -0
- package/dist-kernel/help/features__Sweep_dialog.png +0 -0
- package/dist-kernel/help/features__Transform_dialog.png +0 -0
- package/dist-kernel/help/features__Tube_dialog.png +0 -0
- package/dist-kernel/help/features__assembly-component.html +248 -0
- package/dist-kernel/help/features__boolean.html +248 -0
- package/dist-kernel/help/features__chamfer.html +248 -0
- package/dist-kernel/help/features__datium.html +248 -0
- package/dist-kernel/help/features__datum.html +248 -0
- package/dist-kernel/help/features__extrude.html +248 -0
- package/dist-kernel/help/features__fillet.html +248 -0
- package/dist-kernel/help/features__helix.html +248 -0
- package/dist-kernel/help/features__hole.html +248 -0
- package/dist-kernel/help/features__image-heightmap-solid.html +248 -0
- package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face.html +248 -0
- package/dist-kernel/help/features__import-3d-model.html +248 -0
- package/dist-kernel/help/features__index.html +248 -0
- package/dist-kernel/help/features__loft.html +248 -0
- package/dist-kernel/help/features__mirror.html +248 -0
- package/dist-kernel/help/features__offset-shell.html +248 -0
- package/dist-kernel/help/features__pattern-linear.html +248 -0
- package/dist-kernel/help/features__pattern-radial.html +248 -0
- package/dist-kernel/help/features__pattern.html +248 -0
- package/dist-kernel/help/features__plane.html +248 -0
- package/dist-kernel/help/features__primitive-cone.html +248 -0
- package/dist-kernel/help/features__primitive-cube.html +248 -0
- package/dist-kernel/help/features__primitive-cylinder.html +248 -0
- package/dist-kernel/help/features__primitive-pyramid.html +248 -0
- package/dist-kernel/help/features__primitive-sphere.html +248 -0
- package/dist-kernel/help/features__primitive-torus.html +248 -0
- package/dist-kernel/help/features__remesh.html +248 -0
- package/dist-kernel/help/features__revolve.html +248 -0
- package/dist-kernel/help/features__sheet-metal-contour-flange.html +248 -0
- package/dist-kernel/help/features__sheet-metal-flange.html +248 -0
- package/dist-kernel/help/features__sheet-metal-tab.html +248 -0
- package/dist-kernel/help/features__sketch.html +248 -0
- package/dist-kernel/help/features__spline.html +248 -0
- package/dist-kernel/help/features__sweep.html +248 -0
- package/dist-kernel/help/features__transform.html +248 -0
- package/dist-kernel/help/features__tube.html +248 -0
- package/dist-kernel/help/file-formats.html +248 -0
- package/dist-kernel/help/getting-started.html +248 -0
- package/dist-kernel/help/highlights.html +248 -0
- package/dist-kernel/help/history-systems.html +248 -0
- package/dist-kernel/help/how-it-works.html +248 -0
- package/dist-kernel/help/index.html +862 -0
- package/dist-kernel/help/input-params-schema.html +363 -0
- package/dist-kernel/help/inspector-improvements.html +248 -0
- package/dist-kernel/help/inspector.html +248 -0
- package/dist-kernel/help/modes__modeling.html +248 -0
- package/dist-kernel/help/modes__pmi.html +248 -0
- package/dist-kernel/help/modes__sketch.html +248 -0
- package/dist-kernel/help/plugins.html +248 -0
- package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__angle-dimension.html +248 -0
- package/dist-kernel/help/pmi-annotations__explode-body.html +248 -0
- package/dist-kernel/help/pmi-annotations__hole-callout.html +248 -0
- package/dist-kernel/help/pmi-annotations__index.html +248 -0
- package/dist-kernel/help/pmi-annotations__leader.html +248 -0
- package/dist-kernel/help/pmi-annotations__linear-dimension.html +248 -0
- package/dist-kernel/help/pmi-annotations__note.html +248 -0
- package/dist-kernel/help/pmi-annotations__radial-dimension.html +248 -0
- package/dist-kernel/help/search-index.json +464 -0
- package/dist-kernel/help/simplified-radial-dimensions.html +298 -0
- package/dist-kernel/help/solid-methods.html +359 -0
- package/dist-kernel/help/table-of-contents.html +330 -0
- package/dist-kernel/help/ui-overview.html +248 -0
- package/dist-kernel/help/whats-new.html +248 -0
- package/package.json +54 -0
- package/src/BREP/AssemblyComponent.js +42 -0
- package/src/BREP/BREP.js +43 -0
- package/src/BREP/BetterSolid.js +805 -0
- package/src/BREP/Edge.js +103 -0
- package/src/BREP/Extrude.js +403 -0
- package/src/BREP/Face.js +187 -0
- package/src/BREP/MeshRepairer.js +634 -0
- package/src/BREP/OffsetShellSolid.js +614 -0
- package/src/BREP/PointCloudWrap.js +302 -0
- package/src/BREP/Revolve.js +345 -0
- package/src/BREP/SolidMethods/authoring.js +112 -0
- package/src/BREP/SolidMethods/booleanOps.js +230 -0
- package/src/BREP/SolidMethods/chamfer.js +122 -0
- package/src/BREP/SolidMethods/edgeResolution.js +25 -0
- package/src/BREP/SolidMethods/fillet.js +792 -0
- package/src/BREP/SolidMethods/index.js +72 -0
- package/src/BREP/SolidMethods/io.js +105 -0
- package/src/BREP/SolidMethods/lifecycle.js +103 -0
- package/src/BREP/SolidMethods/manifoldOps.js +375 -0
- package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
- package/src/BREP/SolidMethods/meshQueries.js +264 -0
- package/src/BREP/SolidMethods/metadata.js +106 -0
- package/src/BREP/SolidMethods/metrics.js +51 -0
- package/src/BREP/SolidMethods/transforms.js +361 -0
- package/src/BREP/SolidMethods/visualize.js +508 -0
- package/src/BREP/SolidShared.js +26 -0
- package/src/BREP/Sweep.js +1596 -0
- package/src/BREP/Tube.js +857 -0
- package/src/BREP/Vertex.js +43 -0
- package/src/BREP/applyBooleanOperation.js +704 -0
- package/src/BREP/boundsUtils.js +48 -0
- package/src/BREP/chamfer.js +551 -0
- package/src/BREP/edgePolylineUtils.js +85 -0
- package/src/BREP/fillets/common.js +388 -0
- package/src/BREP/fillets/fillet.js +1422 -0
- package/src/BREP/fillets/filletGeometry.js +15 -0
- package/src/BREP/fillets/inset.js +389 -0
- package/src/BREP/fillets/offsetHelper.js +143 -0
- package/src/BREP/fillets/outset.js +88 -0
- package/src/BREP/helix.js +193 -0
- package/src/BREP/meshToBrep.js +234 -0
- package/src/BREP/primitives.js +279 -0
- package/src/BREP/setupManifold.js +71 -0
- package/src/BREP/threadGeometry.js +1120 -0
- package/src/BREP/triangleUtils.js +8 -0
- package/src/BREP/triangulate.js +608 -0
- package/src/FeatureRegistry.js +183 -0
- package/src/PartHistory.js +1132 -0
- package/src/UI/AccordionWidget.js +292 -0
- package/src/UI/CADmaterials.js +850 -0
- package/src/UI/EnvMonacoEditor.js +522 -0
- package/src/UI/FloatingWindow.js +396 -0
- package/src/UI/HistoryWidget.js +457 -0
- package/src/UI/MainToolbar.js +131 -0
- package/src/UI/ModelLibraryView.js +194 -0
- package/src/UI/OrthoCameraIdle.js +206 -0
- package/src/UI/PluginsWidget.js +280 -0
- package/src/UI/SceneListing.js +606 -0
- package/src/UI/SelectionFilter.js +629 -0
- package/src/UI/ViewCube.js +389 -0
- package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
- package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
- package/src/UI/assembly/constraintFaceUtils.js +115 -0
- package/src/UI/assembly/constraintHighlightUtils.js +70 -0
- package/src/UI/assembly/constraintLabelUtils.js +31 -0
- package/src/UI/assembly/constraintPointUtils.js +64 -0
- package/src/UI/assembly/constraintSelectionUtils.js +185 -0
- package/src/UI/assembly/constraintStatusUtils.js +142 -0
- package/src/UI/componentSelectorModal.js +240 -0
- package/src/UI/controls/CombinedTransformControls.js +386 -0
- package/src/UI/dialogs.js +351 -0
- package/src/UI/expressionsManager.js +100 -0
- package/src/UI/featureDialogWidgets/booleanField.js +25 -0
- package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
- package/src/UI/featureDialogWidgets/buttonField.js +45 -0
- package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
- package/src/UI/featureDialogWidgets/defaultField.js +23 -0
- package/src/UI/featureDialogWidgets/fileField.js +66 -0
- package/src/UI/featureDialogWidgets/index.js +34 -0
- package/src/UI/featureDialogWidgets/numberField.js +165 -0
- package/src/UI/featureDialogWidgets/optionsField.js +33 -0
- package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
- package/src/UI/featureDialogWidgets/stringField.js +24 -0
- package/src/UI/featureDialogWidgets/textareaField.js +28 -0
- package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
- package/src/UI/featureDialogWidgets/transformField.js +252 -0
- package/src/UI/featureDialogWidgets/utils.js +43 -0
- package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
- package/src/UI/featureDialogs.js +1414 -0
- package/src/UI/fileManagerWidget.js +615 -0
- package/src/UI/history/HistoryCollectionWidget.js +1294 -0
- package/src/UI/history/historyCollectionWidget.css.js +257 -0
- package/src/UI/history/historyDisplayInfo.js +133 -0
- package/src/UI/mobile.js +28 -0
- package/src/UI/objectDump.js +442 -0
- package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
- package/src/UI/pmi/AnnotationHistory.js +353 -0
- package/src/UI/pmi/AnnotationRegistry.js +90 -0
- package/src/UI/pmi/BaseAnnotation.js +269 -0
- package/src/UI/pmi/LabelOverlay.css +102 -0
- package/src/UI/pmi/LabelOverlay.js +191 -0
- package/src/UI/pmi/PMIMode.js +1550 -0
- package/src/UI/pmi/PMIViewsWidget.js +1098 -0
- package/src/UI/pmi/annUtils.js +729 -0
- package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
- package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
- package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
- package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
- package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
- package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
- package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
- package/src/UI/pmi/pmiStyle.js +44 -0
- package/src/UI/sketcher/SketchMode3D.js +4095 -0
- package/src/UI/sketcher/dimensions.js +674 -0
- package/src/UI/sketcher/glyphs.js +236 -0
- package/src/UI/sketcher/highlights.js +60 -0
- package/src/UI/toolbarButtons/aboutButton.js +5 -0
- package/src/UI/toolbarButtons/exportButton.js +609 -0
- package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
- package/src/UI/toolbarButtons/importButton.js +160 -0
- package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
- package/src/UI/toolbarButtons/metadataButton.js +1063 -0
- package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
- package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
- package/src/UI/toolbarButtons/saveButton.js +99 -0
- package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
- package/src/UI/toolbarButtons/testsButton.js +26 -0
- package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
- package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
- package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
- package/src/UI/triangleDebuggerWindow.js +945 -0
- package/src/UI/viewer.js +4228 -0
- package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
- package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
- package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
- package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
- package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
- package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
- package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
- package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
- package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
- package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
- package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
- package/src/core/entities/HistoryCollectionBase.js +72 -0
- package/src/core/entities/ListEntityBase.js +109 -0
- package/src/core/entities/schemaProcesser.js +121 -0
- package/src/exporters/sheetMetalFlatPattern.js +659 -0
- package/src/exporters/sheetMetalUnfold.js +862 -0
- package/src/exporters/step.js +1135 -0
- package/src/exporters/threeMF.js +575 -0
- package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
- package/src/features/boolean/BooleanFeature.js +94 -0
- package/src/features/chamfer/ChamferFeature.js +116 -0
- package/src/features/datium/DatiumFeature.js +80 -0
- package/src/features/edgeFeatureUtils.js +41 -0
- package/src/features/extrude/ExtrudeFeature.js +143 -0
- package/src/features/fillet/FilletFeature.js +197 -0
- package/src/features/helix/HelixFeature.js +405 -0
- package/src/features/hole/HoleFeature.js +1050 -0
- package/src/features/hole/screwClearance.js +86 -0
- package/src/features/hole/threadDesignationCatalog.js +149 -0
- package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
- package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
- package/src/features/imageToFace/imageEditor.js +1270 -0
- package/src/features/imageToFace/traceUtils.js +971 -0
- package/src/features/import3dModel/Import3dModelFeature.js +151 -0
- package/src/features/loft/LoftFeature.js +605 -0
- package/src/features/mirror/MirrorFeature.js +151 -0
- package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
- package/src/features/offsetShell/OffsetShellFeature.js +89 -0
- package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
- package/src/features/pattern/PatternFeature.js +275 -0
- package/src/features/patternLinear/PatternLinearFeature.js +120 -0
- package/src/features/patternRadial/PatternRadialFeature.js +186 -0
- package/src/features/plane/PlaneFeature.js +154 -0
- package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
- package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
- package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
- package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
- package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
- package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
- package/src/features/remesh/RemeshFeature.js +97 -0
- package/src/features/revolve/RevolveFeature.js +111 -0
- package/src/features/selectionUtils.js +118 -0
- package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
- package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
- package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
- package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
- package/src/features/sheetMetal/SheetMetalObject.js +141 -0
- package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
- package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
- package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
- package/src/features/sheetMetal/profileUtils.js +25 -0
- package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
- package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
- package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
- package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
- package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
- package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
- package/src/features/sheetMetal/sheetMetalTree.js +210 -0
- package/src/features/sketch/SketchFeature.js +955 -0
- package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
- package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
- package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
- package/src/features/spline/SplineEditorSession.js +988 -0
- package/src/features/spline/SplineFeature.js +1388 -0
- package/src/features/spline/splineUtils.js +218 -0
- package/src/features/sweep/SweepFeature.js +110 -0
- package/src/features/transform/TransformFeature.js +152 -0
- package/src/features/tube/TubeFeature.js +635 -0
- package/src/fs.proxy.js +625 -0
- package/src/idbStorage.js +254 -0
- package/src/index.js +12 -0
- package/src/main.js +15 -0
- package/src/metadataManager.js +64 -0
- package/src/path.proxy.js +277 -0
- package/src/plugins/ghLoader.worker.js +151 -0
- package/src/plugins/pluginManager.js +286 -0
- package/src/pmi/PMIViewsManager.js +134 -0
- package/src/services/componentLibrary.js +198 -0
- package/src/tests/ConsoleCapture.js +189 -0
- package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
- package/src/tests/browserTests.js +597 -0
- package/src/tests/debugBoolean.js +225 -0
- package/src/tests/partFiles/badBoolean.json +957 -0
- package/src/tests/partFiles/extrudeTest.json +88 -0
- package/src/tests/partFiles/filletFail.json +58 -0
- package/src/tests/partFiles/import_TEst.part.part.json +646 -0
- package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
- package/src/tests/test_boolean_subtract.js +27 -0
- package/src/tests/test_chamfer.js +17 -0
- package/src/tests/test_extrudeFace.js +24 -0
- package/src/tests/test_fillet.js +17 -0
- package/src/tests/test_fillet_nonClosed.js +45 -0
- package/src/tests/test_filletsMoreDifficult.js +46 -0
- package/src/tests/test_history_features_basic.js +149 -0
- package/src/tests/test_hole.js +282 -0
- package/src/tests/test_mirror.js +16 -0
- package/src/tests/test_offsetShellGrouping.js +85 -0
- package/src/tests/test_plane.js +4 -0
- package/src/tests/test_primitiveCone.js +11 -0
- package/src/tests/test_primitiveCube.js +7 -0
- package/src/tests/test_primitiveCylinder.js +8 -0
- package/src/tests/test_primitivePyramid.js +9 -0
- package/src/tests/test_primitiveSphere.js +17 -0
- package/src/tests/test_primitiveTorus.js +21 -0
- package/src/tests/test_pushFace.js +126 -0
- package/src/tests/test_sheetMetalContourFlange.js +125 -0
- package/src/tests/test_sheetMetal_features.js +80 -0
- package/src/tests/test_sketch_openLoop.js +45 -0
- package/src/tests/test_solidMetrics.js +58 -0
- package/src/tests/test_stlLoader.js +1889 -0
- package/src/tests/test_sweepFace.js +55 -0
- package/src/tests/test_tube.js +45 -0
- package/src/tests/test_tube_closedLoop.js +67 -0
- package/src/tests/tests.js +493 -0
- package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
- package/src/tools/dialogCapturePageFactory.js +227 -0
- package/src/tools/featureDialogCapturePage.js +47 -0
- package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
- package/src/utils/axisHelpers.js +99 -0
- package/src/utils/deepClone.js +69 -0
- package/src/utils/geometryTolerance.js +37 -0
- package/src/utils/normalizeTypeString.js +8 -0
- package/src/utils/xformMath.js +51 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
// Solid.fillet implementation: consolidates fillet logic so features call this API.
|
|
2
|
+
// Usage: solid.fillet({ radius, edgeNames, featureID, direction, inflate, resolution, debug, showTangentOverlays, combineEdges })
|
|
3
|
+
import { Manifold } from '../SolidShared.js';
|
|
4
|
+
import { resolveEdgesFromInputs } from './edgeResolution.js';
|
|
5
|
+
import { computeFaceAreaFromTriangles } from '../fillets/filletGeometry.js';
|
|
6
|
+
import { createQuantizer, deriveTolerance } from '../../utils/geometryTolerance.js';
|
|
7
|
+
|
|
8
|
+
const debugMode = false;
|
|
9
|
+
|
|
10
|
+
// Threshold for collapsing tiny end caps into the round face.
|
|
11
|
+
const END_CAP_AREA_RATIO_THRESHOLD = 0.05;
|
|
12
|
+
|
|
13
|
+
function computeFaceAreaByName(solid, faceName) {
|
|
14
|
+
if (!solid || typeof solid.getFace !== 'function' || !faceName) return 0;
|
|
15
|
+
try {
|
|
16
|
+
const tris = solid.getFace(faceName);
|
|
17
|
+
return computeFaceAreaFromTriangles(tris);
|
|
18
|
+
} catch {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildFaceAreaCache(solid) {
|
|
24
|
+
const cache = new Map();
|
|
25
|
+
return {
|
|
26
|
+
get(name) {
|
|
27
|
+
if (!name) return 0;
|
|
28
|
+
if (cache.has(name)) return cache.get(name);
|
|
29
|
+
const area = computeFaceAreaByName(solid, name);
|
|
30
|
+
cache.set(name, area);
|
|
31
|
+
return area;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function findNeighborRoundFace(resultSolid, capName, areaCache, boundaryCache) {
|
|
37
|
+
if (!resultSolid || !capName) return null;
|
|
38
|
+
const boundaries = boundaryCache.current || resultSolid.getBoundaryEdgePolylines() || [];
|
|
39
|
+
boundaryCache.current = boundaries;
|
|
40
|
+
let best = null;
|
|
41
|
+
let bestArea = 0;
|
|
42
|
+
for (const poly of boundaries) {
|
|
43
|
+
const a = poly?.faceA;
|
|
44
|
+
const b = poly?.faceB;
|
|
45
|
+
if (a !== capName && b !== capName) continue;
|
|
46
|
+
const other = (a === capName) ? b : a;
|
|
47
|
+
if (!other || typeof other !== 'string') continue;
|
|
48
|
+
if (!other.includes('TUBE_Outer')) continue;
|
|
49
|
+
const aVal = areaCache.get(other);
|
|
50
|
+
if (aVal > bestArea) {
|
|
51
|
+
bestArea = aVal;
|
|
52
|
+
best = other;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return best;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function findLargestRoundFace(resultSolid, areaCache) {
|
|
59
|
+
if (!resultSolid || typeof resultSolid.getFaceNames !== 'function') return null;
|
|
60
|
+
let best = null;
|
|
61
|
+
let bestArea = 0;
|
|
62
|
+
for (const name of resultSolid.getFaceNames()) {
|
|
63
|
+
if (typeof name !== 'string' || !name.includes('TUBE_Outer')) continue;
|
|
64
|
+
const a = areaCache.get(name);
|
|
65
|
+
if (a > bestArea) {
|
|
66
|
+
bestArea = a;
|
|
67
|
+
best = name;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return best;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getFilletMergeCandidateNames(filletSolid) {
|
|
74
|
+
if (!filletSolid || typeof filletSolid.getFaceNames !== 'function') return [];
|
|
75
|
+
const names = filletSolid.getFaceNames();
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const n of names) {
|
|
78
|
+
if (typeof n !== 'string') continue;
|
|
79
|
+
const meta = (typeof filletSolid.getFaceMetadata === 'function') ? filletSolid.getFaceMetadata(n) : {};
|
|
80
|
+
if (meta && (meta.filletRoundFace || meta.filletSourceArea || meta.filletEndCap)) {
|
|
81
|
+
out.push(n);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (n.includes('_END_CAP') || n.includes('_CapStart') || n.includes('_CapEnd') || n.includes('_WEDGE_A') || n.includes('_WEDGE_B')) {
|
|
85
|
+
out.push(n);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function guessRoundFaceName(filletSolid, filletName) {
|
|
92
|
+
const faces = (filletSolid && typeof filletSolid.getFaceNames === 'function')
|
|
93
|
+
? filletSolid.getFaceNames()
|
|
94
|
+
: [];
|
|
95
|
+
const explicitOuter = faces.find(n => typeof n === 'string' && n.includes('_TUBE_Outer'));
|
|
96
|
+
if (explicitOuter) return explicitOuter;
|
|
97
|
+
if (filletName) {
|
|
98
|
+
const guess = `${filletName}_TUBE_Outer`;
|
|
99
|
+
if (faces.includes(guess)) return guess;
|
|
100
|
+
return guess;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function mergeFaceIntoTarget(resultSolid, sourceFaceName, targetFaceName) {
|
|
106
|
+
if (!resultSolid || !sourceFaceName || !targetFaceName) return false;
|
|
107
|
+
const faceToId = resultSolid._faceNameToID instanceof Map ? resultSolid._faceNameToID : new Map();
|
|
108
|
+
const idToFace = resultSolid._idToFaceName instanceof Map ? resultSolid._idToFaceName : new Map();
|
|
109
|
+
const sourceID = faceToId.get(sourceFaceName);
|
|
110
|
+
if (sourceID === undefined) return false;
|
|
111
|
+
const targetID = faceToId.get(targetFaceName);
|
|
112
|
+
|
|
113
|
+
// If target doesn't exist yet, just relabel the source.
|
|
114
|
+
if (targetID === undefined) {
|
|
115
|
+
idToFace.set(sourceID, targetFaceName);
|
|
116
|
+
faceToId.delete(sourceFaceName);
|
|
117
|
+
faceToId.set(targetFaceName, sourceID);
|
|
118
|
+
if (resultSolid._faceMetadata instanceof Map) {
|
|
119
|
+
const meta = resultSolid._faceMetadata;
|
|
120
|
+
if (!meta.has(targetFaceName) && meta.has(sourceFaceName)) {
|
|
121
|
+
meta.set(targetFaceName, meta.get(sourceFaceName));
|
|
122
|
+
}
|
|
123
|
+
meta.delete(sourceFaceName);
|
|
124
|
+
}
|
|
125
|
+
resultSolid._idToFaceName = idToFace;
|
|
126
|
+
resultSolid._faceNameToID = faceToId;
|
|
127
|
+
resultSolid._faceIndex = null;
|
|
128
|
+
resultSolid._dirty = true;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (targetID === sourceID) return false;
|
|
133
|
+
|
|
134
|
+
const triIDs = Array.isArray(resultSolid._triIDs) ? resultSolid._triIDs : null;
|
|
135
|
+
let replaced = 0;
|
|
136
|
+
if (triIDs) {
|
|
137
|
+
for (let i = 0; i < triIDs.length; i++) {
|
|
138
|
+
if ((triIDs[i] >>> 0) === sourceID) {
|
|
139
|
+
triIDs[i] = targetID;
|
|
140
|
+
replaced++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
resultSolid._triIDs = triIDs;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
idToFace.delete(sourceID);
|
|
147
|
+
faceToId.delete(sourceFaceName);
|
|
148
|
+
if (resultSolid._faceMetadata instanceof Map) {
|
|
149
|
+
const meta = resultSolid._faceMetadata;
|
|
150
|
+
if (!meta.has(targetFaceName) && meta.has(sourceFaceName)) {
|
|
151
|
+
meta.set(targetFaceName, meta.get(sourceFaceName));
|
|
152
|
+
}
|
|
153
|
+
meta.delete(sourceFaceName);
|
|
154
|
+
}
|
|
155
|
+
resultSolid._idToFaceName = idToFace;
|
|
156
|
+
resultSolid._faceNameToID = faceToId;
|
|
157
|
+
resultSolid._faceIndex = null;
|
|
158
|
+
resultSolid._dirty = true;
|
|
159
|
+
return replaced > 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function mergeTinyFacesIntoRoundFace(resultSolid, filletSolid, candidateNames, roundFaceName, featureID, boundaryCache, resultAreaCache) {
|
|
163
|
+
if (!resultSolid || !filletSolid || !Array.isArray(candidateNames) || candidateNames.length === 0) return;
|
|
164
|
+
const areaCacheResult = resultAreaCache || buildFaceAreaCache(resultSolid);
|
|
165
|
+
const areaCacheFillet = buildFaceAreaCache(filletSolid);
|
|
166
|
+
|
|
167
|
+
for (const capName of candidateNames) {
|
|
168
|
+
const capMeta = (typeof resultSolid.getFaceMetadata === 'function') ? resultSolid.getFaceMetadata(capName) : {};
|
|
169
|
+
const referenceArea = Number(capMeta?.filletSourceArea) > 0 ? Number(capMeta.filletSourceArea) : areaCacheFillet.get(capName);
|
|
170
|
+
if (!(referenceArea > 0)) continue;
|
|
171
|
+
const finalArea = areaCacheResult.get(capName);
|
|
172
|
+
if (!(finalArea > 0)) continue;
|
|
173
|
+
if (finalArea < referenceArea * END_CAP_AREA_RATIO_THRESHOLD) {
|
|
174
|
+
let targetFace = capMeta?.filletRoundFace || roundFaceName;
|
|
175
|
+
const neighborRound = findNeighborRoundFace(resultSolid, capName, areaCacheResult, boundaryCache);
|
|
176
|
+
if (neighborRound) targetFace = neighborRound;
|
|
177
|
+
if (!targetFace) targetFace = findLargestRoundFace(resultSolid, areaCacheResult);
|
|
178
|
+
if (!targetFace) continue;
|
|
179
|
+
const merged = mergeFaceIntoTarget(resultSolid, capName, targetFace);
|
|
180
|
+
if (merged) {
|
|
181
|
+
consoleLogReplacement('[Solid.fillet] Merged tiny fillet face into round face', {
|
|
182
|
+
featureID,
|
|
183
|
+
capName,
|
|
184
|
+
roundFaceName: targetFace,
|
|
185
|
+
ratio: finalArea / referenceArea,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function mergeSideFacesIntoRoundFace(resultSolid, filletName, roundFaceName) {
|
|
193
|
+
if (!resultSolid || !filletName || !roundFaceName) return;
|
|
194
|
+
const sideA = `${filletName}_SIDE_A`;
|
|
195
|
+
const sideB = `${filletName}_SIDE_B`;
|
|
196
|
+
const surfaceCA = `${filletName}_SURFACE_CA`;
|
|
197
|
+
const surfaceCB = `${filletName}_SURFACE_CB`;
|
|
198
|
+
mergeFaceIntoTarget(resultSolid, sideA, roundFaceName);
|
|
199
|
+
mergeFaceIntoTarget(resultSolid, sideB, roundFaceName);
|
|
200
|
+
mergeFaceIntoTarget(resultSolid, surfaceCA, roundFaceName);
|
|
201
|
+
mergeFaceIntoTarget(resultSolid, surfaceCB, roundFaceName);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getEdgePolylineLocal(edgeObj) {
|
|
205
|
+
if (!edgeObj) return [];
|
|
206
|
+
const cached = edgeObj?.userData?.polylineLocal;
|
|
207
|
+
if (Array.isArray(cached) && cached.length >= 2) {
|
|
208
|
+
return cached.map(p => [Number(p[0]) || 0, Number(p[1]) || 0, Number(p[2]) || 0]);
|
|
209
|
+
}
|
|
210
|
+
if (typeof edgeObj.points === 'function') {
|
|
211
|
+
const pts = edgeObj.points(false);
|
|
212
|
+
if (Array.isArray(pts) && pts.length >= 2) {
|
|
213
|
+
return pts.map(p => [Number(p.x) || 0, Number(p.y) || 0, Number(p.z) || 0]);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const pos = edgeObj?.geometry?.getAttribute?.('position');
|
|
217
|
+
if (pos && pos.itemSize === 3 && pos.count >= 2) {
|
|
218
|
+
const out = [];
|
|
219
|
+
for (let i = 0; i < pos.count; i++) {
|
|
220
|
+
out.push([pos.getX(i), pos.getY(i), pos.getZ(i)]);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function dedupePoints(points, tol = 1e-5) {
|
|
228
|
+
const out = [];
|
|
229
|
+
const seen = new Set();
|
|
230
|
+
const { q, k } = createQuantizer(tol);
|
|
231
|
+
for (const p of points || []) {
|
|
232
|
+
if (!Array.isArray(p) || p.length < 3) continue;
|
|
233
|
+
const qp = q(p);
|
|
234
|
+
const key = k(qp);
|
|
235
|
+
if (seen.has(key)) continue;
|
|
236
|
+
seen.add(key);
|
|
237
|
+
out.push(qp);
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function collectFacePoints(solid, faceName, out) {
|
|
243
|
+
if (!solid || typeof solid.getFace !== 'function' || !faceName) return out;
|
|
244
|
+
const tris = solid.getFace(faceName);
|
|
245
|
+
if (!Array.isArray(tris) || tris.length === 0) return out;
|
|
246
|
+
const dst = Array.isArray(out) ? out : [];
|
|
247
|
+
for (const tri of tris) {
|
|
248
|
+
const p1 = tri?.p1;
|
|
249
|
+
const p2 = tri?.p2;
|
|
250
|
+
const p3 = tri?.p3;
|
|
251
|
+
if (Array.isArray(p1) && p1.length >= 3) dst.push([Number(p1[0]) || 0, Number(p1[1]) || 0, Number(p1[2]) || 0]);
|
|
252
|
+
if (Array.isArray(p2) && p2.length >= 3) dst.push([Number(p2[0]) || 0, Number(p2[1]) || 0, Number(p2[2]) || 0]);
|
|
253
|
+
if (Array.isArray(p3) && p3.length >= 3) dst.push([Number(p3[0]) || 0, Number(p3[1]) || 0, Number(p3[2]) || 0]);
|
|
254
|
+
}
|
|
255
|
+
return dst;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function buildHullSolidFromPoints(points, name, SolidCtor, tol = 1e-5) {
|
|
259
|
+
const unique = dedupePoints(points, tol);
|
|
260
|
+
if (unique.length < 4) return null;
|
|
261
|
+
let hull = null;
|
|
262
|
+
try {
|
|
263
|
+
hull = Manifold.hull(unique);
|
|
264
|
+
} catch {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const solid = SolidCtor._fromManifold(hull, new Map([[0, name]]));
|
|
269
|
+
try { solid.name = name; } catch { }
|
|
270
|
+
const faceNames = (typeof solid.getFaceNames === 'function') ? solid.getFaceNames() : [];
|
|
271
|
+
for (const faceName of faceNames) {
|
|
272
|
+
if (!faceName || faceName === name) continue;
|
|
273
|
+
mergeFaceIntoTarget(solid, faceName, name);
|
|
274
|
+
}
|
|
275
|
+
return solid;
|
|
276
|
+
} catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function averageFaceNormalSimple(solid, faceName) {
|
|
282
|
+
if (!solid || typeof solid.getFace !== 'function' || !faceName) return null;
|
|
283
|
+
const tris = solid.getFace(faceName);
|
|
284
|
+
if (!Array.isArray(tris) || tris.length === 0) return null;
|
|
285
|
+
let nx = 0, ny = 0, nz = 0;
|
|
286
|
+
for (const tri of tris) {
|
|
287
|
+
const p1 = tri?.p1;
|
|
288
|
+
const p2 = tri?.p2;
|
|
289
|
+
const p3 = tri?.p3;
|
|
290
|
+
if (!Array.isArray(p1) || !Array.isArray(p2) || !Array.isArray(p3)) continue;
|
|
291
|
+
const ax = Number(p1[0]) || 0, ay = Number(p1[1]) || 0, az = Number(p1[2]) || 0;
|
|
292
|
+
const bx = Number(p2[0]) || 0, by = Number(p2[1]) || 0, bz = Number(p2[2]) || 0;
|
|
293
|
+
const cx = Number(p3[0]) || 0, cy = Number(p3[1]) || 0, cz = Number(p3[2]) || 0;
|
|
294
|
+
const ux = bx - ax, uy = by - ay, uz = bz - az;
|
|
295
|
+
const vx = cx - ax, vy = cy - ay, vz = cz - az;
|
|
296
|
+
const cxn = uy * vz - uz * vy;
|
|
297
|
+
const cyn = uz * vx - ux * vz;
|
|
298
|
+
const czn = ux * vy - uy * vx;
|
|
299
|
+
nx += cxn;
|
|
300
|
+
ny += cyn;
|
|
301
|
+
nz += czn;
|
|
302
|
+
}
|
|
303
|
+
const len = Math.hypot(nx, ny, nz);
|
|
304
|
+
if (!(len > 1e-12)) return null;
|
|
305
|
+
return [nx / len, ny / len, nz / len];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function deriveSolidToleranceFromVerts(solid, baseTol = 1e-5) {
|
|
309
|
+
const vp = Array.isArray(solid?._vertProperties) ? solid._vertProperties : null;
|
|
310
|
+
if (!vp || vp.length < 6) return baseTol;
|
|
311
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
312
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
313
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
314
|
+
const x = vp[i + 0];
|
|
315
|
+
const y = vp[i + 1];
|
|
316
|
+
const z = vp[i + 2];
|
|
317
|
+
if (x < minX) minX = x; if (x > maxX) maxX = x;
|
|
318
|
+
if (y < minY) minY = y; if (y > maxY) maxY = y;
|
|
319
|
+
if (z < minZ) minZ = z; if (z > maxZ) maxZ = z;
|
|
320
|
+
}
|
|
321
|
+
const dx = maxX - minX;
|
|
322
|
+
const dy = maxY - minY;
|
|
323
|
+
const dz = maxZ - minZ;
|
|
324
|
+
const diag = Math.hypot(dx, dy, dz) || 1;
|
|
325
|
+
return Math.max(baseTol, diag * 1e-6);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function buildAdjacencyFromBoundaryPolylines(solid) {
|
|
329
|
+
const map = new Map();
|
|
330
|
+
if (!solid || typeof solid.getBoundaryEdgePolylines !== 'function') return map;
|
|
331
|
+
const boundaries = solid.getBoundaryEdgePolylines() || [];
|
|
332
|
+
for (const poly of boundaries) {
|
|
333
|
+
const a = poly?.faceA;
|
|
334
|
+
const b = poly?.faceB;
|
|
335
|
+
if (!a || !b) continue;
|
|
336
|
+
if (!map.has(a)) map.set(a, new Set());
|
|
337
|
+
if (!map.has(b)) map.set(b, new Set());
|
|
338
|
+
map.get(a).add(b);
|
|
339
|
+
map.get(b).add(a);
|
|
340
|
+
}
|
|
341
|
+
return map;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function buildAdjacencyFromFaceEdges(solid, faceNames, tol) {
|
|
345
|
+
const { q, k } = createQuantizer(tol);
|
|
346
|
+
const edgeKey = (a, b) => (a < b ? `${a}|${b}` : `${b}|${a}`);
|
|
347
|
+
const edgeToFaces = new Map();
|
|
348
|
+
const faceToEdges = new Map();
|
|
349
|
+
for (const faceName of faceNames) {
|
|
350
|
+
const tris = solid.getFace(faceName);
|
|
351
|
+
if (!Array.isArray(tris) || tris.length === 0) continue;
|
|
352
|
+
const counts = new Map();
|
|
353
|
+
for (const tri of tris) {
|
|
354
|
+
const p1 = tri?.p1;
|
|
355
|
+
const p2 = tri?.p2;
|
|
356
|
+
const p3 = tri?.p3;
|
|
357
|
+
if (!Array.isArray(p1) || !Array.isArray(p2) || !Array.isArray(p3)) continue;
|
|
358
|
+
const v1 = k(q(p1));
|
|
359
|
+
const v2 = k(q(p2));
|
|
360
|
+
const v3 = k(q(p3));
|
|
361
|
+
const e12 = edgeKey(v1, v2);
|
|
362
|
+
const e23 = edgeKey(v2, v3);
|
|
363
|
+
const e31 = edgeKey(v3, v1);
|
|
364
|
+
counts.set(e12, (counts.get(e12) || 0) + 1);
|
|
365
|
+
counts.set(e23, (counts.get(e23) || 0) + 1);
|
|
366
|
+
counts.set(e31, (counts.get(e31) || 0) + 1);
|
|
367
|
+
}
|
|
368
|
+
const boundary = new Set();
|
|
369
|
+
for (const [key, count] of counts.entries()) {
|
|
370
|
+
if (count === 1) boundary.add(key);
|
|
371
|
+
}
|
|
372
|
+
faceToEdges.set(faceName, boundary);
|
|
373
|
+
for (const key of boundary) {
|
|
374
|
+
let set = edgeToFaces.get(key);
|
|
375
|
+
if (!set) { set = new Set(); edgeToFaces.set(key, set); }
|
|
376
|
+
set.add(faceName);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const adj = new Map();
|
|
380
|
+
for (const [faceName, edges] of faceToEdges.entries()) {
|
|
381
|
+
const set = new Set();
|
|
382
|
+
for (const key of edges) {
|
|
383
|
+
const faces = edgeToFaces.get(key);
|
|
384
|
+
if (!faces) continue;
|
|
385
|
+
for (const f of faces) {
|
|
386
|
+
if (f !== faceName) set.add(f);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
adj.set(faceName, set);
|
|
390
|
+
}
|
|
391
|
+
return adj;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function mergeInsetEndCapsByNormal(resultSolid, featureID, direction, dotThreshold = 0.999) {
|
|
395
|
+
if (!resultSolid || String(direction).toUpperCase() !== 'INSET') return;
|
|
396
|
+
if (typeof resultSolid.getFaceNames !== 'function') return;
|
|
397
|
+
const faceNames = resultSolid.getFaceNames() || [];
|
|
398
|
+
if (!Array.isArray(faceNames) || faceNames.length === 0) return;
|
|
399
|
+
const faceHasTris = (name) => {
|
|
400
|
+
if (!name || typeof resultSolid.getFace !== 'function') return false;
|
|
401
|
+
const tris = resultSolid.getFace(name);
|
|
402
|
+
return Array.isArray(tris) && tris.length > 0;
|
|
403
|
+
};
|
|
404
|
+
const activeFaceNames = faceNames.filter(faceHasTris);
|
|
405
|
+
const prefix = featureID ? `${featureID}_FILLET_` : '';
|
|
406
|
+
const endCapFaces = activeFaceNames.filter((name) => {
|
|
407
|
+
if (typeof name !== 'string') return false;
|
|
408
|
+
if (prefix && !name.startsWith(prefix)) return false;
|
|
409
|
+
return /_END_CAP_\d+$/.test(name);
|
|
410
|
+
});
|
|
411
|
+
consoleLogReplacement('[Solid.fillet] Inset end cap scan', {
|
|
412
|
+
featureID,
|
|
413
|
+
direction,
|
|
414
|
+
endCapFaces,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
if (!endCapFaces.length) return;
|
|
418
|
+
|
|
419
|
+
const adjacentMap = buildAdjacencyFromBoundaryPolylines(resultSolid);
|
|
420
|
+
const tol = deriveSolidToleranceFromVerts(resultSolid, 1e-5);
|
|
421
|
+
const edgeAdjMap = buildAdjacencyFromFaceEdges(resultSolid, activeFaceNames, tol);
|
|
422
|
+
|
|
423
|
+
const normalCache = new Map();
|
|
424
|
+
const getNormal = (name) => {
|
|
425
|
+
if (normalCache.has(name)) return normalCache.get(name);
|
|
426
|
+
const n = averageFaceNormalSimple(resultSolid, name);
|
|
427
|
+
normalCache.set(name, n);
|
|
428
|
+
return n;
|
|
429
|
+
};
|
|
430
|
+
const fmtNormal = (n) => (Array.isArray(n) && n.length >= 3)
|
|
431
|
+
? [Number(n[0].toFixed(6)), Number(n[1].toFixed(6)), Number(n[2].toFixed(6))]
|
|
432
|
+
: null;
|
|
433
|
+
|
|
434
|
+
const tryMergeWithAdj = (capName, adj) => {
|
|
435
|
+
if (!adj || adj.size === 0) return false;
|
|
436
|
+
const nCap = getNormal(capName);
|
|
437
|
+
if (!nCap) return false;
|
|
438
|
+
for (const neighbor of adj) {
|
|
439
|
+
if (neighbor === capName) continue;
|
|
440
|
+
const nAdj = getNormal(neighbor);
|
|
441
|
+
if (!nAdj) continue;
|
|
442
|
+
const dot = (nCap[0] * nAdj[0]) + (nCap[1] * nAdj[1]) + (nCap[2] * nAdj[2]);
|
|
443
|
+
if (dot >= dotThreshold) {
|
|
444
|
+
mergeFaceIntoTarget(resultSolid, capName, neighbor);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
let mergedCount = 0;
|
|
452
|
+
for (const capName of endCapFaces) {
|
|
453
|
+
const adj = adjacentMap.get(capName);
|
|
454
|
+
const adjEdge = edgeAdjMap.get(capName);
|
|
455
|
+
const adjAll = new Set([
|
|
456
|
+
...(adj ? Array.from(adj) : []),
|
|
457
|
+
...(adjEdge ? Array.from(adjEdge) : []),
|
|
458
|
+
]);
|
|
459
|
+
consoleLogReplacement('[Solid.fillet] Inset end cap normals', {
|
|
460
|
+
featureID,
|
|
461
|
+
capName,
|
|
462
|
+
capNormal: fmtNormal(getNormal(capName)),
|
|
463
|
+
adjacent: Array.from(adjAll).map((name) => ({
|
|
464
|
+
name,
|
|
465
|
+
normal: fmtNormal(getNormal(name)),
|
|
466
|
+
})),
|
|
467
|
+
});
|
|
468
|
+
if (tryMergeWithAdj(capName, adj)) {
|
|
469
|
+
consoleLogReplacement('[Solid.fillet] Inset end cap merged', { featureID, capName });
|
|
470
|
+
mergedCount++;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (tryMergeWithAdj(capName, edgeAdjMap.get(capName))) {
|
|
474
|
+
consoleLogReplacement('[Solid.fillet] Inset end cap merged', { featureID, capName });
|
|
475
|
+
mergedCount++;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
consoleLogReplacement('[Solid.fillet] Inset end cap merge summary', { featureID, mergedCount });
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Apply fillets to this Solid and return a new Solid with the result.
|
|
483
|
+
* Accepts either `edgeNames` (preferred) or explicit `edges` objects.
|
|
484
|
+
*
|
|
485
|
+
* @param {Object} opts
|
|
486
|
+
* @param {number} opts.radius Required fillet radius (> 0)
|
|
487
|
+
* @param {string[]} [opts.edgeNames] Optional edge names to fillet (resolved from this Solid's children)
|
|
488
|
+
* @param {any[]} [opts.edges] Optional pre-resolved Edge objects (must belong to this Solid)
|
|
489
|
+
* @param {'INSET'|'OUTSET'|string} [opts.direction='INSET'] Boolean behavior (subtract vs union)
|
|
490
|
+
* @param {number} [opts.inflate=0.1] Inflation for cutting tube
|
|
491
|
+
* @param {number} [opts.resolution=32] Tube resolution (segments around circumference)
|
|
492
|
+
* @param {boolean} [opts.combineEdges=false] Combine connected edges that share face pairs into single paths
|
|
493
|
+
* @param {boolean} [opts.debug=false] Enable debug visuals in fillet builder
|
|
494
|
+
* @param {boolean} [opts.showTangentOverlays=false] Show pre-inflate tangent overlays on the fillet tube
|
|
495
|
+
* @param {string} [opts.featureID='FILLET'] For naming of intermediates and result
|
|
496
|
+
* @returns {import('../BetterSolid.js').Solid}
|
|
497
|
+
*/
|
|
498
|
+
export async function fillet(opts = {}) {
|
|
499
|
+
const { filletSolid } = await import("../fillets/fillet.js");
|
|
500
|
+
const radius = Number(opts.radius);
|
|
501
|
+
if (!Number.isFinite(radius) || radius <= 0) {
|
|
502
|
+
throw new Error(`Solid.fillet: radius must be > 0, got ${opts.radius}`);
|
|
503
|
+
}
|
|
504
|
+
const dir = String(opts.direction || 'INSET').toUpperCase();
|
|
505
|
+
const inflate = Number.isFinite(opts.inflate) ? Number(opts.inflate) : 0.1;
|
|
506
|
+
const debug = !!opts.debug;
|
|
507
|
+
const resolutionRaw = Number(opts.resolution);
|
|
508
|
+
const resolution = (Number.isFinite(resolutionRaw) && resolutionRaw > 0)
|
|
509
|
+
? Math.max(8, Math.floor(resolutionRaw))
|
|
510
|
+
: 32;
|
|
511
|
+
const combineEdges = (dir !== 'INSET') && !!opts.combineEdges;
|
|
512
|
+
const showTangentOverlays = !!opts.showTangentOverlays;
|
|
513
|
+
const featureID = opts.featureID || 'FILLET';
|
|
514
|
+
const SolidCtor = this?.constructor;
|
|
515
|
+
consoleLogReplacement('[Solid.fillet] Begin', {
|
|
516
|
+
featureID,
|
|
517
|
+
solid: this?.name,
|
|
518
|
+
radius,
|
|
519
|
+
direction: dir,
|
|
520
|
+
inflate,
|
|
521
|
+
resolution,
|
|
522
|
+
debug,
|
|
523
|
+
showTangentOverlays,
|
|
524
|
+
combineEdges,
|
|
525
|
+
requestedEdgeNames: Array.isArray(opts.edgeNames) ? opts.edgeNames : [],
|
|
526
|
+
providedEdgeCount: Array.isArray(opts.edges) ? opts.edges.length : 0,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Resolve edges from names and/or provided objects
|
|
530
|
+
const unique = resolveEdgesFromInputs(this, { edgeNames: opts.edgeNames, edges: opts.edges });
|
|
531
|
+
if (unique.length === 0) {
|
|
532
|
+
console.warn('[Solid.fillet] No edges resolved on target solid; returning clone.', { featureID, solid: this?.name });
|
|
533
|
+
// Nothing to do - return an unchanged clone so caller can replace scene node safely
|
|
534
|
+
const c = this.clone();
|
|
535
|
+
try { c.name = this.name; } catch { }
|
|
536
|
+
return c;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const combineCornerHulls = combineEdges && unique.length > 1;
|
|
540
|
+
let filletEdges = unique;
|
|
541
|
+
if (combineCornerHulls) {
|
|
542
|
+
consoleLogReplacement('[Solid.fillet] combineEdges enabled: using corner hulls for shared endpoints.');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Build fillet solids per edge using existing core implementation
|
|
546
|
+
const filletEntries = [];
|
|
547
|
+
let idx = 0;
|
|
548
|
+
const debugAdded = [];
|
|
549
|
+
const attachDebugSolids = (target, reason = '') => {
|
|
550
|
+
if (!target || debugAdded.length === 0) return;
|
|
551
|
+
try { target.__debugAddedSolids = debugAdded; } catch { }
|
|
552
|
+
const prefix = debug ? '🐛 Debug' : '⚠️ Failure Debug';
|
|
553
|
+
const suffix = reason ? ` (${reason})` : '';
|
|
554
|
+
consoleLogReplacement(`${prefix}: Added ${debugAdded.length} debug solids to result${suffix}`);
|
|
555
|
+
};
|
|
556
|
+
for (const e of filletEdges) {
|
|
557
|
+
const name = `${featureID}_FILLET_${idx++}`;
|
|
558
|
+
const res = filletSolid({ edgeToFillet: e, radius, sideMode: dir, inflate, resolution, debug, name, showTangentOverlays }) || {};
|
|
559
|
+
|
|
560
|
+
// Handle debug solids even on failure
|
|
561
|
+
if (debug || !res.finalSolid) {
|
|
562
|
+
try { if (res.tube) debugAdded.push(res.tube); } catch { }
|
|
563
|
+
try { if (res.wedge) debugAdded.push(res.wedge); } catch { }
|
|
564
|
+
|
|
565
|
+
// If there was an error, log it and add debug info
|
|
566
|
+
if (res.error) {
|
|
567
|
+
console.warn(`Fillet failed for edge ${e?.name || idx}: ${res.error}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (!res.finalSolid) {
|
|
571
|
+
console.warn('[Solid.fillet] Fillet builder returned no finalSolid.', {
|
|
572
|
+
featureID,
|
|
573
|
+
edge: e?.name,
|
|
574
|
+
error: res.error,
|
|
575
|
+
hasTube: !!res.tube,
|
|
576
|
+
hasWedge: !!res.wedge,
|
|
577
|
+
});
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const mergeCandidates = getFilletMergeCandidateNames(res.finalSolid);
|
|
582
|
+
const roundFaceName = guessRoundFaceName(res.finalSolid, name);
|
|
583
|
+
filletEntries.push({
|
|
584
|
+
filletSolid: res.finalSolid,
|
|
585
|
+
filletName: name,
|
|
586
|
+
mergeCandidates,
|
|
587
|
+
roundFaceName,
|
|
588
|
+
wedgeSolid: res.wedge || null,
|
|
589
|
+
tubeSolid: res.tube || null,
|
|
590
|
+
edgeObj: e,
|
|
591
|
+
edgePoints: Array.isArray(res.edge) ? res.edge : [],
|
|
592
|
+
});
|
|
593
|
+
if (debug) {
|
|
594
|
+
try { if (res.tube) debugAdded.push(res.tube); } catch { }
|
|
595
|
+
try { if (res.wedge) debugAdded.push(res.wedge); } catch { }
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (filletEntries.length === 0) {
|
|
599
|
+
console.error('[Solid.fillet] All edge fillets failed; returning clone.', { featureID, edgeCount: unique.length });
|
|
600
|
+
const c = this.clone();
|
|
601
|
+
try { c.name = this.name; } catch { }
|
|
602
|
+
attachDebugSolids(c, 'all fillets failed');
|
|
603
|
+
return c;
|
|
604
|
+
}
|
|
605
|
+
consoleLogReplacement('[Solid.fillet] Built fillet solids for edges', filletEntries.length);
|
|
606
|
+
|
|
607
|
+
const cornerWedgeHulls = [];
|
|
608
|
+
const cornerTubeHulls = [];
|
|
609
|
+
let combinedFilletSolid = null;
|
|
610
|
+
if (combineCornerHulls && SolidCtor && filletEntries.length > 1) {
|
|
611
|
+
try {
|
|
612
|
+
const polylines = [];
|
|
613
|
+
for (const entry of filletEntries) {
|
|
614
|
+
const poly = getEdgePolylineLocal(entry.edgeObj);
|
|
615
|
+
if (poly.length >= 2) polylines.push(poly);
|
|
616
|
+
}
|
|
617
|
+
const cornerTol = deriveTolerance(polylines, 1e-5);
|
|
618
|
+
const { q, k } = createQuantizer(cornerTol);
|
|
619
|
+
const groups = new Map();
|
|
620
|
+
|
|
621
|
+
const addEndpoint = (pt, entry, cap) => {
|
|
622
|
+
if (!Array.isArray(pt) || pt.length < 3) return;
|
|
623
|
+
const qp = q(pt);
|
|
624
|
+
const key = k(qp);
|
|
625
|
+
if (!groups.has(key)) groups.set(key, { point: qp, items: [] });
|
|
626
|
+
groups.get(key).items.push({ entry, cap });
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
for (const entry of filletEntries) {
|
|
630
|
+
let poly = getEdgePolylineLocal(entry.edgeObj);
|
|
631
|
+
if (poly.length < 2 && Array.isArray(entry.edgePoints) && entry.edgePoints.length >= 2) {
|
|
632
|
+
poly = entry.edgePoints.map(p => [Number(p.x) || 0, Number(p.y) || 0, Number(p.z) || 0]);
|
|
633
|
+
}
|
|
634
|
+
if (poly.length < 2) continue;
|
|
635
|
+
addEndpoint(poly[0], entry, 'start');
|
|
636
|
+
addEndpoint(poly[poly.length - 1], entry, 'end');
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
let cornerIdx = 0;
|
|
640
|
+
for (const group of groups.values()) {
|
|
641
|
+
if (!group || !Array.isArray(group.items) || group.items.length < 2) continue;
|
|
642
|
+
const wedgePoints = [];
|
|
643
|
+
const tubePoints = [];
|
|
644
|
+
for (const item of group.items) {
|
|
645
|
+
const entry = item.entry;
|
|
646
|
+
if (!entry) continue;
|
|
647
|
+
const filletName = entry.filletName;
|
|
648
|
+
const wedge = entry.wedgeSolid;
|
|
649
|
+
const tube = entry.tubeSolid;
|
|
650
|
+
const capSuffix = (item.cap === 'start') ? '_END_CAP_1' : '_END_CAP_2';
|
|
651
|
+
const tubeSuffix = (item.cap === 'start') ? '_TUBE_CapStart' : '_TUBE_CapEnd';
|
|
652
|
+
if (wedge) collectFacePoints(wedge, `${filletName}${capSuffix}`, wedgePoints);
|
|
653
|
+
if (tube) collectFacePoints(tube, `${filletName}${tubeSuffix}`, tubePoints);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const wedgeHull = buildHullSolidFromPoints(wedgePoints, `${featureID}_CORNER_${cornerIdx}_WEDGE_HULL`, SolidCtor, cornerTol);
|
|
657
|
+
const tubeHull = buildHullSolidFromPoints(tubePoints, `${featureID}_CORNER_${cornerIdx}_TUBE_HULL`, SolidCtor, cornerTol);
|
|
658
|
+
if (!wedgeHull || !tubeHull) {
|
|
659
|
+
cornerIdx++;
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
cornerWedgeHulls.push(wedgeHull);
|
|
663
|
+
cornerTubeHulls.push(tubeHull);
|
|
664
|
+
if (debug) {
|
|
665
|
+
debugAdded.push(wedgeHull);
|
|
666
|
+
debugAdded.push(tubeHull);
|
|
667
|
+
}
|
|
668
|
+
cornerIdx++;
|
|
669
|
+
}
|
|
670
|
+
const wedgeParts = [];
|
|
671
|
+
const tubeParts = [];
|
|
672
|
+
for (const entry of filletEntries) {
|
|
673
|
+
if (entry.wedgeSolid) wedgeParts.push(entry.wedgeSolid);
|
|
674
|
+
if (entry.tubeSolid) tubeParts.push(entry.tubeSolid);
|
|
675
|
+
}
|
|
676
|
+
if (cornerWedgeHulls.length) wedgeParts.push(...cornerWedgeHulls);
|
|
677
|
+
if (cornerTubeHulls.length) tubeParts.push(...cornerTubeHulls);
|
|
678
|
+
|
|
679
|
+
const unionAll = (parts) => {
|
|
680
|
+
let acc = null;
|
|
681
|
+
for (const solid of parts) {
|
|
682
|
+
acc = acc ? acc.union(solid) : solid;
|
|
683
|
+
}
|
|
684
|
+
return acc;
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
const combinedWedge = unionAll(wedgeParts);
|
|
688
|
+
const combinedTube = unionAll(tubeParts);
|
|
689
|
+
if (combinedWedge && combinedTube) {
|
|
690
|
+
combinedFilletSolid = combinedWedge.subtract(combinedTube);
|
|
691
|
+
try { combinedFilletSolid.name = `${featureID}_FILLET_COMBINED`; } catch { }
|
|
692
|
+
if (debug) {
|
|
693
|
+
debugAdded.push(combinedWedge);
|
|
694
|
+
debugAdded.push(combinedTube);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
} catch (err) {
|
|
698
|
+
console.warn('[Solid.fillet] Corner hull build failed', { featureID, error: err?.message || err });
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Apply to base solid (union for OUTSET, subtract for INSET)
|
|
703
|
+
let result = this;
|
|
704
|
+
const solidsToApply = combinedFilletSolid ? [combinedFilletSolid] : filletEntries.map(entry => entry.filletSolid);
|
|
705
|
+
try {
|
|
706
|
+
for (const filletSolid of solidsToApply) {
|
|
707
|
+
const operation = (dir === 'OUTSET') ? 'union' : 'subtract';
|
|
708
|
+
result = (operation === 'union') ? result.union(filletSolid) : result.subtract(filletSolid);
|
|
709
|
+
|
|
710
|
+
result.visualize();
|
|
711
|
+
|
|
712
|
+
// Name the result for scene grouping/debugging
|
|
713
|
+
try { result.name = this.name; } catch { }
|
|
714
|
+
}
|
|
715
|
+
} catch (err) {
|
|
716
|
+
console.error('[Solid.fillet] Fillet boolean failed; returning clone.', { featureID, error: err?.message || err });
|
|
717
|
+
const fallback = this.clone();
|
|
718
|
+
try { fallback.name = this.name; } catch { }
|
|
719
|
+
attachDebugSolids(fallback, 'boolean failure');
|
|
720
|
+
return fallback;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
try {
|
|
724
|
+
const boundaryCache = { current: null };
|
|
725
|
+
const resultAreaCache = buildFaceAreaCache(result);
|
|
726
|
+
for (const entry of filletEntries) {
|
|
727
|
+
const { filletSolid, filletName } = entry;
|
|
728
|
+
const mergeSolid = combinedFilletSolid || filletSolid;
|
|
729
|
+
const roundFaceName = entry.roundFaceName || guessRoundFaceName(mergeSolid, filletName);
|
|
730
|
+
const candidateNames = (Array.isArray(entry.mergeCandidates) && entry.mergeCandidates.length)
|
|
731
|
+
? entry.mergeCandidates
|
|
732
|
+
: getFilletMergeCandidateNames(mergeSolid);
|
|
733
|
+
mergeTinyFacesIntoRoundFace(result, mergeSolid, candidateNames, roundFaceName, featureID, boundaryCache, resultAreaCache);
|
|
734
|
+
mergeSideFacesIntoRoundFace(result, filletName, roundFaceName);
|
|
735
|
+
}
|
|
736
|
+
} catch (err) {
|
|
737
|
+
console.warn('[Solid.fillet] Tiny fillet face merge failed', { featureID, error: err?.message || err });
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
try {
|
|
741
|
+
if (dir === 'INSET' && typeof result.mergeTinyFaces === 'function') {
|
|
742
|
+
await result.mergeTinyFaces(0.001);
|
|
743
|
+
}
|
|
744
|
+
} catch (err) {
|
|
745
|
+
console.warn('[Solid.fillet] mergeTinyFaces failed', { featureID, error: err?.message || err });
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
try {
|
|
749
|
+
await mergeInsetEndCapsByNormal(result, featureID, dir);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
console.warn('[Solid.fillet] Inset end cap merge failed', { featureID, error: err?.message || err });
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
await result.collapseTinyTriangles(0.0009);
|
|
756
|
+
} catch (err) {
|
|
757
|
+
console.warn('[Solid.fillet] collapseTinyTriangles failed', { featureID, error: err?.message || err });
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Attach debug artifacts for callers that want to add them to the scene
|
|
761
|
+
attachDebugSolids(result);
|
|
762
|
+
|
|
763
|
+
// Simplify the final result in place to clean up artifacts from booleans.
|
|
764
|
+
try {
|
|
765
|
+
await result.removeSmallIslands();
|
|
766
|
+
} catch (err) {
|
|
767
|
+
console.warn('[Solid.fillet] simplify failed; continuing without simplification', { featureID, error: err?.message || err });
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const finalTriCount = Array.isArray(result?._triVerts) ? (result._triVerts.length / 3) : 0;
|
|
771
|
+
const finalVertCount = Array.isArray(result?._vertProperties) ? (result._vertProperties.length / 3) : 0;
|
|
772
|
+
if (!result || finalTriCount === 0 || finalVertCount === 0) {
|
|
773
|
+
console.error('[Solid.fillet] Fillet result is empty or missing geometry.', {
|
|
774
|
+
featureID,
|
|
775
|
+
finalTriCount,
|
|
776
|
+
finalVertCount,
|
|
777
|
+
edgeCount: unique.length,
|
|
778
|
+
direction: dir,
|
|
779
|
+
inflate,
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
consoleLogReplacement('[Solid.fillet] Completed', { featureID, triangles: finalTriCount, vertices: finalVertCount });
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return result;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
function consoleLogReplacement(args){
|
|
791
|
+
if (debugMode) console.log(...args);
|
|
792
|
+
}
|