brep-io-kernel 1.0.0-ci.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +32 -0
- package/README.md +154 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/package.json +58 -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,43 @@
|
|
|
1
|
+
import { SheetMetalFlangeFeature } from "./SheetMetalFlangeFeature.js";
|
|
2
|
+
|
|
3
|
+
const baseSchema = SheetMetalFlangeFeature.inputParamsSchema || {};
|
|
4
|
+
const inputParamsSchema = {
|
|
5
|
+
...baseSchema,
|
|
6
|
+
id: {
|
|
7
|
+
...(baseSchema.id || {}),
|
|
8
|
+
hint: "Unique identifier for the hem feature",
|
|
9
|
+
},
|
|
10
|
+
flangeLength: {
|
|
11
|
+
...(baseSchema.flangeLength || {}),
|
|
12
|
+
label: "Hem length",
|
|
13
|
+
hint: "Optional straight leg length extruded from the hem end face. Set to 0 to create only the bend.",
|
|
14
|
+
},
|
|
15
|
+
flangeLengthReference: {
|
|
16
|
+
...(baseSchema.flangeLengthReference || {}),
|
|
17
|
+
label: "Hem length reference",
|
|
18
|
+
hint: "Measurement basis for the hem leg: inside, outside, or web.",
|
|
19
|
+
},
|
|
20
|
+
angle: {
|
|
21
|
+
...(baseSchema.angle || {}),
|
|
22
|
+
default_value: 180,
|
|
23
|
+
min: 180,
|
|
24
|
+
max: 180,
|
|
25
|
+
hint: "Hem angle is fixed at 180 degrees.",
|
|
26
|
+
},
|
|
27
|
+
bendRadius: {
|
|
28
|
+
...(baseSchema.bendRadius || {}),
|
|
29
|
+
default_value: 0.0001,
|
|
30
|
+
hint: "Hem bend radius (inside). Defaults to 0.0001.",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export class SheetMetalHemFeature extends SheetMetalFlangeFeature {
|
|
35
|
+
static shortName = "SM.HEM";
|
|
36
|
+
static longName = "Sheet Metal Hem";
|
|
37
|
+
static inputParamsSchema = inputParamsSchema;
|
|
38
|
+
static baseType = "HEM";
|
|
39
|
+
static logTag = "SheetMetalHem";
|
|
40
|
+
static defaultAngle = 180;
|
|
41
|
+
static angleOverride = 180;
|
|
42
|
+
static defaultBendRadius = 0.0001;
|
|
43
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Solid } from "../../BREP/BetterSolid.js";
|
|
2
|
+
import { buildSheetMetalFlatPatternSolids } from "../../exporters/sheetMetalFlatPattern.js";
|
|
3
|
+
import { applySheetMetalMetadata } from "./sheetMetalMetadata.js";
|
|
4
|
+
import { propagateSheetMetalFaceTypesToEdges } from "./sheetMetalFaceTypes.js";
|
|
5
|
+
import { buildSheetMetalSolidFromTree } from "./sheetMetalPipeline.js";
|
|
6
|
+
import {
|
|
7
|
+
SHEET_METAL_NODE_TYPES,
|
|
8
|
+
appendSheetMetalNode,
|
|
9
|
+
cloneSheetMetalTree,
|
|
10
|
+
getSheetMetalBaseNode,
|
|
11
|
+
} from "./sheetMetalTree.js";
|
|
12
|
+
|
|
13
|
+
export class SheetMetalObject extends Solid {
|
|
14
|
+
constructor({ tree = null, kFactor = null, thickness = null, bendRadius = null } = {}) {
|
|
15
|
+
super();
|
|
16
|
+
this.tree = tree ? cloneSheetMetalTree(tree) : { version: 1, nodes: [] };
|
|
17
|
+
this.kFactor = kFactor;
|
|
18
|
+
this.thickness = thickness;
|
|
19
|
+
this.bendRadius = bendRadius;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
cloneWithNode(node) {
|
|
23
|
+
const next = new SheetMetalObject({
|
|
24
|
+
tree: appendSheetMetalNode(this.tree, node),
|
|
25
|
+
kFactor: this.kFactor,
|
|
26
|
+
thickness: this.thickness,
|
|
27
|
+
bendRadius: this.bendRadius,
|
|
28
|
+
});
|
|
29
|
+
next.name = this.name;
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
appendNode(node) {
|
|
34
|
+
this.tree = appendSheetMetalNode(this.tree, node);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async generate({
|
|
39
|
+
partHistory = null,
|
|
40
|
+
metadataManager = null,
|
|
41
|
+
mode = "solid",
|
|
42
|
+
flatPatternOptions = null,
|
|
43
|
+
} = {}) {
|
|
44
|
+
const built = await buildSheetMetalSolidFromTree(this.tree, { partHistory });
|
|
45
|
+
this._replaceFromSolid(built);
|
|
46
|
+
|
|
47
|
+
const baseNode = getSheetMetalBaseNode(this.tree);
|
|
48
|
+
const baseParams = baseNode?.params || {};
|
|
49
|
+
const baseType = baseNode?.type === SHEET_METAL_NODE_TYPES.CONTOUR_FLANGE
|
|
50
|
+
? "CONTOUR_FLANGE"
|
|
51
|
+
: "TAB";
|
|
52
|
+
let baseExtra = {};
|
|
53
|
+
if (baseType === "CONTOUR_FLANGE") {
|
|
54
|
+
const signedDistanceRaw = Number(baseParams.signedDistance ?? baseParams.distance);
|
|
55
|
+
const signedDistance = Number.isFinite(signedDistanceRaw) ? signedDistanceRaw : 0;
|
|
56
|
+
const distance = Math.abs(signedDistance);
|
|
57
|
+
baseExtra = {
|
|
58
|
+
signedThickness: baseParams.signedThickness,
|
|
59
|
+
sheetSide: baseParams.sheetSide,
|
|
60
|
+
reverseSheetSide: baseParams.reverseSheetSide,
|
|
61
|
+
signedDistance,
|
|
62
|
+
distance,
|
|
63
|
+
consumePathSketch: baseParams.consumePathSketch,
|
|
64
|
+
pathRefCount: Array.isArray(baseParams.pathRefs) ? baseParams.pathRefs.length : null,
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
baseExtra = {
|
|
68
|
+
placementMode: baseParams.placementMode,
|
|
69
|
+
signedThickness: baseParams.signedThickness,
|
|
70
|
+
consumeProfileSketch: baseParams.consumeProfileSketch,
|
|
71
|
+
profileName: baseParams.profileName,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
applySheetMetalMetadata([this], metadataManager, {
|
|
75
|
+
featureID: baseParams.featureID || null,
|
|
76
|
+
thickness: this.thickness ?? baseParams.thickness ?? null,
|
|
77
|
+
bendRadius: this.bendRadius ?? baseParams.bendRadius ?? null,
|
|
78
|
+
neutralFactor: this.kFactor ?? baseParams.neutralFactor ?? null,
|
|
79
|
+
baseType,
|
|
80
|
+
extra: baseExtra,
|
|
81
|
+
forceBaseOverwrite: true,
|
|
82
|
+
});
|
|
83
|
+
propagateSheetMetalFaceTypesToEdges([this]);
|
|
84
|
+
|
|
85
|
+
this.userData = this.userData || {};
|
|
86
|
+
this.userData.sheetMetalTree = cloneSheetMetalTree(this.tree);
|
|
87
|
+
this.userData.sheetMetalKFactor = this.kFactor;
|
|
88
|
+
|
|
89
|
+
let flat = null;
|
|
90
|
+
if (mode === "flat" || mode === "both") {
|
|
91
|
+
const flatOptions = { ...(flatPatternOptions || {}) };
|
|
92
|
+
if (flatOptions.metadataManager == null && metadataManager) {
|
|
93
|
+
flatOptions.metadataManager = metadataManager;
|
|
94
|
+
}
|
|
95
|
+
if (flatOptions.thickness == null) {
|
|
96
|
+
const thickness = this.thickness ?? baseParams.thickness ?? null;
|
|
97
|
+
if (thickness != null) flatOptions.thickness = thickness;
|
|
98
|
+
}
|
|
99
|
+
if (flatOptions.neutralFactor == null) {
|
|
100
|
+
const neutralFactor = this.kFactor ?? baseParams.neutralFactor ?? null;
|
|
101
|
+
if (neutralFactor != null) flatOptions.neutralFactor = neutralFactor;
|
|
102
|
+
}
|
|
103
|
+
const entries = buildSheetMetalFlatPatternSolids([this], flatOptions);
|
|
104
|
+
flat = Array.isArray(entries) && entries.length ? entries[0] : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (mode === "solid") return { solid: this };
|
|
108
|
+
if (mode === "flat") return { solid: this, flat };
|
|
109
|
+
if (mode === "both") return { solid: this, flat };
|
|
110
|
+
return { solid: this };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_replaceFromSolid(source) {
|
|
114
|
+
this._numProp = source._numProp;
|
|
115
|
+
this._vertProperties = source._vertProperties.slice();
|
|
116
|
+
this._triVerts = source._triVerts.slice();
|
|
117
|
+
this._triIDs = source._triIDs.slice();
|
|
118
|
+
this._vertKeyToIndex = new Map(source._vertKeyToIndex);
|
|
119
|
+
this._faceNameToID = new Map(source._faceNameToID);
|
|
120
|
+
this._idToFaceName = new Map(source._idToFaceName);
|
|
121
|
+
this._faceMetadata = new Map(source._faceMetadata);
|
|
122
|
+
this._edgeMetadata = new Map(source._edgeMetadata);
|
|
123
|
+
this._auxEdges = Array.isArray(source._auxEdges)
|
|
124
|
+
? source._auxEdges.map((edge) => ({
|
|
125
|
+
name: edge?.name,
|
|
126
|
+
closedLoop: !!edge?.closedLoop,
|
|
127
|
+
polylineWorld: !!edge?.polylineWorld,
|
|
128
|
+
materialKey: edge?.materialKey,
|
|
129
|
+
centerline: !!edge?.centerline,
|
|
130
|
+
points: Array.isArray(edge?.points)
|
|
131
|
+
? edge.points.map((p) => (Array.isArray(p) ? [p[0], p[1], p[2]] : p))
|
|
132
|
+
: [],
|
|
133
|
+
}))
|
|
134
|
+
: [];
|
|
135
|
+
this._dirty = true;
|
|
136
|
+
this._manifold = null;
|
|
137
|
+
this._faceIndex = null;
|
|
138
|
+
this.type = "SOLID";
|
|
139
|
+
this.renderOrder = source.renderOrder ?? this.renderOrder;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
import { selectionHasSketch } from "../selectionUtils.js";
|
|
3
|
+
import { normalizeThickness, normalizeBendRadius, normalizeNeutralFactor, applySheetMetalMetadata } from "./sheetMetalMetadata.js";
|
|
4
|
+
import { propagateSheetMetalFaceTypesToEdges } from "./sheetMetalFaceTypes.js";
|
|
5
|
+
import { resolveProfileFace, collectSketchParents } from "./profileUtils.js";
|
|
6
|
+
import { cleanupSheetMetalOppositeEdgeFaces } from "./sheetMetalCleanup.js";
|
|
7
|
+
import { SheetMetalObject } from "./SheetMetalObject.js";
|
|
8
|
+
import { cloneSheetMetalTree, createSheetMetalTree, createSheetMetalTabNode } from "./sheetMetalTree.js";
|
|
9
|
+
import { resolvePlacementMode } from "./sheetMetalTabUtils.js";
|
|
10
|
+
import { cloneProfileGroups, collectProfileEdges } from "./sheetMetalProfileUtils.js";
|
|
11
|
+
|
|
12
|
+
const inputParamsSchema = {
|
|
13
|
+
id: {
|
|
14
|
+
type: "string",
|
|
15
|
+
default_value: null,
|
|
16
|
+
hint: "Unique identifier for the sheet metal tab",
|
|
17
|
+
},
|
|
18
|
+
profile: {
|
|
19
|
+
type: "reference_selection",
|
|
20
|
+
selectionFilter: ["FACE", "SKETCH"],
|
|
21
|
+
multiple: false,
|
|
22
|
+
default_value: null,
|
|
23
|
+
hint: "Closed sketch or face defining the tab footprint",
|
|
24
|
+
},
|
|
25
|
+
thickness: {
|
|
26
|
+
type: "number",
|
|
27
|
+
default_value: 1,
|
|
28
|
+
min: 0,
|
|
29
|
+
hint: "Sheet metal thickness. Also used as the tab extrusion distance.",
|
|
30
|
+
},
|
|
31
|
+
placementMode: {
|
|
32
|
+
type: "options",
|
|
33
|
+
options: ["forward", "reverse", "midplane"],
|
|
34
|
+
default_value: "forward",
|
|
35
|
+
hint: "Controls whether material is added forward, backward, or split about the sketch plane.",
|
|
36
|
+
},
|
|
37
|
+
bendRadius: {
|
|
38
|
+
type: "number",
|
|
39
|
+
default_value: 0.125,
|
|
40
|
+
min: 0,
|
|
41
|
+
hint: "Default bend radius captured with the sheet-metal base feature.",
|
|
42
|
+
},
|
|
43
|
+
neutralFactor: {
|
|
44
|
+
type: "number",
|
|
45
|
+
default_value: 0.5,
|
|
46
|
+
min: 0,
|
|
47
|
+
max: 1,
|
|
48
|
+
step: 0.01,
|
|
49
|
+
hint: "Neutral factor used for flat pattern bend allowance (0-1).",
|
|
50
|
+
},
|
|
51
|
+
consumeProfileSketch: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default_value: true,
|
|
54
|
+
hint: "Remove the referenced sketch after creating the tab. Turn off to keep it in the scene.",
|
|
55
|
+
},
|
|
56
|
+
boolean: {
|
|
57
|
+
type: "boolean_operation",
|
|
58
|
+
default_value: { targets: [], operation: "NONE" },
|
|
59
|
+
hint: "Optional boolean operation with existing solids.",
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export class SheetMetalTabFeature {
|
|
64
|
+
static shortName = "SM.TAB";
|
|
65
|
+
static longName = "Sheet Metal Tab";
|
|
66
|
+
static inputParamsSchema = inputParamsSchema;
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
this.inputParams = {};
|
|
70
|
+
this.persistentData = {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
uiFieldsTest(context) {
|
|
74
|
+
const params = this.inputParams || context?.params || {};
|
|
75
|
+
const partHistory = context?.history || null;
|
|
76
|
+
return selectionHasSketch(params.profile, partHistory) ? [] : ["consumeProfileSketch"];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async run(partHistory) {
|
|
80
|
+
const faceObj = resolveProfileFace(this.inputParams?.profile, partHistory);
|
|
81
|
+
if (!faceObj) {
|
|
82
|
+
throw new Error("Sheet Metal Tab requires a valid FACE or SKETCH selection.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const sketchParentsToRemove = collectSketchParents(faceObj);
|
|
86
|
+
const { magnitude: thicknessAbs, signed: signedThickness } = normalizeThickness(
|
|
87
|
+
this.inputParams?.thickness ?? 1
|
|
88
|
+
);
|
|
89
|
+
const bendRadius = normalizeBendRadius(this.inputParams?.bendRadius ?? 0.125);
|
|
90
|
+
const neutralFactor = normalizeNeutralFactor(this.inputParams?.neutralFactor ?? 0.5);
|
|
91
|
+
const placement = resolvePlacementMode(this.inputParams?.placementMode, signedThickness);
|
|
92
|
+
const sheetMetal = new SheetMetalObject({
|
|
93
|
+
tree: createSheetMetalTree(),
|
|
94
|
+
kFactor: neutralFactor,
|
|
95
|
+
thickness: thicknessAbs,
|
|
96
|
+
bendRadius,
|
|
97
|
+
});
|
|
98
|
+
const profileGroups = cloneProfileGroups(faceObj);
|
|
99
|
+
const profileEdges = collectProfileEdges(faceObj);
|
|
100
|
+
const tabNode = createSheetMetalTabNode({
|
|
101
|
+
featureID: this.inputParams?.featureID || null,
|
|
102
|
+
profileRef: this.inputParams?.profile ?? null,
|
|
103
|
+
profileName: faceObj?.name || null,
|
|
104
|
+
profileGroups,
|
|
105
|
+
profileEdges,
|
|
106
|
+
thickness: thicknessAbs,
|
|
107
|
+
placementMode: placement,
|
|
108
|
+
bendRadius,
|
|
109
|
+
neutralFactor,
|
|
110
|
+
signedThickness,
|
|
111
|
+
consumeProfileSketch: this.inputParams?.consumeProfileSketch !== false,
|
|
112
|
+
});
|
|
113
|
+
sheetMetal.appendNode(tabNode);
|
|
114
|
+
await sheetMetal.generate({
|
|
115
|
+
partHistory,
|
|
116
|
+
metadataManager: partHistory?.metadataManager,
|
|
117
|
+
mode: "solid",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const effects = await BREP.applyBooleanOperation(
|
|
121
|
+
partHistory || {},
|
|
122
|
+
sheetMetal,
|
|
123
|
+
this.inputParams?.boolean,
|
|
124
|
+
this.inputParams?.featureID
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const consumeSketch = this.inputParams?.consumeProfileSketch !== false;
|
|
128
|
+
const removedArtifacts = [
|
|
129
|
+
...(consumeSketch ? sketchParentsToRemove : []),
|
|
130
|
+
...(effects?.removed || []),
|
|
131
|
+
];
|
|
132
|
+
const added = effects?.added || [];
|
|
133
|
+
|
|
134
|
+
cleanupSheetMetalOppositeEdgeFaces(added);
|
|
135
|
+
propagateSheetMetalFaceTypesToEdges(added);
|
|
136
|
+
|
|
137
|
+
applySheetMetalMetadata(added, partHistory?.metadataManager, {
|
|
138
|
+
featureID: this.inputParams?.featureID || null,
|
|
139
|
+
thickness: thicknessAbs,
|
|
140
|
+
bendRadius,
|
|
141
|
+
baseType: "TAB",
|
|
142
|
+
extra: { placementMode: placement, signedThickness, consumeProfileSketch: consumeSketch },
|
|
143
|
+
neutralFactor,
|
|
144
|
+
forceBaseOverwrite: true,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
for (const solid of added) {
|
|
148
|
+
if (!solid) continue;
|
|
149
|
+
solid.userData = solid.userData || {};
|
|
150
|
+
solid.userData.sheetMetalTree = cloneSheetMetalTree(sheetMetal.tree);
|
|
151
|
+
solid.userData.sheetMetalKFactor = neutralFactor;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.persistentData = this.persistentData || {};
|
|
155
|
+
this.persistentData.sheetMetal = {
|
|
156
|
+
baseType: "TAB",
|
|
157
|
+
thickness: thicknessAbs,
|
|
158
|
+
bendRadius,
|
|
159
|
+
neutralFactor,
|
|
160
|
+
placementMode: placement,
|
|
161
|
+
signedThickness,
|
|
162
|
+
consumeProfileSketch: consumeSketch,
|
|
163
|
+
profileName: faceObj?.name || null,
|
|
164
|
+
tree: sheetMetal.tree,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Flag removed parents so history cleans them up
|
|
168
|
+
try {
|
|
169
|
+
for (const obj of removedArtifacts) {
|
|
170
|
+
if (obj) obj.__removeFlag = true;
|
|
171
|
+
}
|
|
172
|
+
} catch { /* flag optional */ }
|
|
173
|
+
|
|
174
|
+
return { added, removed: removedArtifacts };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Sheet Metal Unfolding Requirements (Neutral Surface)
|
|
2
|
+
|
|
3
|
+
This document defines the requirements for unfolding a triangulated sheet metal mesh using a neutral value (neutral factor) so that bend regions are distorted correctly in the resulting flat pattern.
|
|
4
|
+
|
|
5
|
+
## 1) Scope
|
|
6
|
+
|
|
7
|
+
These requirements apply to:
|
|
8
|
+
- The neutral surface mesh construction used for flat pattern generation.
|
|
9
|
+
- The hinge- or triangle-based unfolding steps that transform 3D geometry into a 2D flat pattern.
|
|
10
|
+
- The preview and export paths (SVG/DXF) that use the unfolded mesh.
|
|
11
|
+
|
|
12
|
+
## 2) Definitions
|
|
13
|
+
|
|
14
|
+
- **Thickness (t)**: The sheet metal thickness.
|
|
15
|
+
- **Neutral factor (k)**: A value in [0, 1] representing where the neutral surface lies between the A-side (outer) and B-side (inner) faces.
|
|
16
|
+
- **Neutral surface**: The surface offset from the A or B face by `k * t` (A-side reference) or `(1 - k) * t` (B-side reference), used to preserve material length during flattening.
|
|
17
|
+
- **Bend region**: Faces classified as cylindrical or conical in metadata; these should be unfolded using neutral surface geometry.
|
|
18
|
+
- **Planar region**: Faces classified as planar (A or B), carried through unfold without distortion.
|
|
19
|
+
- **Island**: A disconnected triangle component in the mesh used for unfolding.
|
|
20
|
+
|
|
21
|
+
## 3) Inputs and Preconditions
|
|
22
|
+
|
|
23
|
+
### 3.1 Geometry and Topology
|
|
24
|
+
- The input mesh MUST provide:
|
|
25
|
+
- `vertProperties` (Float32Array or equivalent), `triVerts` (triangulated indices), and `faceID` for each triangle.
|
|
26
|
+
- Consistent face metadata for identifying planar vs bend faces.
|
|
27
|
+
- Triangle winding MUST be coherent across connected triangles. If not, the system MUST correct it or alert.
|
|
28
|
+
- Shared edges MUST be detected across the full mesh. If shared vertices are duplicated or nearly coincident, the system MUST merge or connect them via tolerance rules.
|
|
29
|
+
|
|
30
|
+
### 3.2 Neutral Factor and Thickness
|
|
31
|
+
- The neutral factor MUST be resolved deterministically using metadata, with a default fallback (for example, 0.5) if not provided.
|
|
32
|
+
- Thickness MUST be resolved from sheet metal metadata with a fallback if not provided.
|
|
33
|
+
- The system MUST reject or warn on invalid thickness (<= 0) or a neutral factor outside [0, 1].
|
|
34
|
+
|
|
35
|
+
## 4) Neutral Surface Construction Requirements
|
|
36
|
+
|
|
37
|
+
### 4.1 Face Classification
|
|
38
|
+
- Faces MUST be classified into:
|
|
39
|
+
- A-side planar faces.
|
|
40
|
+
- B-side planar faces.
|
|
41
|
+
- Bend faces (cylindrical or conical).
|
|
42
|
+
- Bend faces MUST be included in the unfolding set if they are required to connect planar faces.
|
|
43
|
+
|
|
44
|
+
### 4.2 Offset Direction and Magnitude
|
|
45
|
+
- For A-side reference, neutral offset MUST be:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
neutral_offset = k * t
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- For B-side reference, neutral offset MUST be:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
neutral_offset = (1 - k) * t
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- The system MUST be consistent about which side is used (A or B) and apply the same rule for planar faces in the unfolding mesh.
|
|
58
|
+
- For cylindrical or conical bend faces, the flat pattern MUST correct bend allowance perpendicular to the bend centerline so that equal bend angles yield equal flat widths regardless of bend direction. The correction MAY be applied as a post-unfold translation of downstream faces rather than by changing the global neutral offset.
|
|
59
|
+
|
|
60
|
+
### 4.3 Vertex Normal Handling
|
|
61
|
+
- Vertex normals used to generate the neutral surface MUST be computed from the unified mesh (shared vertices across all included triangles).
|
|
62
|
+
- Normals MUST be oriented consistently toward the A-side reference used by the offset.
|
|
63
|
+
- If the system detects normals that oppose their neighbors, it MUST flip them or alert.
|
|
64
|
+
|
|
65
|
+
### 4.4 Connectivity (Islands)
|
|
66
|
+
- The neutral surface mesh MUST be connected for a valid single-piece flat pattern.
|
|
67
|
+
- If multiple islands remain after offsetting, the system MUST:
|
|
68
|
+
- Attempt to align islands using edge matching (parallel + overlap) or vertex matching.
|
|
69
|
+
- Weld vertices within a tolerance once alignment occurs.
|
|
70
|
+
- If islands remain, the system MUST alert that the flat pattern will be disconnected.
|
|
71
|
+
|
|
72
|
+
## 5) Unfolding Requirements
|
|
73
|
+
|
|
74
|
+
### 5.1 Hinge Graph Construction
|
|
75
|
+
- Adjacent triangles MUST be detected by shared edges. If shared edges are not exact matches, the system MUST:
|
|
76
|
+
- Use position tolerance to group near-coincident vertices.
|
|
77
|
+
- Or match boundary edges that are parallel and partially overlapping within tolerance.
|
|
78
|
+
- Non-manifold edges (shared by > 2 triangles) MUST be flagged, and the system MUST choose the best pair or alert.
|
|
79
|
+
|
|
80
|
+
### 5.2 Hinge Angle Selection
|
|
81
|
+
- For each shared edge, the system MUST select the hinge rotation that aligns adjacent triangle normals in the same direction (never opposite).
|
|
82
|
+
- The rotation MUST be chosen so that the angle between triangles after unfolding is 180 degrees (flat), not 0 degrees (folded back).
|
|
83
|
+
|
|
84
|
+
### 5.3 Component Unfold Order
|
|
85
|
+
- The system MUST choose a base component (typically the largest area) and unfold adjacent components via BFS/DFS.
|
|
86
|
+
- Every reachable component MUST be transformed using the same hinge graph.
|
|
87
|
+
- If components remain unreachable, the system MUST alert that islands persist.
|
|
88
|
+
|
|
89
|
+
### 5.4 Second-Pass Unfold
|
|
90
|
+
- The system MUST support a second unfold pass using the already-unfolded mesh to reduce residual bends.
|
|
91
|
+
- The second pass MUST recompute hinge data from the mostly-flat geometry and re-apply the full unfolding sequence.
|
|
92
|
+
|
|
93
|
+
## 6) 2D Placement Requirements
|
|
94
|
+
|
|
95
|
+
### 6.1 Distance Preservation
|
|
96
|
+
- The unfolded mesh MUST preserve edge lengths from the neutral surface (within tolerance) for all triangles.
|
|
97
|
+
- Distortion beyond tolerance MUST be reported.
|
|
98
|
+
|
|
99
|
+
### 6.2 Planarity
|
|
100
|
+
- The final unfolded mesh MUST be coplanar. All vertices MUST lie on a single plane within tolerance.
|
|
101
|
+
- If planarity fails after the second pass, the system MUST force a planar projection of vertices and alert.
|
|
102
|
+
|
|
103
|
+
### 6.3 Island Handling
|
|
104
|
+
- If the unfolded mesh still contains multiple islands, the system MUST:
|
|
105
|
+
- Alert the user with the island count.
|
|
106
|
+
- Continue generating the flat pattern unless the user explicitly requests a hard stop.
|
|
107
|
+
|
|
108
|
+
## 7) Output Requirements
|
|
109
|
+
|
|
110
|
+
- The flat pattern mesh MUST include:
|
|
111
|
+
- `vertProperties` (2D positions, Z = 0 or planar),
|
|
112
|
+
- `triVerts` (indices),
|
|
113
|
+
- `triFaces` (face ids),
|
|
114
|
+
- `thickness`, `faceMetaById`, `faceNameById`.
|
|
115
|
+
- Debug artifacts MUST include:
|
|
116
|
+
- Placement order,
|
|
117
|
+
- Unfold steps or hinge steps,
|
|
118
|
+
- Island count.
|
|
119
|
+
|
|
120
|
+
## 8) Validation and Error Reporting
|
|
121
|
+
|
|
122
|
+
### 8.1 Required Checks
|
|
123
|
+
- **Triangle winding**: Must be coherent across all connected triangles.
|
|
124
|
+
- **Islands**: Component count MUST be computed and reported.
|
|
125
|
+
- **Planarity**: Max distance from plane MUST be computed and compared to a tolerance.
|
|
126
|
+
|
|
127
|
+
### 8.2 Tolerances
|
|
128
|
+
- A size-based tolerance MUST be used for geometric comparisons, for example:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
edge_tol = max(1e-5, diag * 1e-5)
|
|
132
|
+
planar_tol = max(1e-6, diag * 1e-5)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Where `diag` is the bounding box diagonal of the working mesh.
|
|
136
|
+
|
|
137
|
+
### 8.3 Alerts
|
|
138
|
+
- The system MUST alert when:
|
|
139
|
+
- Islands remain after unfolding (include island count).
|
|
140
|
+
- The final mesh is not planar after the second pass (include max deviation).
|
|
141
|
+
|
|
142
|
+
## 9) Acceptance Criteria
|
|
143
|
+
|
|
144
|
+
The unfolding implementation is considered correct if:
|
|
145
|
+
- All triangles are coplanar within tolerance after the second pass or forced planarization.
|
|
146
|
+
- The neutral surface offset produces correct bend allowance (edge lengths on the neutral surface are preserved).
|
|
147
|
+
- Islands are reported clearly if they remain.
|
|
148
|
+
- SVG/DXF exports represent the same unfolded topology as the preview.
|
|
149
|
+
|
|
150
|
+
## 10) Non-Goals
|
|
151
|
+
|
|
152
|
+
- Exact manufacturing simulation of material spring-back or stretching beyond neutral surface modeling.
|
|
153
|
+
- Automatic resolution of topological errors beyond island alignment and vertex welding.
|