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,605 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
import { selectionHasSketch } from "../selectionUtils.js";
|
|
3
|
+
const THREE = BREP.THREE;
|
|
4
|
+
|
|
5
|
+
const inputParamsSchema = {
|
|
6
|
+
id: {
|
|
7
|
+
type: "string",
|
|
8
|
+
default_value: null,
|
|
9
|
+
hint: "unique identifier for the loft feature",
|
|
10
|
+
},
|
|
11
|
+
profiles: {
|
|
12
|
+
type: "reference_selection",
|
|
13
|
+
selectionFilter: ["SKETCH", "FACE"],
|
|
14
|
+
multiple: true,
|
|
15
|
+
default_value: [],
|
|
16
|
+
hint: "Select 2+ profiles (faces) to loft",
|
|
17
|
+
},
|
|
18
|
+
consumeProfileSketch: {
|
|
19
|
+
type: "boolean",
|
|
20
|
+
default_value: true,
|
|
21
|
+
hint: "Remove referenced sketches after creating the loft. Turn off to keep them in the scene.",
|
|
22
|
+
},
|
|
23
|
+
referencePoints: {
|
|
24
|
+
type: "reference_selection",
|
|
25
|
+
selectionFilter: ["VERTEX"],
|
|
26
|
+
multiple: true,
|
|
27
|
+
default_value: [],
|
|
28
|
+
label: "Start Points (optional)",
|
|
29
|
+
hint: "Optionally select start vertex per profile (order matches profiles)",
|
|
30
|
+
},
|
|
31
|
+
guideCurves: {
|
|
32
|
+
type: "reference_selection",
|
|
33
|
+
selectionFilter: ["EDGE"],
|
|
34
|
+
multiple: true,
|
|
35
|
+
default_value: [],
|
|
36
|
+
hint: "Optional guide curves (unused)",
|
|
37
|
+
},
|
|
38
|
+
loftType: {
|
|
39
|
+
type: "options",
|
|
40
|
+
options: ["normal"],
|
|
41
|
+
default_value: "normal",
|
|
42
|
+
hint: "Type of loft to create",
|
|
43
|
+
},
|
|
44
|
+
reverseFirstLoop: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
default_value: false,
|
|
47
|
+
label: "Reverse first loop order",
|
|
48
|
+
hint: "Reverse edge order of the first profile's outer loop",
|
|
49
|
+
},
|
|
50
|
+
boolean: {
|
|
51
|
+
type: "boolean_operation",
|
|
52
|
+
default_value: { targets: [], operation: 'NONE' },
|
|
53
|
+
hint: "Optional boolean operation with selected solids"
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export class LoftFeature {
|
|
58
|
+
static shortName = "LOFT";
|
|
59
|
+
static longName = "Loft";
|
|
60
|
+
static inputParamsSchema = inputParamsSchema;
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
this.inputParams = {};
|
|
64
|
+
this.persistentData = {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
uiFieldsTest(context) {
|
|
68
|
+
const params = this.inputParams || context?.params || {};
|
|
69
|
+
const partHistory = context?.history || null;
|
|
70
|
+
return selectionHasSketch(params.profiles, partHistory) ? [] : ["consumeProfileSketch"];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async run(partHistory) {
|
|
74
|
+
const { profiles } = this.inputParams;
|
|
75
|
+
if (!Array.isArray(profiles) || profiles.length < 2) {
|
|
76
|
+
console.warn("LoftFeature: select at least two profiles (faces or sketches)");
|
|
77
|
+
return { added: [], removed: [] };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Resolve input names to FACE objects; allow SKETCH that contains a FACE
|
|
81
|
+
const faces = [];
|
|
82
|
+
const removed = [];
|
|
83
|
+
const consumeSketch = this.inputParams?.consumeProfileSketch !== false;
|
|
84
|
+
for (const obj of profiles) {
|
|
85
|
+
if (!obj) continue;
|
|
86
|
+
let faceObj = obj;
|
|
87
|
+
if (obj && obj.type === 'SKETCH') {
|
|
88
|
+
faceObj = obj.children.find(ch => ch.type === 'FACE') || obj.children.find(ch => ch.userData?.faceName);
|
|
89
|
+
}
|
|
90
|
+
if (faceObj && faceObj.type === 'FACE') {
|
|
91
|
+
// If face came from a sketch, mark sketch for removal (structured)
|
|
92
|
+
if (consumeSketch && faceObj.parent && faceObj.parent.type === 'SKETCH') {
|
|
93
|
+
removed.push(faceObj.parent);
|
|
94
|
+
}
|
|
95
|
+
faces.push(faceObj);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (faces.length < 2) {
|
|
100
|
+
console.warn("LoftFeature: need at least two resolved FACE objects");
|
|
101
|
+
return { added: [], removed: [] };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Build a sidewall naming map using ONLY the first face's edge names
|
|
105
|
+
const firstFace = faces[0];
|
|
106
|
+
const key = (p) => `${p[0].toFixed(6)},${p[1].toFixed(6)},${p[2].toFixed(6)}`;
|
|
107
|
+
const pointToEdgeNames = new Map(); // key -> Set(edgeName)
|
|
108
|
+
const seedEdgePoint = (edgeName, arrP) => {
|
|
109
|
+
for (const p of arrP) {
|
|
110
|
+
const k = key(p);
|
|
111
|
+
let set = pointToEdgeNames.get(k);
|
|
112
|
+
if (!set) { set = new Set(); pointToEdgeNames.set(k, set); }
|
|
113
|
+
set.add(edgeName);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const collectEdgePolylineWorld = (edge) => {
|
|
117
|
+
const out = [];
|
|
118
|
+
const cached = edge?.userData?.polylineLocal;
|
|
119
|
+
const isWorld = !!(edge?.userData?.polylineWorld);
|
|
120
|
+
const v = new THREE.Vector3();
|
|
121
|
+
if (Array.isArray(cached) && cached.length >= 2) {
|
|
122
|
+
if (isWorld) return cached.map(p => [p[0], p[1], p[2]]);
|
|
123
|
+
for (const p of cached) { v.set(p[0], p[1], p[2]).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
const posAttr = edge?.geometry?.getAttribute?.('position');
|
|
127
|
+
if (posAttr && posAttr.itemSize === 3 && posAttr.count >= 2) {
|
|
128
|
+
for (let i = 0; i < posAttr.count; i++) { v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
const aStart = edge?.geometry?.attributes?.instanceStart;
|
|
132
|
+
const aEnd = edge?.geometry?.attributes?.instanceEnd;
|
|
133
|
+
if (aStart && aEnd && aStart.itemSize === 3 && aEnd.itemSize === 3 && aStart.count === aEnd.count && aStart.count >= 1) {
|
|
134
|
+
v.set(aStart.getX(0), aStart.getY(0), aStart.getZ(0)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]);
|
|
135
|
+
for (let i = 0; i < aEnd.count; i++) { v.set(aEnd.getX(i), aEnd.getY(i), aEnd.getZ(i)).applyMatrix4(edge.matrixWorld); out.push([v.x, v.y, v.z]); }
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
};
|
|
140
|
+
const edges0 = Array.isArray(firstFace?.edges) ? firstFace.edges : [];
|
|
141
|
+
const firstEdgesWorld = [];
|
|
142
|
+
for (const e of edges0) {
|
|
143
|
+
const name = e?.name || 'EDGE'; // use raw edge name from first face
|
|
144
|
+
const pts = collectEdgePolylineWorld(e);
|
|
145
|
+
if (pts.length >= 2) { seedEdgePoint(name, pts); firstEdgesWorld.push({ name, pts }); }
|
|
146
|
+
}
|
|
147
|
+
// Helper: unify loop form and cleanup
|
|
148
|
+
const closeAndDedup = (pts) => {
|
|
149
|
+
const pA = pts.slice();
|
|
150
|
+
if (pA.length >= 2) {
|
|
151
|
+
const first = pA[0], last = pA[pA.length - 1];
|
|
152
|
+
if (!(first[0] === last[0] && first[1] === last[1] && first[2] === last[2])) pA.push([first[0], first[1], first[2]]);
|
|
153
|
+
}
|
|
154
|
+
for (let i = pA.length - 2; i >= 0; i--) {
|
|
155
|
+
const a = pA[i], b = pA[i + 1];
|
|
156
|
+
if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) pA.splice(i + 1, 1);
|
|
157
|
+
}
|
|
158
|
+
return pA;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getLoops = (face) => {
|
|
162
|
+
const loops = Array.isArray(face?.userData?.boundaryLoopsWorld) ? face.userData.boundaryLoopsWorld : null;
|
|
163
|
+
if (loops && loops.length) return loops.map(l => ({ isHole: !!(l && l.isHole), pts: Array.isArray(l?.pts) ? l.pts : l }));
|
|
164
|
+
// Fallback: approximate a single outer loop by concatenating edge polylines
|
|
165
|
+
const edges = Array.isArray(face?.edges) ? face.edges : [];
|
|
166
|
+
const poly = [];
|
|
167
|
+
for (const e of edges) {
|
|
168
|
+
const pts = collectEdgePolylineWorld(e);
|
|
169
|
+
if (pts.length) poly.push(...pts);
|
|
170
|
+
}
|
|
171
|
+
return poly.length ? [{ isHole: false, pts: poly }] : [];
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Helpers to manipulate loops: ensure closed, rotate start, and reverse
|
|
175
|
+
const ensureClosed = (pts) => {
|
|
176
|
+
if (!Array.isArray(pts) || pts.length === 0) return [];
|
|
177
|
+
const out = pts.slice();
|
|
178
|
+
if (out.length >= 2) {
|
|
179
|
+
const a = out[0], b = out[out.length - 1];
|
|
180
|
+
if (!(a[0] === b[0] && a[1] === b[1] && a[2] === b[2])) out.push([a[0], a[1], a[2]]);
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
};
|
|
184
|
+
const toOpen = (pts) => {
|
|
185
|
+
if (!Array.isArray(pts) || pts.length === 0) return [];
|
|
186
|
+
const out = pts.slice();
|
|
187
|
+
if (out.length >= 2) {
|
|
188
|
+
const a = out[0], b = out[out.length - 1];
|
|
189
|
+
if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2]) out.pop();
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
};
|
|
193
|
+
const rotateStart = (ptsClosed, startIndex) => {
|
|
194
|
+
const open = toOpen(ptsClosed);
|
|
195
|
+
const n = open.length;
|
|
196
|
+
if (n === 0) return ensureClosed(ptsClosed);
|
|
197
|
+
const k = ((startIndex % n) + n) % n;
|
|
198
|
+
const rotated = open.slice(k).concat(open.slice(0, k));
|
|
199
|
+
return ensureClosed(rotated);
|
|
200
|
+
};
|
|
201
|
+
const reverseLoop = (ptsClosed) => {
|
|
202
|
+
const open = toOpen(ptsClosed).slice().reverse();
|
|
203
|
+
return ensureClosed(open);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Precompute loops for all faces (closed + dedup)
|
|
207
|
+
const loopsByFace = faces.map(f => getLoops(f).map(l => ({ ...l, pts: closeAndDedup(l.pts) })));
|
|
208
|
+
|
|
209
|
+
// Apply optional start-point alignment and first-loop reversal
|
|
210
|
+
try {
|
|
211
|
+
const refNames = Array.isArray(this.inputParams?.referencePoints) ? this.inputParams.referencePoints : [];
|
|
212
|
+
const scene = partHistory && partHistory.scene ? partHistory.scene : null;
|
|
213
|
+
const refObjs = [];
|
|
214
|
+
if (scene && refNames.length) {
|
|
215
|
+
for (const nm of refNames) {
|
|
216
|
+
try { refObjs.push(scene.getObjectByName(String(nm))); } catch { refObjs.push(null); }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const tmpV = new THREE.Vector3();
|
|
220
|
+
const dist2 = (a, b) => {
|
|
221
|
+
const dx=a[0]-b[0], dy=a[1]-b[1], dz=a[2]-b[2];
|
|
222
|
+
return dx*dx+dy*dy+dz*dz;
|
|
223
|
+
};
|
|
224
|
+
// For each face that has a provided vertex, rotate the OUTER loop (li=0) to start near that vertex
|
|
225
|
+
for (let i = 0; i < faces.length; i++) {
|
|
226
|
+
const ref = refObjs[i];
|
|
227
|
+
if (!ref || !loopsByFace[i] || !loopsByFace[i].length) continue;
|
|
228
|
+
try {
|
|
229
|
+
tmpV.set(0,0,0); ref.getWorldPosition(tmpV);
|
|
230
|
+
} catch { continue; }
|
|
231
|
+
const loop0 = loopsByFace[i][0];
|
|
232
|
+
const pts = Array.isArray(loop0?.pts) ? loop0.pts : [];
|
|
233
|
+
if (pts.length < 2) continue;
|
|
234
|
+
const rp = [tmpV.x, tmpV.y, tmpV.z];
|
|
235
|
+
// Find nearest point index on the closed loop
|
|
236
|
+
let bestIdx = 0; let bestD = +Infinity;
|
|
237
|
+
for (let j = 0; j < pts.length - 1; j++) { // ignore duplicate last
|
|
238
|
+
const d = dist2(pts[j], rp);
|
|
239
|
+
if (d < bestD) { bestD = d; bestIdx = j; }
|
|
240
|
+
}
|
|
241
|
+
loopsByFace[i][0] = { ...loop0, pts: rotateStart(pts, bestIdx) };
|
|
242
|
+
}
|
|
243
|
+
// Reverse first face's outer loop if requested
|
|
244
|
+
if (this.inputParams?.reverseFirstLoop && loopsByFace[0] && loopsByFace[0][0]) {
|
|
245
|
+
loopsByFace[0][0] = { ...loopsByFace[0][0], pts: reverseLoop(loopsByFace[0][0].pts) };
|
|
246
|
+
}
|
|
247
|
+
} catch (_) { /* alignment optional; ignore errors to keep legacy behavior */ }
|
|
248
|
+
|
|
249
|
+
// Robust pairwise alignment to prevent loft twisting
|
|
250
|
+
// - Resample B loop to match A loop count
|
|
251
|
+
// - Choose best rotation and orientation (reverse or not) to minimize distance
|
|
252
|
+
const toOpenLoop = (pts) => toOpen(pts);
|
|
253
|
+
const segLen = (p, q) => Math.hypot(p[0]-q[0], p[1]-q[1], p[2]-q[2]);
|
|
254
|
+
const ringLength = (open) => {
|
|
255
|
+
const n = open.length; if (n === 0) return 0;
|
|
256
|
+
let L = 0; for (let i=0;i<n;i++){ const a=open[i], b=open[(i+1)%n]; L += segLen(a,b);} return L;
|
|
257
|
+
};
|
|
258
|
+
const lerp = (a,b,t)=>[a[0]+(b[0]-a[0])*t, a[1]+(b[1]-a[1])*t, a[2]+(b[2]-a[2])*t];
|
|
259
|
+
const resampleRing = (open, count) => {
|
|
260
|
+
const n = open.length; if (n === 0 || count <= 0) return [];
|
|
261
|
+
if (n === count) return open.slice();
|
|
262
|
+
const total = ringLength(open);
|
|
263
|
+
if (total <= 0) return open.slice(0, count);
|
|
264
|
+
// build cumulative lengths
|
|
265
|
+
const cum = new Array(n+1); cum[0]=0;
|
|
266
|
+
for (let i=0;i<n;i++){ cum[i+1]=cum[i]+segLen(open[i], open[(i+1)%n]); }
|
|
267
|
+
const out = new Array(count);
|
|
268
|
+
const step = total / count;
|
|
269
|
+
let seg = 0; // current segment index
|
|
270
|
+
for (let k=0;k<count;k++){
|
|
271
|
+
const s = k*step;
|
|
272
|
+
// advance until cum[seg+1] >= s
|
|
273
|
+
while (seg < n && cum[seg+1] < s - 1e-9) seg++;
|
|
274
|
+
const a = open[seg % n];
|
|
275
|
+
const b = open[(seg+1)%n];
|
|
276
|
+
const segStart = cum[seg];
|
|
277
|
+
const segLenTot = Math.max(1e-12, cum[seg+1]-segStart);
|
|
278
|
+
const t = (s - segStart) / segLenTot;
|
|
279
|
+
out[k] = lerp(a,b,Math.max(0,Math.min(1,t)));
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
};
|
|
283
|
+
const rotateOpen = (open, off)=>{
|
|
284
|
+
const n=open.length; if (!n) return [];
|
|
285
|
+
const k=((off%n)+n)%n; return open.slice(k).concat(open.slice(0,k));
|
|
286
|
+
};
|
|
287
|
+
const reverseOpen = (open)=>open.slice().reverse();
|
|
288
|
+
const sumSqDist = (aOpen, bOpen)=>{
|
|
289
|
+
const n=aOpen.length; let s=0; for(let i=0;i<n;i++){ const p=aOpen[i], q=bOpen[i]; const dx=p[0]-q[0], dy=p[1]-q[1], dz=p[2]-q[2]; s+=dx*dx+dy*dy+dz*dz; } return s;
|
|
290
|
+
};
|
|
291
|
+
const bestAlignBtoA = (aClosed, bClosed) => {
|
|
292
|
+
const aOpen = toOpenLoop(aClosed);
|
|
293
|
+
let bOpen = toOpenLoop(bClosed);
|
|
294
|
+
const n = aOpen.length; if (n === 0 || bOpen.length === 0) return bClosed;
|
|
295
|
+
if (bOpen.length !== n) bOpen = resampleRing(bOpen, n);
|
|
296
|
+
let best = { cost: +Infinity, off: 0, rev: false };
|
|
297
|
+
// same orientation
|
|
298
|
+
for (let off=0; off<n; off++){
|
|
299
|
+
const cand = rotateOpen(bOpen, off);
|
|
300
|
+
const cost = sumSqDist(aOpen, cand);
|
|
301
|
+
if (cost < best.cost) best = { cost, off, rev: false };
|
|
302
|
+
}
|
|
303
|
+
// reversed orientation
|
|
304
|
+
const bRev = reverseOpen(bOpen);
|
|
305
|
+
for (let off=0; off<n; off++){
|
|
306
|
+
const cand = rotateOpen(bRev, off);
|
|
307
|
+
const cost = sumSqDist(aOpen, cand);
|
|
308
|
+
if (cost < best.cost) best = { cost, off, rev: true };
|
|
309
|
+
}
|
|
310
|
+
const chosenOpen = best.rev ? rotateOpen(bRev, best.off) : rotateOpen(bOpen, best.off);
|
|
311
|
+
return ensureClosed(chosenOpen);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Build an aligned copy of loops across faces to avoid twisting
|
|
315
|
+
const loopsAligned = loopsByFace.map(faceLoops => faceLoops.map(l => ({ ...l, pts: l.pts.slice() })));
|
|
316
|
+
for (let i = 0; i < faces.length - 1; i++) {
|
|
317
|
+
const lfA = loopsAligned[i] || [];
|
|
318
|
+
const lfB = loopsAligned[i + 1] || [];
|
|
319
|
+
const L = Math.min(lfA.length, lfB.length);
|
|
320
|
+
for (let li = 0; li < L; li++) {
|
|
321
|
+
try {
|
|
322
|
+
const a = lfA[li].pts;
|
|
323
|
+
const b = lfB[li].pts;
|
|
324
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length < 2 || b.length < 2) continue;
|
|
325
|
+
lfB[li] = { ...lfB[li], pts: bestAlignBtoA(a, b) };
|
|
326
|
+
} catch (_) { /* continue */ }
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const solid = new BREP.Solid();
|
|
331
|
+
solid.name = this.inputParams.featureID || 'Loft';
|
|
332
|
+
|
|
333
|
+
// Caps on first and last faces
|
|
334
|
+
const addCapFromFace = (face, capNamePrefix, reverseStart) => {
|
|
335
|
+
const groups = Array.isArray(face?.userData?.profileGroups) ? face.userData.profileGroups : null;
|
|
336
|
+
if (groups && groups.length) {
|
|
337
|
+
for (const g of groups) {
|
|
338
|
+
const contour2D = g.contour2D || [];
|
|
339
|
+
const holes2D = g.holes2D || [];
|
|
340
|
+
const contourW = g.contourW || [];
|
|
341
|
+
const holesW = g.holesW || [];
|
|
342
|
+
if (contour2D.length < 3 || contourW.length !== contour2D.length) continue;
|
|
343
|
+
const contourV2 = contour2D.map(p => new THREE.Vector2(p[0], p[1]));
|
|
344
|
+
const holesV2 = holes2D.map(h => h.map(p => new THREE.Vector2(p[0], p[1])));
|
|
345
|
+
const tris = THREE.ShapeUtils.triangulateShape(contourV2, holesV2);
|
|
346
|
+
const allW = contourW.concat(...holesW);
|
|
347
|
+
for (const t of tris) {
|
|
348
|
+
const p0 = allW[t[0]], p1 = allW[t[1]], p2 = allW[t[2]];
|
|
349
|
+
if (reverseStart) solid.addTriangle(`${capNamePrefix}_START`, p0, p2, p1);
|
|
350
|
+
else solid.addTriangle(`${capNamePrefix}_END`, p0, p1, p2);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
const baseGeom = face.geometry;
|
|
355
|
+
const posAttr = baseGeom?.getAttribute?.('position');
|
|
356
|
+
if (posAttr) {
|
|
357
|
+
const idx = baseGeom.getIndex();
|
|
358
|
+
const hasIndex = !!idx;
|
|
359
|
+
const v = new THREE.Vector3();
|
|
360
|
+
const world = new Array(posAttr.count);
|
|
361
|
+
for (let i = 0; i < posAttr.count; i++) { v.set(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i)).applyMatrix4(face.matrixWorld); world[i] = [v.x, v.y, v.z]; }
|
|
362
|
+
const addTri = (i0, i1, i2) => {
|
|
363
|
+
const p0 = world[i0], p1 = world[i1], p2 = world[i2];
|
|
364
|
+
if (reverseStart) solid.addTriangle(`${capNamePrefix}_START`, p0, p2, p1);
|
|
365
|
+
else solid.addTriangle(`${capNamePrefix}_END`, p0, p1, p2);
|
|
366
|
+
};
|
|
367
|
+
if (hasIndex) { for (let i = 0; i < idx.count; i += 3) addTri(idx.getX(i+0)>>>0, idx.getX(i+1)>>>0, idx.getX(i+2)>>>0); }
|
|
368
|
+
else { for (let t = 0; t < (posAttr.count/3|0); t++) addTri(3*t+0, 3*t+1, 3*t+2); }
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// Add start and end caps
|
|
374
|
+
addCapFromFace(faces[0], `${faces[0].name || 'Face'}`, true);
|
|
375
|
+
addCapFromFace(faces[faces.length - 1], `${faces[faces.length - 1].name || 'Face'}`, false);
|
|
376
|
+
|
|
377
|
+
// Side walls: connect corresponding edges (1:1) between consecutive faces
|
|
378
|
+
|
|
379
|
+
// Order edges around the loop by connectivity; orient to follow boundary
|
|
380
|
+
const pointKey = (p)=> `${p[0].toFixed(5)},${p[1].toFixed(5)},${p[2].toFixed(5)}`;
|
|
381
|
+
const getOrderedOuterEdges = (face) => {
|
|
382
|
+
const all = Array.isArray(face?.edges) ? face.edges.filter(Boolean) : [];
|
|
383
|
+
// Prefer only outer-loop edges when flagged by SketchFeature
|
|
384
|
+
const outers = all.filter(e => !e?.userData?.isHole);
|
|
385
|
+
const list = (outers.length ? outers : all).map(e => ({ e, name: e?.name || 'EDGE', pts: collectEdgePolylineWorld(e) }));
|
|
386
|
+
// Build adjacency via endpoints
|
|
387
|
+
const nodes = new Map(); // key -> array of {idx, end:'start'|'end'}
|
|
388
|
+
const startKey = []; const endKey = [];
|
|
389
|
+
for (let i=0;i<list.length;i++){
|
|
390
|
+
const pts = list[i].pts;
|
|
391
|
+
if (!pts || pts.length < 2) continue;
|
|
392
|
+
const a = pts[0], b = pts[pts.length-1];
|
|
393
|
+
const ka = pointKey(a), kb = pointKey(b);
|
|
394
|
+
startKey[i]=ka; endKey[i]=kb;
|
|
395
|
+
if (!nodes.has(ka)) nodes.set(ka, []); nodes.get(ka).push({ idx:i, end:'start' });
|
|
396
|
+
if (!nodes.has(kb)) nodes.set(kb, []); nodes.get(kb).push({ idx:i, end:'end' });
|
|
397
|
+
}
|
|
398
|
+
if (!list.length) return [];
|
|
399
|
+
// Pick a start edge (arbitrary deterministic)
|
|
400
|
+
let curIdx = 0; let curPts = list[curIdx].pts.slice();
|
|
401
|
+
let curEnd = endKey[curIdx];
|
|
402
|
+
const used = new Set([curIdx]);
|
|
403
|
+
const ordered = [{ name:list[curIdx].name, pts:curPts }];
|
|
404
|
+
let guard = 0;
|
|
405
|
+
while (used.size < list.length && guard++ < list.length*2) {
|
|
406
|
+
const cand = (nodes.get(curEnd) || []).filter(n => !used.has(n.idx));
|
|
407
|
+
if (!cand.length) {
|
|
408
|
+
// Heuristic: find nearest unvisited edge start/end to curEnd
|
|
409
|
+
let best=null, bestD=+Infinity;
|
|
410
|
+
const toVec = (k)=>{ const s=k.split(',').map(Number); return {x:s[0],y:s[1],z:s[2]}; };
|
|
411
|
+
const C = toVec(curEnd);
|
|
412
|
+
for (let i=0;i<list.length;i++){
|
|
413
|
+
if (used.has(i)) continue;
|
|
414
|
+
const a=list[i].pts[0], b=list[i].pts[list[i].pts.length-1];
|
|
415
|
+
const dA=Math.hypot(a[0]-C.x,a[1]-C.y,a[2]-C.z);
|
|
416
|
+
const dB=Math.hypot(b[0]-C.x,b[1]-C.y,b[2]-C.z);
|
|
417
|
+
if (dA<bestD){ bestD=dA; best={idx:i, end:'start'}; }
|
|
418
|
+
if (dB<bestD){ bestD=dB; best={idx:i, end:'end'}; }
|
|
419
|
+
}
|
|
420
|
+
if (!best) break;
|
|
421
|
+
cand.push(best);
|
|
422
|
+
}
|
|
423
|
+
const next = cand[0];
|
|
424
|
+
used.add(next.idx);
|
|
425
|
+
let pts = list[next.idx].pts.slice();
|
|
426
|
+
// Orient to connect from current end
|
|
427
|
+
if (next.end === 'start') {
|
|
428
|
+
// already starts at curEnd
|
|
429
|
+
} else {
|
|
430
|
+
pts = pts.slice().reverse();
|
|
431
|
+
}
|
|
432
|
+
ordered.push({ name:list[next.idx].name, pts });
|
|
433
|
+
curEnd = pointKey(pts[pts.length-1]);
|
|
434
|
+
}
|
|
435
|
+
return ordered;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// Rotate ring so a vertex (if provided) lies on the first edge
|
|
439
|
+
const rotateEdgesByRef = (ring, refObj) => {
|
|
440
|
+
if (!refObj || !Array.isArray(ring) || ring.length===0) return ring;
|
|
441
|
+
const pos = new THREE.Vector3();
|
|
442
|
+
try { refObj.getWorldPosition(pos); } catch { return ring; }
|
|
443
|
+
const rp = [pos.x,pos.y,pos.z];
|
|
444
|
+
let bestI=0, bestD=+Infinity;
|
|
445
|
+
for (let i=0;i<ring.length;i++){
|
|
446
|
+
const pts = ring[i].pts; if (!pts || pts.length===0) continue;
|
|
447
|
+
const p = pts[0];
|
|
448
|
+
const d = (p[0]-rp[0])*(p[0]-rp[0])+(p[1]-rp[1])*(p[1]-rp[1])+(p[2]-rp[2])*(p[2]-rp[2]);
|
|
449
|
+
if (d<bestD){bestD=d; bestI=i;}
|
|
450
|
+
}
|
|
451
|
+
return ring.slice(bestI).concat(ring.slice(0,bestI));
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const reverseEdgeRing = (ring) => ring.slice().reverse().map(seg => ({ name: seg.name, pts: seg.pts.slice().reverse() }));
|
|
455
|
+
|
|
456
|
+
const edgeRingMidpoints = (ring) => ring.map(seg => {
|
|
457
|
+
const pts = seg.pts || []; if (pts.length===0) return [0,0,0];
|
|
458
|
+
const a = pts[0], b = pts[pts.length-1]; return [(a[0]+b[0])/2,(a[1]+b[1])/2,(a[2]+b[2])/2];
|
|
459
|
+
});
|
|
460
|
+
const sumDist = (A,B)=>{ let s=0; for(let i=0;i<A.length;i++){ const p=A[i], q=B[i]; const dx=p[0]-q[0], dy=p[1]-q[1], dz=p[2]-q[2]; s += Math.hypot(dx,dy,dz); } return s; };
|
|
461
|
+
const rotateRing = (ring, off)=>{ const n=ring.length; const k=((off%n)+n)%n; return ring.slice(k).concat(ring.slice(0,k)); };
|
|
462
|
+
const bestAlignRings = (ringA, ringB) => {
|
|
463
|
+
// Try both orientations; choose rotation minimizing mid-point distance
|
|
464
|
+
const midA = edgeRingMidpoints(ringA);
|
|
465
|
+
let best = { rev:false, off:0, cost:+Infinity };
|
|
466
|
+
const tryOrient = (RB, rev) => {
|
|
467
|
+
const midB = edgeRingMidpoints(RB);
|
|
468
|
+
for (let off=0; off<RB.length; off++){
|
|
469
|
+
const rotB = rotateRing(midB, off);
|
|
470
|
+
const cost = sumDist(midA, rotB);
|
|
471
|
+
if (cost < best.cost) best = { rev, off, cost };
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
tryOrient(ringB, false);
|
|
475
|
+
tryOrient(reverseEdgeRing(ringB), true);
|
|
476
|
+
return best;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Build reference of start points (optional) to rotate rings
|
|
480
|
+
const refNames = Array.isArray(this.inputParams?.referencePoints) ? this.inputParams.referencePoints : [];
|
|
481
|
+
const scene = partHistory && partHistory.scene ? partHistory.scene : null;
|
|
482
|
+
const refObjs = [];
|
|
483
|
+
if (scene && refNames.length) {
|
|
484
|
+
for (const nm of refNames) { try { refObjs.push(scene.getObjectByName(String(nm))); } catch { refObjs.push(null); } }
|
|
485
|
+
}
|
|
486
|
+
// Build aligned edge rings for all faces relative to the first face's ring
|
|
487
|
+
let refRing = getOrderedOuterEdges(faces[0]);
|
|
488
|
+
if (refObjs[0]) refRing = rotateEdgesByRef(refRing, refObjs[0]);
|
|
489
|
+
if (this.inputParams?.reverseFirstLoop) refRing = reverseEdgeRing(refRing);
|
|
490
|
+
const alignedRings = new Array(faces.length);
|
|
491
|
+
alignedRings[0] = refRing;
|
|
492
|
+
for (let j = 1; j < faces.length; j++) {
|
|
493
|
+
let ring = getOrderedOuterEdges(faces[j]);
|
|
494
|
+
if (refObjs[j]) ring = rotateEdgesByRef(ring, refObjs[j]);
|
|
495
|
+
const al = bestAlignRings(refRing, ring);
|
|
496
|
+
if (al.rev) ring = reverseEdgeRing(ring);
|
|
497
|
+
ring = rotateRing(ring, al.off);
|
|
498
|
+
alignedRings[j] = ring;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
for (let i = 0; i < faces.length - 1; i++) {
|
|
502
|
+
// Use rings aligned to the first face for consistent naming
|
|
503
|
+
const ringA = alignedRings[i];
|
|
504
|
+
const ringB = alignedRings[i + 1];
|
|
505
|
+
if (!ringA || !ringB || !ringA.length || !ringB.length) continue;
|
|
506
|
+
|
|
507
|
+
const N = Math.min(ringA.length, ringB.length, refRing.length);
|
|
508
|
+
const d2 = (a,b)=>{ const dx=a[0]-b[0], dy=a[1]-b[1], dz=a[2]-b[2]; return dx*dx+dy*dy+dz*dz; };
|
|
509
|
+
const triArea2 = (p0,p1,p2)=>{
|
|
510
|
+
const ux=p1[0]-p0[0], uy=p1[1]-p0[1], uz=p1[2]-p0[2];
|
|
511
|
+
const vx=p2[0]-p0[0], vy=p2[1]-p0[1], vz=p2[2]-p0[2];
|
|
512
|
+
const cx=uy*vz-uz*vy, cy=uz*vx-ux*vz, cz=ux*vy-uy*vx;
|
|
513
|
+
return cx*cx+cy*cy+cz*cz;
|
|
514
|
+
};
|
|
515
|
+
const dedup = (arr)=>{ const out=[]; for(const p of (arr||[])){ const q=out[out.length-1]; if(!q||d2(q,p)>1e-18) out.push(p); } return out; };
|
|
516
|
+
const triangulateStripNoResample = (Araw, Braw, isHole, name) => {
|
|
517
|
+
const A = dedup(Araw), B = dedup(Braw);
|
|
518
|
+
const m = A.length, n = B.length;
|
|
519
|
+
if (m < 2 || n < 2) return;
|
|
520
|
+
let i = 0, j = 0;
|
|
521
|
+
const da = m - 1, db = n - 1;
|
|
522
|
+
// Balanced Bresenham-style walk so steps distribute evenly from both ends
|
|
523
|
+
if (da >= db) {
|
|
524
|
+
let err = (da - db) / 2; // shift start so the switch is centered
|
|
525
|
+
for (let step = 0; step < da; step++) {
|
|
526
|
+
// A step
|
|
527
|
+
const p0 = A[i], p1 = A[i + 1], p2 = B[j];
|
|
528
|
+
if (triArea2(p0, p1, p2) > 1e-24) {
|
|
529
|
+
if (isHole) solid.addTriangle(name, p0, p2, p1);
|
|
530
|
+
else solid.addTriangle(name, p0, p1, p2);
|
|
531
|
+
}
|
|
532
|
+
i++;
|
|
533
|
+
err -= db;
|
|
534
|
+
if (err < 0 && j < db) {
|
|
535
|
+
// B step
|
|
536
|
+
const q0 = A[i], q1 = B[j + 1], q2 = B[j];
|
|
537
|
+
if (triArea2(q0, q1, q2) > 1e-24) {
|
|
538
|
+
if (isHole) solid.addTriangle(name, q0, q1, q2);
|
|
539
|
+
else solid.addTriangle(name, q0, q2, q1);
|
|
540
|
+
}
|
|
541
|
+
j++;
|
|
542
|
+
err += da;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
while (j < db) {
|
|
546
|
+
const r0 = A[i], r1 = B[j + 1], r2 = B[j];
|
|
547
|
+
if (triArea2(r0, r1, r2) > 1e-24) {
|
|
548
|
+
if (isHole) solid.addTriangle(name, r0, r1, r2);
|
|
549
|
+
else solid.addTriangle(name, r0, r2, r1);
|
|
550
|
+
}
|
|
551
|
+
j++;
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
let err = (db - da) / 2; // symmetric start for B-major
|
|
555
|
+
for (let step = 0; step < db; step++) {
|
|
556
|
+
// B step
|
|
557
|
+
const p0 = A[i], p1 = B[j + 1], p2 = B[j];
|
|
558
|
+
if (triArea2(p0, p1, p2) > 1e-24) {
|
|
559
|
+
if (isHole) solid.addTriangle(name, p0, p1, p2);
|
|
560
|
+
else solid.addTriangle(name, p0, p2, p1);
|
|
561
|
+
}
|
|
562
|
+
j++;
|
|
563
|
+
err -= da;
|
|
564
|
+
if (err < 0 && i < da) {
|
|
565
|
+
// A step
|
|
566
|
+
const q0 = A[i], q1 = A[i + 1], q2 = B[j];
|
|
567
|
+
if (triArea2(q0, q1, q2) > 1e-24) {
|
|
568
|
+
if (isHole) solid.addTriangle(name, q0, q2, q1);
|
|
569
|
+
else solid.addTriangle(name, q0, q1, q2);
|
|
570
|
+
}
|
|
571
|
+
i++;
|
|
572
|
+
err += db;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
while (i < da) {
|
|
576
|
+
const r0 = A[i], r1 = A[i + 1], r2 = B[j];
|
|
577
|
+
if (triArea2(r0, r1, r2) > 1e-24) {
|
|
578
|
+
if (isHole) solid.addTriangle(name, r0, r2, r1);
|
|
579
|
+
else solid.addTriangle(name, r0, r1, r2);
|
|
580
|
+
}
|
|
581
|
+
i++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
for (let ei = 0; ei < N; ei++) {
|
|
586
|
+
const a = ringA[ei];
|
|
587
|
+
const b = ringB[ei];
|
|
588
|
+
const fname = `${refRing[ei]?.name || 'EDGE'}_LF`;
|
|
589
|
+
triangulateStripNoResample(a.pts, b.pts, false, fname);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
try { solid.setEpsilon(1e-6); } catch {}
|
|
594
|
+
solid.visualize();
|
|
595
|
+
const effects = await BREP.applyBooleanOperation(partHistory || {}, solid, this.inputParams.boolean, this.inputParams.featureID);
|
|
596
|
+
const booleanRemoved = Array.isArray(effects.removed) ? effects.removed : [];
|
|
597
|
+
const removedArtifacts = [...removed, ...booleanRemoved];
|
|
598
|
+
// Flag removals (sketch parents + boolean effects)
|
|
599
|
+
try { for (const obj of removedArtifacts) { if (obj) obj.__removeFlag = true; } } catch {}
|
|
600
|
+
return {
|
|
601
|
+
added: Array.isArray(effects.added) ? effects.added : [],
|
|
602
|
+
removed: removedArtifacts,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|