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,218 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
const THREE = BREP.THREE;
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_RESOLUTION = 24;
|
|
5
|
+
|
|
6
|
+
const ensurePoint = (point, fallbackId, fallbackPosition, index = 0) => {
|
|
7
|
+
const positionSource = Array.isArray(point?.position)
|
|
8
|
+
? point.position
|
|
9
|
+
: Array.isArray(fallbackPosition)
|
|
10
|
+
? fallbackPosition
|
|
11
|
+
: [0, 0, 0];
|
|
12
|
+
const position = [
|
|
13
|
+
Number(positionSource[0]) || 0,
|
|
14
|
+
Number(positionSource[1]) || 0,
|
|
15
|
+
Number(positionSource[2]) || 0,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Ensure forward and backward extension distances
|
|
19
|
+
const forwardDistance = typeof point?.forwardDistance === "number"
|
|
20
|
+
? Math.max(0, Number(point.forwardDistance))
|
|
21
|
+
: 1.0; // Default forward distance
|
|
22
|
+
|
|
23
|
+
const backwardDistance = typeof point?.backwardDistance === "number"
|
|
24
|
+
? Math.max(0, Number(point.backwardDistance))
|
|
25
|
+
: 1.0; // Default backward distance
|
|
26
|
+
|
|
27
|
+
const flipDirection = typeof point?.flipDirection === "boolean"
|
|
28
|
+
? point.flipDirection
|
|
29
|
+
: false;
|
|
30
|
+
|
|
31
|
+
// Store complete transformation matrix (position + rotation)
|
|
32
|
+
// Default to identity rotation (pointing along X-axis)
|
|
33
|
+
const rotation = Array.isArray(point?.rotation) && point.rotation.length === 9
|
|
34
|
+
? point.rotation.slice() // Copy existing rotation matrix
|
|
35
|
+
: [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Default identity rotation (X-axis forward)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id: String(point?.id ?? fallbackId ?? `pt-${Math.random().toString(36).slice(2)}`),
|
|
39
|
+
position,
|
|
40
|
+
rotation, // 3x3 rotation matrix stored as flat array
|
|
41
|
+
forwardDistance,
|
|
42
|
+
backwardDistance,
|
|
43
|
+
flipDirection,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function normalizeSplineData(rawSpline) {
|
|
48
|
+
let spline = rawSpline;
|
|
49
|
+
if (!spline || typeof spline !== "object") {
|
|
50
|
+
spline = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let points = Array.isArray(spline?.points) ? spline.points : null;
|
|
54
|
+
if (!points || points.length < 2) {
|
|
55
|
+
points = [
|
|
56
|
+
{
|
|
57
|
+
id: "p0",
|
|
58
|
+
position: [0, 0, 0],
|
|
59
|
+
rotation: [1, 0, 0, 0, 1, 0, 0, 0, 1], // Identity matrix
|
|
60
|
+
forwardDistance: 1.0,
|
|
61
|
+
backwardDistance: 1.0,
|
|
62
|
+
flipDirection: false
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "p1",
|
|
66
|
+
position: [5, 0, 0],
|
|
67
|
+
rotation: [1, 0, 0, 0, 1, 0, 0, 0, 1], // Identity matrix
|
|
68
|
+
forwardDistance: 1.0,
|
|
69
|
+
backwardDistance: 1.0,
|
|
70
|
+
flipDirection: false
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const normalizedPoints = points.map((pt, index) =>
|
|
76
|
+
ensurePoint(pt, `p${index}`, null, index)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
points: normalizedPoints,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const hermitePoint = (p0, p1, t0, t1, t) => {
|
|
85
|
+
const t2 = t * t;
|
|
86
|
+
const t3 = t2 * t;
|
|
87
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
88
|
+
const h10 = t3 - 2 * t2 + t;
|
|
89
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
90
|
+
const h11 = t3 - t2;
|
|
91
|
+
|
|
92
|
+
const out = new THREE.Vector3();
|
|
93
|
+
out
|
|
94
|
+
.addScaledVector(p0, h00)
|
|
95
|
+
.addScaledVector(t0, h10)
|
|
96
|
+
.addScaledVector(p1, h01)
|
|
97
|
+
.addScaledVector(t1, h11);
|
|
98
|
+
return out;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export function buildHermitePolyline(spline, resolution = DEFAULT_RESOLUTION, bendRadius = 1.0) {
|
|
102
|
+
const pointsData = Array.isArray(spline?.points) ? spline.points : [];
|
|
103
|
+
|
|
104
|
+
if (pointsData.length < 2) {
|
|
105
|
+
return { positions: [], polyline: [] };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const samplesPerSegment = Math.max(4, Math.floor(resolution));
|
|
109
|
+
const positions = [];
|
|
110
|
+
const polyline = [];
|
|
111
|
+
|
|
112
|
+
// Clamp bend radius to reasonable range
|
|
113
|
+
const clampedBendRadius = Math.max(0.1, Math.min(5.0, Number(bendRadius) || 1.0));
|
|
114
|
+
|
|
115
|
+
// Helper function to calculate extension point
|
|
116
|
+
const calculateExtensionPoint = (anchor, pointData, isForward) => {
|
|
117
|
+
const rotation = pointData.rotation || [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
|
118
|
+
let direction = new THREE.Vector3(rotation[0], rotation[1], rotation[2]);
|
|
119
|
+
|
|
120
|
+
if (pointData.flipDirection) {
|
|
121
|
+
direction.multiplyScalar(-1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const distance = isForward ? pointData.forwardDistance : pointData.backwardDistance;
|
|
125
|
+
const extensionDir = isForward ? direction : direction.clone().multiplyScalar(-1);
|
|
126
|
+
|
|
127
|
+
return anchor.clone().add(extensionDir.multiplyScalar(distance));
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Helper function to add straight line segment with specified number of samples
|
|
131
|
+
const addStraightSegment = (start, end, samples) => {
|
|
132
|
+
for (let i = 0; i <= samples; i++) {
|
|
133
|
+
const t = i / samples;
|
|
134
|
+
const point = start.clone().lerp(end, t);
|
|
135
|
+
positions.push(point.x, point.y, point.z);
|
|
136
|
+
polyline.push([point.x, point.y, point.z]);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Convert control points to THREE.Vector3
|
|
141
|
+
const anchors = pointsData.map(
|
|
142
|
+
(pt) =>
|
|
143
|
+
new THREE.Vector3(
|
|
144
|
+
Number(pt.position?.[0]) || 0,
|
|
145
|
+
Number(pt.position?.[1]) || 0,
|
|
146
|
+
Number(pt.position?.[2]) || 0,
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Build the complete spline with straight and curved segments
|
|
151
|
+
for (let i = 0; i < pointsData.length - 1; i++) {
|
|
152
|
+
const currentAnchor = anchors[i];
|
|
153
|
+
const nextAnchor = anchors[i + 1];
|
|
154
|
+
const currentData = pointsData[i];
|
|
155
|
+
const nextData = pointsData[i + 1];
|
|
156
|
+
|
|
157
|
+
// Calculate extension points
|
|
158
|
+
const currentForwardExt = calculateExtensionPoint(currentAnchor, currentData, true);
|
|
159
|
+
const nextBackwardExt = calculateExtensionPoint(nextAnchor, nextData, false);
|
|
160
|
+
|
|
161
|
+
// Use minimal samples for straight segments and maximum for curves
|
|
162
|
+
const straightSamples = Math.max(1, Math.floor(samplesPerSegment * 0.15)); // Only 15% for straight parts
|
|
163
|
+
const curvedSamples = Math.max(6, samplesPerSegment - (2 * straightSamples)); // Much more samples for smoother curves
|
|
164
|
+
|
|
165
|
+
// Segment 1: Straight line from current anchor to its forward extension point
|
|
166
|
+
if (i === 0) {
|
|
167
|
+
// For the first segment, include the starting point
|
|
168
|
+
addStraightSegment(currentAnchor, currentForwardExt, straightSamples);
|
|
169
|
+
} else {
|
|
170
|
+
// Skip the first point to avoid duplication
|
|
171
|
+
for (let j = 1; j <= straightSamples; j++) {
|
|
172
|
+
const t = j / straightSamples;
|
|
173
|
+
const point = currentAnchor.clone().lerp(currentForwardExt, t);
|
|
174
|
+
positions.push(point.x, point.y, point.z);
|
|
175
|
+
polyline.push([point.x, point.y, point.z]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Segment 2: Curved hermite interpolation from current forward extension to next backward extension
|
|
180
|
+
// Create tangent vectors for the curved segment
|
|
181
|
+
const currentExtDirection = currentForwardExt.clone().sub(currentAnchor).normalize();
|
|
182
|
+
const nextExtDirection = nextAnchor.clone().sub(nextBackwardExt).normalize();
|
|
183
|
+
|
|
184
|
+
// Calculate tangent scale based on user-controlled bend radius
|
|
185
|
+
const extDistance = currentForwardExt.distanceTo(nextBackwardExt);
|
|
186
|
+
const avgExtDistance = (currentData.forwardDistance + nextData.backwardDistance) * 0.5;
|
|
187
|
+
|
|
188
|
+
// Use the bend radius to control curve smoothness
|
|
189
|
+
// Lower bend radius = tighter curves, higher bend radius = smoother curves
|
|
190
|
+
const baseScale = Math.max(extDistance * 0.3, avgExtDistance * 0.5); // Much more conservative base
|
|
191
|
+
const tangentScale = baseScale * clampedBendRadius;
|
|
192
|
+
|
|
193
|
+
const t0 = currentExtDirection.multiplyScalar(tangentScale);
|
|
194
|
+
const t1 = nextExtDirection.multiplyScalar(tangentScale);
|
|
195
|
+
|
|
196
|
+
// Add the curved segment (skip first point to avoid duplication)
|
|
197
|
+
for (let j = 1; j <= curvedSamples; j++) {
|
|
198
|
+
const t = j / curvedSamples;
|
|
199
|
+
const point = hermitePoint(currentForwardExt, nextBackwardExt, t0, t1, t);
|
|
200
|
+
positions.push(point.x, point.y, point.z);
|
|
201
|
+
polyline.push([point.x, point.y, point.z]);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Segment 3: Straight line from next backward extension to next anchor
|
|
205
|
+
for (let j = 1; j <= straightSamples; j++) {
|
|
206
|
+
const t = j / straightSamples;
|
|
207
|
+
const point = nextBackwardExt.clone().lerp(nextAnchor, t);
|
|
208
|
+
positions.push(point.x, point.y, point.z);
|
|
209
|
+
polyline.push([point.x, point.y, point.z]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { positions, polyline };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function cloneSplineData(spline) {
|
|
217
|
+
return JSON.parse(JSON.stringify(spline || null));
|
|
218
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
import { selectionHasSketch } from "../selectionUtils.js";
|
|
3
|
+
|
|
4
|
+
const inputParamsSchema = {
|
|
5
|
+
id: {
|
|
6
|
+
type: "string",
|
|
7
|
+
default_value: null,
|
|
8
|
+
hint: "unique identifier for the sweep feature",
|
|
9
|
+
},
|
|
10
|
+
profile: {
|
|
11
|
+
type: "reference_selection",
|
|
12
|
+
selectionFilter: ["SKETCH", "FACE"],
|
|
13
|
+
multiple: false,
|
|
14
|
+
default_value: null,
|
|
15
|
+
hint: "Select the profile to sweep",
|
|
16
|
+
},
|
|
17
|
+
consumeProfileSketch: {
|
|
18
|
+
type: "boolean",
|
|
19
|
+
default_value: true,
|
|
20
|
+
hint: "Remove the referenced sketch after creating the sweep. Turn off to keep it in the scene.",
|
|
21
|
+
},
|
|
22
|
+
path: {
|
|
23
|
+
type: "reference_selection",
|
|
24
|
+
selectionFilter: ["EDGE"],
|
|
25
|
+
multiple: true,
|
|
26
|
+
default_value: null,
|
|
27
|
+
hint: "Select one or more edges to define the sweep path (connected edges are chained)",
|
|
28
|
+
},
|
|
29
|
+
orientationMode: {
|
|
30
|
+
type: "options",
|
|
31
|
+
options: ["translate", "pathAlign"],
|
|
32
|
+
default_value: "translate",
|
|
33
|
+
hint: "Sweep orientation mode: 'translate' (fixed) or 'pathAlign' (profile aligns and rotates with path)",
|
|
34
|
+
},
|
|
35
|
+
twistAngle: {
|
|
36
|
+
type: "number",
|
|
37
|
+
default_value: 0,
|
|
38
|
+
hint: "Twist angle for the sweep",
|
|
39
|
+
},
|
|
40
|
+
boolean: {
|
|
41
|
+
type: "boolean_operation",
|
|
42
|
+
default_value: { targets: [], operation: 'NONE' },
|
|
43
|
+
hint: "Optional boolean operation with selected solids"
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export class SweepFeature {
|
|
48
|
+
static shortName = "SW";
|
|
49
|
+
static longName = "Sweep";
|
|
50
|
+
static inputParamsSchema = inputParamsSchema;
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
this.inputParams = {};
|
|
54
|
+
this.persistentData = {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
uiFieldsTest(context) {
|
|
58
|
+
const params = this.inputParams || context?.params || {};
|
|
59
|
+
const partHistory = context?.history || null;
|
|
60
|
+
return selectionHasSketch(params.profile, partHistory) ? [] : ["consumeProfileSketch"];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async run(partHistory) {
|
|
64
|
+
// actual code to create the sweep feature.
|
|
65
|
+
const { profile, path, twistAngle, orientationMode } = this.inputParams;
|
|
66
|
+
|
|
67
|
+
// Require a valid path edge; sweep now only follows a path
|
|
68
|
+
const pathArr = Array.isArray(path) ? path.filter(Boolean) : (path ? [path] : []);
|
|
69
|
+
if (!pathArr.length) {
|
|
70
|
+
throw new Error('Sweep requires a path edge selection. Please select an EDGE to sweep along.');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Resolve profile object: accept FACE or SKETCH group object
|
|
74
|
+
const obj = Array.isArray(profile) ? (profile[0] || null) : (profile || null);
|
|
75
|
+
let faceObj = obj;
|
|
76
|
+
if (obj && obj.type === 'SKETCH') {
|
|
77
|
+
faceObj = obj.children.find(ch => ch.type === 'FACE') || obj.children.find(ch => ch.userData?.faceName);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const removed = [];
|
|
81
|
+
// if the face is a child of a sketch we need to remove the sketch from the scene
|
|
82
|
+
const consumeSketch = this.inputParams?.consumeProfileSketch !== false;
|
|
83
|
+
if (consumeSketch && faceObj && faceObj.type === 'FACE' && faceObj.parent && faceObj.parent.type === 'SKETCH') {
|
|
84
|
+
removed.push(faceObj.parent);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const twistAngleNum = Number(twistAngle);
|
|
88
|
+
|
|
89
|
+
// Create the sweep solid
|
|
90
|
+
const sweep = new BREP.Sweep({
|
|
91
|
+
face: faceObj,
|
|
92
|
+
sweepPathEdges: pathArr,
|
|
93
|
+
mode: (orientationMode === 'pathAlign') ? 'pathAlign' : 'translate',
|
|
94
|
+
twistAngle: Number.isFinite(twistAngleNum) ? twistAngleNum : 0,
|
|
95
|
+
name: this.inputParams.featureID
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
sweep.collapseTinyTriangles(0.1);
|
|
99
|
+
sweep.simplify(0.1);
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// Build and show the solid. Let errors surface so we can debug if needed.
|
|
103
|
+
sweep.visualize();
|
|
104
|
+
|
|
105
|
+
// Apply optional boolean operation via shared helper
|
|
106
|
+
const effects = await BREP.applyBooleanOperation(partHistory || {}, sweep, this.inputParams.boolean, this.inputParams.featureID);
|
|
107
|
+
effects.removed = [...removed, ...effects.removed];
|
|
108
|
+
return effects;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
const THREE = BREP.THREE;
|
|
3
|
+
|
|
4
|
+
const inputParamsSchema = {
|
|
5
|
+
id: {
|
|
6
|
+
type: "string",
|
|
7
|
+
default_value: null,
|
|
8
|
+
hint: "unique identifier for the loft feature",
|
|
9
|
+
},
|
|
10
|
+
solids: {
|
|
11
|
+
type: "reference_selection",
|
|
12
|
+
selectionFilter: ["SOLID"],
|
|
13
|
+
multiple: true,
|
|
14
|
+
default_value: [],
|
|
15
|
+
hint: "Select solids to transform",
|
|
16
|
+
},
|
|
17
|
+
space: {
|
|
18
|
+
type: "options",
|
|
19
|
+
options: ["WORLD", "LOCAL"],
|
|
20
|
+
default_value: "WORLD",
|
|
21
|
+
hint: "Interpret translation/rotation in WORLD or LOCAL space",
|
|
22
|
+
},
|
|
23
|
+
pivot: {
|
|
24
|
+
type: "options",
|
|
25
|
+
options: ["ORIGIN", "BBOX_CENTER"],
|
|
26
|
+
default_value: "ORIGIN",
|
|
27
|
+
hint: "Pivot point for rotation/scale",
|
|
28
|
+
},
|
|
29
|
+
translate: {
|
|
30
|
+
type: "vec3",
|
|
31
|
+
default_value: [0, 0, 0],
|
|
32
|
+
hint: "Translation [x,y,z]",
|
|
33
|
+
},
|
|
34
|
+
rotateEulerDeg: {
|
|
35
|
+
type: "vec3",
|
|
36
|
+
default_value: [0, 0, 0],
|
|
37
|
+
hint: "Rotation in degrees [rx,ry,rz] about pivot (XYZ order)",
|
|
38
|
+
},
|
|
39
|
+
scale: {
|
|
40
|
+
type: "vec3",
|
|
41
|
+
default_value: [1, 1, 1],
|
|
42
|
+
hint: "Non-uniform scale [sx,sy,sz] about pivot",
|
|
43
|
+
step: 0.1,
|
|
44
|
+
uniformToggle: true,
|
|
45
|
+
uniformDefault: true,
|
|
46
|
+
uniformLockLabel: 'Uniform scale',
|
|
47
|
+
},
|
|
48
|
+
copy: {
|
|
49
|
+
type: "boolean",
|
|
50
|
+
default_value: false,
|
|
51
|
+
label: "Copy",
|
|
52
|
+
hint: "Create a new transformed copy; keep the original",
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export class TransformFeature {
|
|
57
|
+
static shortName = "XFORM";
|
|
58
|
+
static longName = "Transform";
|
|
59
|
+
static inputParamsSchema = inputParamsSchema;
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
this.inputParams = {};
|
|
63
|
+
this.persistentData = {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async run(partHistory) {
|
|
67
|
+
const solids = Array.isArray(this.inputParams.solids) ? this.inputParams.solids.filter(s => s && s.type === 'SOLID') : [];
|
|
68
|
+
if (!solids.length) return { added: [], removed: [] };
|
|
69
|
+
|
|
70
|
+
const space = (this.inputParams.space || 'WORLD').toUpperCase();
|
|
71
|
+
const pivot = (this.inputParams.pivot || 'ORIGIN').toUpperCase();
|
|
72
|
+
const t = toVec3(this.inputParams.translate, 0, 0, 0);
|
|
73
|
+
const rDeg = toVec3(this.inputParams.rotateEulerDeg, 0, 0, 0);
|
|
74
|
+
const s = toVec3(this.inputParams.scale, 1, 1, 1);
|
|
75
|
+
const copy = !!this.inputParams.copy;
|
|
76
|
+
const replace = !copy;
|
|
77
|
+
|
|
78
|
+
const out = [];
|
|
79
|
+
const removed = [];
|
|
80
|
+
for (const src of solids) {
|
|
81
|
+
const dst = src.clone();
|
|
82
|
+
|
|
83
|
+
// Compute pivot point in world
|
|
84
|
+
const pivotPoint = (pivot === 'BBOX_CENTER') ? bboxCenterWorld(src) : new THREE.Vector3(0, 0, 0);
|
|
85
|
+
|
|
86
|
+
// Build rotation and scale in requested space
|
|
87
|
+
const eulerRad = new THREE.Euler(
|
|
88
|
+
THREE.MathUtils.degToRad(rDeg.x),
|
|
89
|
+
THREE.MathUtils.degToRad(rDeg.y),
|
|
90
|
+
THREE.MathUtils.degToRad(rDeg.z),
|
|
91
|
+
'XYZ'
|
|
92
|
+
);
|
|
93
|
+
const quat = new THREE.Quaternion().setFromEuler(eulerRad);
|
|
94
|
+
const scl = new THREE.Vector3(s.x, s.y, s.z);
|
|
95
|
+
|
|
96
|
+
// Construct matrix with pivot: T * Tr(pivot) * R * S * Tr(-pivot)
|
|
97
|
+
const M = new THREE.Matrix4();
|
|
98
|
+
const Tpivot = new THREE.Matrix4().makeTranslation(pivotPoint.x, pivotPoint.y, pivotPoint.z);
|
|
99
|
+
const Tnp = new THREE.Matrix4().makeTranslation(-pivotPoint.x, -pivotPoint.y, -pivotPoint.z);
|
|
100
|
+
const RS = new THREE.Matrix4().compose(new THREE.Vector3(0, 0, 0), quat, scl);
|
|
101
|
+
const Tmove = new THREE.Matrix4().makeTranslation(t.x, t.y, t.z);
|
|
102
|
+
|
|
103
|
+
if (space === 'LOCAL') {
|
|
104
|
+
// Translate in local space of src: convert local move to world by applying src's world matrix
|
|
105
|
+
const localDir = new THREE.Vector3(t.x, t.y, t.z).applyMatrix3(new THREE.Matrix3().setFromMatrix4(src.matrixWorld));
|
|
106
|
+
Tmove.setPosition(localDir);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
M.multiply(Tmove).multiply(Tpivot).multiply(RS).multiply(Tnp);
|
|
110
|
+
|
|
111
|
+
dst.bakeTransform(M); // bake into geometry arrays
|
|
112
|
+
if (replace) {
|
|
113
|
+
// Keep the original name when replacing
|
|
114
|
+
dst.name = src.name || dst.name || 'Solid';
|
|
115
|
+
} else {
|
|
116
|
+
// Name copies with _COPY suffix
|
|
117
|
+
const base = src.name || 'Solid';
|
|
118
|
+
dst.name = `${base}_${this.inputParams.id}`;
|
|
119
|
+
}
|
|
120
|
+
dst.visualize();
|
|
121
|
+
|
|
122
|
+
if (replace) {
|
|
123
|
+
try { src.__removeFlag = true; } catch {}
|
|
124
|
+
removed.push(src);
|
|
125
|
+
}
|
|
126
|
+
out.push(dst);
|
|
127
|
+
}
|
|
128
|
+
return { added: out, removed };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function toVec3(v, dx, dy, dz) {
|
|
133
|
+
if (Array.isArray(v)) return new THREE.Vector3(v[0] ?? dx, v[1] ?? dy, v[2] ?? dz);
|
|
134
|
+
if (v && typeof v === 'object') return new THREE.Vector3(v.x ?? dx, v.y ?? dy, v.z ?? dz);
|
|
135
|
+
return new THREE.Vector3(dx, dy, dz);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function bboxCenterWorld(solid) {
|
|
139
|
+
const mesh = solid.getMesh();
|
|
140
|
+
try {
|
|
141
|
+
const vp = mesh.vertProperties;
|
|
142
|
+
if (!vp || vp.length < 3) return new THREE.Vector3(0, 0, 0);
|
|
143
|
+
const bbMin = new THREE.Vector3(+Infinity, +Infinity, +Infinity);
|
|
144
|
+
const bbMax = new THREE.Vector3(-Infinity, -Infinity, -Infinity);
|
|
145
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
146
|
+
const x = vp[i + 0], y = vp[i + 1], z = vp[i + 2];
|
|
147
|
+
bbMin.x = Math.min(bbMin.x, x); bbMin.y = Math.min(bbMin.y, y); bbMin.z = Math.min(bbMin.z, z);
|
|
148
|
+
bbMax.x = Math.max(bbMax.x, x); bbMax.y = Math.max(bbMax.y, y); bbMax.z = Math.max(bbMax.z, z);
|
|
149
|
+
}
|
|
150
|
+
return bbMin.add(bbMax).multiplyScalar(0.5);
|
|
151
|
+
} finally { try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch {} }
|
|
152
|
+
}
|