fluidcad 0.0.35 → 0.0.37
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.txt +21 -504
- package/README.md +1 -1
- package/bin/commands/login.js +33 -5
- package/bin/commands/mcp.js +3 -2
- package/bin/commands/publish.js +103 -8
- package/bin/lib/api-client.js +8 -0
- package/bin/lib/model-config.js +27 -4
- package/bin/lib/prompt.js +97 -0
- package/lib/dist/common/edge.d.ts +1 -1
- package/lib/dist/common/face.d.ts +1 -1
- package/lib/dist/common/scene-object.d.ts +6 -0
- package/lib/dist/common/scene-object.js +8 -0
- package/lib/dist/common/shape-factory.d.ts +1 -1
- package/lib/dist/common/shape-history-tracker.d.ts +1 -1
- package/lib/dist/common/shape.d.ts +1 -1
- package/lib/dist/common/solid.d.ts +1 -1
- package/lib/dist/common/transformable-primitive.d.ts +12 -1
- package/lib/dist/common/transformable-primitive.js +27 -0
- package/lib/dist/common/vertex.d.ts +1 -1
- package/lib/dist/common/wire.d.ts +1 -1
- package/lib/dist/core/2d/index.d.ts +1 -0
- package/lib/dist/core/2d/index.js +1 -0
- package/lib/dist/core/2d/text.d.ts +30 -0
- package/lib/dist/core/2d/text.js +37 -0
- package/lib/dist/core/helix.d.ts +20 -0
- package/lib/dist/core/helix.js +36 -0
- package/lib/dist/core/index.d.ts +3 -1
- package/lib/dist/core/index.js +2 -0
- package/lib/dist/core/interfaces.d.ts +180 -0
- package/lib/dist/core/wrap.d.ts +17 -0
- package/lib/dist/core/wrap.js +39 -0
- package/lib/dist/features/2d/text.d.ts +67 -0
- package/lib/dist/features/2d/text.js +320 -0
- package/lib/dist/features/cylinder.d.ts +3 -1
- package/lib/dist/features/cylinder.js +5 -2
- package/lib/dist/features/extrude-base.d.ts +1 -0
- package/lib/dist/features/extrude-to-face.d.ts +1 -0
- package/lib/dist/features/extrude-to-face.js +6 -0
- package/lib/dist/features/fillet.d.ts +1 -1
- package/lib/dist/features/helix.d.ts +41 -0
- package/lib/dist/features/helix.js +337 -0
- package/lib/dist/features/select.js +32 -8
- package/lib/dist/features/simple-extruder.d.ts +1 -1
- package/lib/dist/features/simple-extruder.js +7 -2
- package/lib/dist/features/sphere.d.ts +3 -1
- package/lib/dist/features/sphere.js +5 -2
- package/lib/dist/features/sweep.js +7 -2
- package/lib/dist/features/wrap.d.ts +39 -0
- package/lib/dist/features/wrap.js +116 -0
- package/lib/dist/filters/edge/belongs-to-face.d.ts +3 -1
- package/lib/dist/filters/edge/belongs-to-face.js +14 -10
- package/lib/dist/filters/filter.d.ts +1 -1
- package/lib/dist/filters/from-object.d.ts +1 -1
- package/lib/dist/filters/tangent-expander.d.ts +1 -1
- package/lib/dist/filters/tangent-expander.js +57 -40
- package/lib/dist/helpers/scene-helpers.d.ts +2 -0
- package/lib/dist/helpers/scene-helpers.js +1 -1
- package/lib/dist/index.d.ts +2 -0
- package/lib/dist/index.js +3 -1
- package/lib/dist/io/file-import.d.ts +7 -0
- package/lib/dist/io/file-import.js +28 -1
- package/lib/dist/io/font-registry.d.ts +45 -0
- package/lib/dist/io/font-registry.js +272 -0
- package/lib/dist/math/bspline-interpolation.d.ts +29 -0
- package/lib/dist/math/bspline-interpolation.js +194 -0
- package/lib/dist/oc/boolean-ops.d.ts +3 -1
- package/lib/dist/oc/boolean-ops.js +15 -1
- package/lib/dist/oc/color-transfer.d.ts +1 -1
- package/lib/dist/oc/constraints/constraint-helpers.d.ts +4 -4
- package/lib/dist/oc/constraints/curve/tangent-circle-solver.js +10 -9
- package/lib/dist/oc/constraints/curve/tangent-line-solver.js +5 -6
- package/lib/dist/oc/convert.d.ts +1 -1
- package/lib/dist/oc/draft-ops.d.ts +1 -1
- package/lib/dist/oc/edge-ops.d.ts +2 -2
- package/lib/dist/oc/edge-ops.js +13 -14
- package/lib/dist/oc/edge-props.d.ts +1 -1
- package/lib/dist/oc/edge-query.d.ts +1 -1
- package/lib/dist/oc/edge-query.js +3 -8
- package/lib/dist/oc/errors.d.ts +8 -0
- package/lib/dist/oc/errors.js +27 -0
- package/lib/dist/oc/explorer.d.ts +2 -2
- package/lib/dist/oc/extrude-ops.d.ts +28 -2
- package/lib/dist/oc/extrude-ops.js +56 -7
- package/lib/dist/oc/face-ops.d.ts +2 -1
- package/lib/dist/oc/face-ops.js +11 -0
- package/lib/dist/oc/face-props.d.ts +1 -1
- package/lib/dist/oc/face-query.d.ts +12 -1
- package/lib/dist/oc/face-query.js +39 -0
- package/lib/dist/oc/fillet-ops.d.ts +1 -1
- package/lib/dist/oc/fillet-ops.js +4 -4
- package/lib/dist/oc/geometry.d.ts +1 -1
- package/lib/dist/oc/geometry.js +12 -14
- package/lib/dist/oc/helix-ops.d.ts +37 -0
- package/lib/dist/oc/helix-ops.js +88 -0
- package/lib/dist/oc/hit-test.d.ts +1 -1
- package/lib/dist/oc/index.d.ts +4 -0
- package/lib/dist/oc/index.js +2 -0
- package/lib/dist/oc/init.d.ts +1 -1
- package/lib/dist/oc/init.js +1 -1
- package/lib/dist/oc/intersection.js +1 -1
- package/lib/dist/oc/io.d.ts +6 -6
- package/lib/dist/oc/io.js +31 -24
- package/lib/dist/oc/measure/classify.d.ts +34 -0
- package/lib/dist/oc/measure/classify.js +246 -0
- package/lib/dist/oc/measure/measure-ops.d.ts +9 -0
- package/lib/dist/oc/measure/measure-ops.js +210 -0
- package/lib/dist/oc/measure/measure-types.d.ts +39 -0
- package/lib/dist/oc/measure/measure-types.js +1 -0
- package/lib/dist/oc/measure/sampling.d.ts +9 -0
- package/lib/dist/oc/measure/sampling.js +77 -0
- package/lib/dist/oc/measure/vec.d.ts +13 -0
- package/lib/dist/oc/measure/vec.js +23 -0
- package/lib/dist/oc/mesh.d.ts +1 -1
- package/lib/dist/oc/mesh.js +40 -28
- package/lib/dist/oc/path-sampler.d.ts +29 -0
- package/lib/dist/oc/path-sampler.js +63 -0
- package/lib/dist/oc/props.d.ts +1 -1
- package/lib/dist/oc/props.js +4 -1
- package/lib/dist/oc/shape-hash.d.ts +26 -0
- package/lib/dist/oc/shape-hash.js +32 -0
- package/lib/dist/oc/shape-ops.d.ts +5 -3
- package/lib/dist/oc/shape-ops.js +6 -5
- package/lib/dist/oc/sweep-ops.d.ts +22 -1
- package/lib/dist/oc/sweep-ops.js +206 -18
- package/lib/dist/oc/text-outline.d.ts +62 -0
- package/lib/dist/oc/text-outline.js +212 -0
- package/lib/dist/oc/topology-index.d.ts +1 -1
- package/lib/dist/oc/vertex-ops.d.ts +1 -1
- package/lib/dist/oc/wire-ops.d.ts +1 -1
- package/lib/dist/oc/wire-ops.js +1 -1
- package/lib/dist/oc/wrap-development.d.ts +105 -0
- package/lib/dist/oc/wrap-development.js +179 -0
- package/lib/dist/oc/wrap-ops.d.ts +100 -0
- package/lib/dist/oc/wrap-ops.js +406 -0
- package/lib/dist/rendering/render-solid.js +10 -2
- package/lib/dist/scene-manager.d.ts +2 -0
- package/lib/dist/scene-manager.js +29 -0
- package/lib/dist/tests/features/cylinder-curve-filter.test.js +3 -3
- package/lib/dist/tests/features/extrude-to-face.test.js +38 -1
- package/lib/dist/tests/features/helix.test.d.ts +1 -0
- package/lib/dist/tests/features/helix.test.js +295 -0
- package/lib/dist/tests/features/repeat-primitive.test.d.ts +1 -0
- package/lib/dist/tests/features/repeat-primitive.test.js +60 -0
- package/lib/dist/tests/features/rib.test.js +6 -1
- package/lib/dist/tests/features/sweep.test.js +125 -1
- package/lib/dist/tests/features/text.test.d.ts +1 -0
- package/lib/dist/tests/features/text.test.js +347 -0
- package/lib/dist/tests/features/wrap-development.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-development.test.js +130 -0
- package/lib/dist/tests/features/wrap-extruded-target.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-extruded-target.test.js +106 -0
- package/lib/dist/tests/features/wrap-repeat.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-repeat.test.js +93 -0
- package/lib/dist/tests/features/wrap.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap.test.js +331 -0
- package/lib/dist/tests/math/bspline-interpolation.test.d.ts +1 -0
- package/lib/dist/tests/math/bspline-interpolation.test.js +119 -0
- package/lib/dist/tests/measure.test.d.ts +1 -0
- package/lib/dist/tests/measure.test.js +288 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/llm-docs/api/helix.md +64 -0
- package/llm-docs/api/index.json +11 -2
- package/llm-docs/api/text.md +52 -0
- package/llm-docs/api/types/helix.md +105 -0
- package/llm-docs/api/types/text.md +138 -0
- package/llm-docs/api/types/wrap.md +131 -0
- package/llm-docs/api/wrap.md +62 -0
- package/llm-docs/index.json +121 -1
- package/mcp/dist/server.js +20 -1
- package/mcp/dist/tools/inspection.d.ts +17 -0
- package/mcp/dist/tools/inspection.js +14 -0
- package/package.json +7 -3
- package/server/dist/fluidcad-server.d.ts +29 -0
- package/server/dist/fluidcad-server.js +40 -0
- package/server/dist/index.js +4 -2
- package/server/dist/model-package/pack.js +7 -6
- package/server/dist/model-package/types.d.ts +4 -3
- package/server/dist/preferences.d.ts +4 -0
- package/server/dist/preferences.js +2 -0
- package/server/dist/routes/measure.d.ts +3 -0
- package/server/dist/routes/measure.js +32 -0
- package/server/dist/routes/preferences.js +6 -0
- package/server/dist/routes/sketch-edits.js +2 -1
- package/ui/dist/assets/{index-CDJmUpFI.css → index-dAFdg2Un.css} +1 -1
- package/ui/dist/assets/{index-MRqwG9Vh.js → index-no7mtr5s.js} +149 -102
- package/ui/dist/index.html +2 -2
|
@@ -139,6 +139,9 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
139
139
|
if (surfaceType === 'cylinder') {
|
|
140
140
|
splitTargetFace = this.resizeCylindricalFace(targetFace);
|
|
141
141
|
}
|
|
142
|
+
else if (surfaceType === 'cone') {
|
|
143
|
+
splitTargetFace = this.resizeConicalFace(targetFace);
|
|
144
|
+
}
|
|
142
145
|
else {
|
|
143
146
|
splitTargetFace = targetFace;
|
|
144
147
|
}
|
|
@@ -157,6 +160,9 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
157
160
|
resizeCylindricalFace(targetFace) {
|
|
158
161
|
return FaceQuery.makeInfiniteCylindricalFace(targetFace, this.getEndOffset());
|
|
159
162
|
}
|
|
163
|
+
resizeConicalFace(targetFace) {
|
|
164
|
+
return FaceQuery.makeInfiniteConicalFace(targetFace, this.getEndOffset());
|
|
165
|
+
}
|
|
160
166
|
/**
|
|
161
167
|
* Computes the signed distance from the sketch plane to the farthest
|
|
162
168
|
* bounding-box corner of the target face, measured along the sketch
|
|
@@ -8,7 +8,7 @@ export declare class Fillet extends SceneObject {
|
|
|
8
8
|
validate(): void;
|
|
9
9
|
build(context: BuildSceneObjectContext): void;
|
|
10
10
|
doBuild(sceneObjectsMap: Map<SceneObject, Shape[]>, selections: SceneObject[]): {
|
|
11
|
-
addedShapes: Shape<import("
|
|
11
|
+
addedShapes: Shape<import("ocjs-fluidcad").TopoDS_Shape>[];
|
|
12
12
|
removedShapes: {
|
|
13
13
|
shape: Shape;
|
|
14
14
|
owner: SceneObject;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
|
|
2
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
3
|
+
import { IHelix } from "../core/interfaces.js";
|
|
4
|
+
export declare class Helix extends SceneObject implements IHelix {
|
|
5
|
+
source: AxisObjectBase | SceneObject;
|
|
6
|
+
private _pitch?;
|
|
7
|
+
private _turns?;
|
|
8
|
+
private _startOffset;
|
|
9
|
+
private _endOffset;
|
|
10
|
+
private _height?;
|
|
11
|
+
private _radius?;
|
|
12
|
+
private _endRadius?;
|
|
13
|
+
constructor(source: AxisObjectBase | SceneObject);
|
|
14
|
+
pitch(value: number): this;
|
|
15
|
+
turns(value: number): this;
|
|
16
|
+
startOffset(value: number): this;
|
|
17
|
+
endOffset(value: number): this;
|
|
18
|
+
height(value: number): this;
|
|
19
|
+
radius(value: number): this;
|
|
20
|
+
endRadius(value: number): this;
|
|
21
|
+
validate(): void;
|
|
22
|
+
build(_context?: BuildSceneObjectContext): void;
|
|
23
|
+
private deriveTurnsFromHeight;
|
|
24
|
+
private resolveSource;
|
|
25
|
+
private resolveFace;
|
|
26
|
+
private resolveEdge;
|
|
27
|
+
getType(): string;
|
|
28
|
+
getDependencies(): SceneObject[];
|
|
29
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
30
|
+
compareTo(other: SceneObject): boolean;
|
|
31
|
+
serialize(): {
|
|
32
|
+
source: any;
|
|
33
|
+
pitch: number;
|
|
34
|
+
turns: number;
|
|
35
|
+
startOffset: number;
|
|
36
|
+
endOffset: number;
|
|
37
|
+
height: number;
|
|
38
|
+
radius: number;
|
|
39
|
+
endRadius: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { SceneObject } from "../common/scene-object.js";
|
|
2
|
+
import { BuildError } from "../common/build-error.js";
|
|
3
|
+
import { requireShapes } from "../common/operand-check.js";
|
|
4
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
5
|
+
import { HelixOps } from "../oc/helix-ops.js";
|
|
6
|
+
import { FaceQuery } from "../oc/face-query.js";
|
|
7
|
+
import { EdgeQuery } from "../oc/edge-query.js";
|
|
8
|
+
import { EdgeOps } from "../oc/edge-ops.js";
|
|
9
|
+
import { Convert } from "../oc/convert.js";
|
|
10
|
+
import { CoordinateSystem } from "../math/coordinate-system.js";
|
|
11
|
+
import { Vector3d } from "../math/vector3d.js";
|
|
12
|
+
import { Axis } from "../math/axis.js";
|
|
13
|
+
const DEFAULT_RADIUS = 20;
|
|
14
|
+
const DEFAULT_HEIGHT = 50;
|
|
15
|
+
const DEFAULT_TURNS = 1;
|
|
16
|
+
const EPS = 1e-7;
|
|
17
|
+
const TANGENCY_BREAK_EPSILON = 1e-6;
|
|
18
|
+
export class Helix extends SceneObject {
|
|
19
|
+
source;
|
|
20
|
+
_pitch;
|
|
21
|
+
_turns;
|
|
22
|
+
_startOffset = 0;
|
|
23
|
+
_endOffset = 0;
|
|
24
|
+
_height;
|
|
25
|
+
_radius;
|
|
26
|
+
_endRadius;
|
|
27
|
+
constructor(source) {
|
|
28
|
+
super();
|
|
29
|
+
this.source = source;
|
|
30
|
+
}
|
|
31
|
+
pitch(value) {
|
|
32
|
+
this._pitch = value;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
turns(value) {
|
|
36
|
+
this._turns = value;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
startOffset(value) {
|
|
40
|
+
this._startOffset = value;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
endOffset(value) {
|
|
44
|
+
this._endOffset = value;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
height(value) {
|
|
48
|
+
this._height = value;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
radius(value) {
|
|
52
|
+
this._radius = value;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
endRadius(value) {
|
|
56
|
+
this._endRadius = value;
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
validate() {
|
|
60
|
+
if (this.source instanceof AxisObjectBase) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
requireShapes(this.source, "source", "helix");
|
|
64
|
+
}
|
|
65
|
+
build(_context) {
|
|
66
|
+
const resolved = this.resolveSource();
|
|
67
|
+
let startRadius;
|
|
68
|
+
let endRadius;
|
|
69
|
+
let cs;
|
|
70
|
+
let zStart;
|
|
71
|
+
let zEnd;
|
|
72
|
+
let offsetsAlreadyApplied = false;
|
|
73
|
+
switch (resolved.kind) {
|
|
74
|
+
case 'axis': {
|
|
75
|
+
cs = HelixOps_csFromAxis(resolved.axis);
|
|
76
|
+
startRadius = this._radius ?? DEFAULT_RADIUS;
|
|
77
|
+
endRadius = this._endRadius ?? startRadius;
|
|
78
|
+
const { height } = resolveAxisHeightAndPitch(this._height, this._pitch, this._turns);
|
|
79
|
+
zStart = 0;
|
|
80
|
+
zEnd = height;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'cylinder-face': {
|
|
84
|
+
if (this._endRadius !== undefined && this._endRadius !== (this._radius ?? resolved.radius)) {
|
|
85
|
+
console.warn("helix: .endRadius() is ignored when source is a cylindrical face — for a tapered helix, use a conical face or axis input.");
|
|
86
|
+
}
|
|
87
|
+
cs = resolved.cs;
|
|
88
|
+
// Nudge inward by TANGENCY_BREAK_EPSILON when falling back to the
|
|
89
|
+
// face's natural radius. A helix exactly on the cylinder's surface
|
|
90
|
+
// produces a swept tube that's tangent to the cylinder along helical
|
|
91
|
+
// curves, and OCC's BOPAlgo (BRepAlgoAPI_Fuse/Cut) silently fails on
|
|
92
|
+
// tangent contact along curves — fuse returns the inputs as a
|
|
93
|
+
// compound, cut is a no-op. The 1e-6mm nudge is sub-nanometer
|
|
94
|
+
// (visually identical) but produces transversal intersections that
|
|
95
|
+
// BOPAlgo handles cleanly. Sweep also passes skipSimplify=true to
|
|
96
|
+
// avoid SimplifyResult/UnifySameDomain hanging on the resulting
|
|
97
|
+
// tangent-curve topology.
|
|
98
|
+
startRadius = this._radius ?? (resolved.radius - TANGENCY_BREAK_EPSILON);
|
|
99
|
+
endRadius = startRadius;
|
|
100
|
+
if (this._height !== undefined) {
|
|
101
|
+
zStart = 0;
|
|
102
|
+
zEnd = this._height;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
zStart = resolved.vMin;
|
|
106
|
+
zEnd = resolved.vMax;
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'cone-face': {
|
|
111
|
+
if (this._radius !== undefined || this._endRadius !== undefined) {
|
|
112
|
+
console.warn("helix: .radius()/.endRadius() are ignored when source is a conical face — radii are derived from the face geometry.");
|
|
113
|
+
}
|
|
114
|
+
cs = resolved.cs;
|
|
115
|
+
const cosA = Math.cos(resolved.semiAngle);
|
|
116
|
+
const sinA = Math.sin(resolved.semiAngle);
|
|
117
|
+
const zMinFace = resolved.vMin * cosA;
|
|
118
|
+
const zMaxFace = resolved.vMax * cosA;
|
|
119
|
+
const zLow = Math.min(zMinFace, zMaxFace);
|
|
120
|
+
const zHigh = Math.max(zMinFace, zMaxFace);
|
|
121
|
+
if (this._height !== undefined) {
|
|
122
|
+
zStart = zLow;
|
|
123
|
+
zEnd = zLow + this._height;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
zStart = zLow;
|
|
127
|
+
zEnd = zHigh;
|
|
128
|
+
}
|
|
129
|
+
// Apply offsets here so the radii follow the cone's surface at the
|
|
130
|
+
// extended positions (offsets extend along the cone's natural taper,
|
|
131
|
+
// not as a cylindrical extension).
|
|
132
|
+
zStart += this._startOffset;
|
|
133
|
+
zEnd += this._endOffset;
|
|
134
|
+
startRadius = resolved.refRadius + (zStart / cosA) * sinA;
|
|
135
|
+
endRadius = resolved.refRadius + (zEnd / cosA) * sinA;
|
|
136
|
+
offsetsAlreadyApplied = true;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'line-edge': {
|
|
140
|
+
cs = HelixOps_csFromAxis(resolved.axis);
|
|
141
|
+
startRadius = this._radius ?? DEFAULT_RADIUS;
|
|
142
|
+
endRadius = this._endRadius ?? startRadius;
|
|
143
|
+
zStart = 0;
|
|
144
|
+
zEnd = this._height ?? resolved.length;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 'circle-edge': {
|
|
148
|
+
if (this._endRadius !== undefined) {
|
|
149
|
+
console.warn("helix: .endRadius() is ignored when source is a circular edge — both radii equal the circle radius.");
|
|
150
|
+
}
|
|
151
|
+
cs = resolved.cs;
|
|
152
|
+
startRadius = this._radius ?? resolved.radius;
|
|
153
|
+
endRadius = startRadius;
|
|
154
|
+
zStart = 0;
|
|
155
|
+
zEnd = this._height ?? DEFAULT_HEIGHT;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!offsetsAlreadyApplied) {
|
|
160
|
+
zStart += this._startOffset;
|
|
161
|
+
zEnd += this._endOffset;
|
|
162
|
+
}
|
|
163
|
+
if (this._pitch !== undefined && Math.abs(this._pitch) < EPS) {
|
|
164
|
+
throw new BuildError(`helix: .pitch() must be non-zero.`);
|
|
165
|
+
}
|
|
166
|
+
if (this._turns !== undefined && this._turns <= 0) {
|
|
167
|
+
throw new BuildError(`helix: .turns() must be > 0, got ${this._turns}.`, `Pass a positive number to .turns().`);
|
|
168
|
+
}
|
|
169
|
+
const turns = this._turns ?? this.deriveTurnsFromHeight(zEnd - zStart);
|
|
170
|
+
if (!Number.isFinite(turns) || turns <= 0) {
|
|
171
|
+
throw new BuildError(`helix: turns must be > 0, got ${turns}.`, `Pass a positive number to .turns() or .pitch().`);
|
|
172
|
+
}
|
|
173
|
+
if (Math.abs(zEnd - zStart) < EPS) {
|
|
174
|
+
throw new BuildError(`helix: resulting axial height is zero (zStart=${zStart}, zEnd=${zEnd}).`, `Check .startOffset()/.endOffset()/.height() values.`);
|
|
175
|
+
}
|
|
176
|
+
if (startRadius <= 0) {
|
|
177
|
+
throw new BuildError(`helix: start radius must be > 0, got ${startRadius}.`);
|
|
178
|
+
}
|
|
179
|
+
if (endRadius <= 0) {
|
|
180
|
+
throw new BuildError(`helix: end radius would be ≤ 0 (got ${endRadius}). For a conical helix, the end radius must stay positive.`, `Reduce .endOffset() or use a smaller turns/height combination.`);
|
|
181
|
+
}
|
|
182
|
+
const edge = HelixOps.makeHelix(cs, startRadius, endRadius, zStart, zEnd, turns);
|
|
183
|
+
this.addShape(edge);
|
|
184
|
+
this.source.removeShapes(this);
|
|
185
|
+
}
|
|
186
|
+
deriveTurnsFromHeight(height) {
|
|
187
|
+
if (this._pitch === undefined) {
|
|
188
|
+
return DEFAULT_TURNS;
|
|
189
|
+
}
|
|
190
|
+
if (Math.abs(this._pitch) < EPS) {
|
|
191
|
+
throw new BuildError(`helix: .pitch() must be non-zero.`);
|
|
192
|
+
}
|
|
193
|
+
return Math.abs(height / this._pitch);
|
|
194
|
+
}
|
|
195
|
+
resolveSource() {
|
|
196
|
+
if (this.source instanceof AxisObjectBase) {
|
|
197
|
+
return { kind: 'axis', axis: this.source.getAxis() };
|
|
198
|
+
}
|
|
199
|
+
const shapes = this.source.getShapes({ excludeGuide: false });
|
|
200
|
+
if (shapes.length !== 1) {
|
|
201
|
+
throw new BuildError(`helix: source must contain exactly one shape (got ${shapes.length}).`, `Wrap multi-shape sources in select(...) to pick a single face or edge.`);
|
|
202
|
+
}
|
|
203
|
+
const shape = shapes[0];
|
|
204
|
+
if (shape.isFace()) {
|
|
205
|
+
return this.resolveFace(shape);
|
|
206
|
+
}
|
|
207
|
+
if (shape.isEdge()) {
|
|
208
|
+
return this.resolveEdge(shape);
|
|
209
|
+
}
|
|
210
|
+
throw new BuildError(`helix: source shape must be a face or edge, got '${shape.getType()}'.`);
|
|
211
|
+
}
|
|
212
|
+
resolveFace(face) {
|
|
213
|
+
const surfaceType = FaceQuery.getSurfaceType(face);
|
|
214
|
+
if (surfaceType === 'cylinder') {
|
|
215
|
+
const cylinder = FaceQuery.getSurfaceAdaptorCylinderRaw(face.getShape());
|
|
216
|
+
const ax3 = cylinder.Position();
|
|
217
|
+
const cs = Convert.toCoordinateSystemFromGpAx3(ax3, true);
|
|
218
|
+
const radius = cylinder.Radius();
|
|
219
|
+
cylinder.delete();
|
|
220
|
+
const { vMin, vMax } = FaceQuery.getSurfaceVBoundsRaw(face.getShape());
|
|
221
|
+
const canon = canonicalizeAxialBounds(cs, vMin, vMax);
|
|
222
|
+
return { kind: 'cylinder-face', cs: canon.cs, radius, vMin: canon.vMin, vMax: canon.vMax };
|
|
223
|
+
}
|
|
224
|
+
if (surfaceType === 'cone') {
|
|
225
|
+
const cone = FaceQuery.getSurfaceAdaptorConeRaw(face.getShape());
|
|
226
|
+
const ax3 = cone.Position();
|
|
227
|
+
const cs = Convert.toCoordinateSystemFromGpAx3(ax3, true);
|
|
228
|
+
const semiAngle = cone.SemiAngle();
|
|
229
|
+
const refRadius = cone.RefRadius();
|
|
230
|
+
cone.delete();
|
|
231
|
+
const { vMin, vMax } = FaceQuery.getSurfaceVBoundsRaw(face.getShape());
|
|
232
|
+
// For a cone, the V-axis lies along the slant; canonicalizing the
|
|
233
|
+
// CS direction here doesn't simplify the math the same way it does for
|
|
234
|
+
// a cylinder, so leave the cone's frame alone.
|
|
235
|
+
return { kind: 'cone-face', cs, semiAngle, refRadius, vMin, vMax };
|
|
236
|
+
}
|
|
237
|
+
throw new BuildError(`helix: face must be cylindrical or conical (got '${surfaceType}').`);
|
|
238
|
+
}
|
|
239
|
+
resolveEdge(edge) {
|
|
240
|
+
const curveType = EdgeQuery.getEdgeCurveType(edge);
|
|
241
|
+
if (curveType === 'line') {
|
|
242
|
+
const axis = EdgeOps.edgeToAxis(edge);
|
|
243
|
+
const params = EdgeQuery.getEdgeCurveParams(edge);
|
|
244
|
+
const length = Math.abs(params.last - params.first);
|
|
245
|
+
return { kind: 'line-edge', axis, length };
|
|
246
|
+
}
|
|
247
|
+
if (curveType === 'circle') {
|
|
248
|
+
const data = EdgeQuery.getCircleDataFromEdge(edge);
|
|
249
|
+
const axis = new Axis(data.center, data.axisDirection);
|
|
250
|
+
const cs = HelixOps_csFromAxis(axis);
|
|
251
|
+
return { kind: 'circle-edge', cs, radius: data.radius };
|
|
252
|
+
}
|
|
253
|
+
throw new BuildError(`helix: edge must be a line or circle (got '${curveType}').`);
|
|
254
|
+
}
|
|
255
|
+
getType() {
|
|
256
|
+
return 'helix';
|
|
257
|
+
}
|
|
258
|
+
getDependencies() {
|
|
259
|
+
return [this.source];
|
|
260
|
+
}
|
|
261
|
+
createCopy(remap) {
|
|
262
|
+
const newSource = (remap.get(this.source) ?? this.source);
|
|
263
|
+
const copy = new Helix(newSource);
|
|
264
|
+
copy._pitch = this._pitch;
|
|
265
|
+
copy._turns = this._turns;
|
|
266
|
+
copy._startOffset = this._startOffset;
|
|
267
|
+
copy._endOffset = this._endOffset;
|
|
268
|
+
copy._height = this._height;
|
|
269
|
+
copy._radius = this._radius;
|
|
270
|
+
copy._endRadius = this._endRadius;
|
|
271
|
+
return copy;
|
|
272
|
+
}
|
|
273
|
+
compareTo(other) {
|
|
274
|
+
if (!(other instanceof Helix)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (!super.compareTo(other)) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (!this.source.compareTo(other.source)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return this._pitch === other._pitch
|
|
284
|
+
&& this._turns === other._turns
|
|
285
|
+
&& this._startOffset === other._startOffset
|
|
286
|
+
&& this._endOffset === other._endOffset
|
|
287
|
+
&& this._height === other._height
|
|
288
|
+
&& this._radius === other._radius
|
|
289
|
+
&& this._endRadius === other._endRadius;
|
|
290
|
+
}
|
|
291
|
+
serialize() {
|
|
292
|
+
return {
|
|
293
|
+
source: this.source.serialize(),
|
|
294
|
+
pitch: this._pitch,
|
|
295
|
+
turns: this._turns,
|
|
296
|
+
startOffset: this._startOffset,
|
|
297
|
+
endOffset: this._endOffset,
|
|
298
|
+
height: this._height,
|
|
299
|
+
radius: this._radius,
|
|
300
|
+
endRadius: this._endRadius,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function resolveAxisHeightAndPitch(height, pitch, turns) {
|
|
305
|
+
if (height !== undefined) {
|
|
306
|
+
return { height };
|
|
307
|
+
}
|
|
308
|
+
if (pitch !== undefined && turns !== undefined) {
|
|
309
|
+
return { height: Math.abs(pitch * turns) };
|
|
310
|
+
}
|
|
311
|
+
if (pitch !== undefined) {
|
|
312
|
+
return { height: Math.abs(pitch * DEFAULT_TURNS) };
|
|
313
|
+
}
|
|
314
|
+
return { height: DEFAULT_HEIGHT };
|
|
315
|
+
}
|
|
316
|
+
function HelixOps_csFromAxis(axis) {
|
|
317
|
+
const dir = axis.direction.normalize();
|
|
318
|
+
const seed = Math.abs(dir.z) < 0.9 ? Vector3d.unitZ() : Vector3d.unitX();
|
|
319
|
+
const xDir = seed.cross(dir).normalize();
|
|
320
|
+
return new CoordinateSystem(axis.origin, dir, xDir);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* A cylindrical face's `Position()` Ax3 may have its main direction pointing
|
|
324
|
+
* "into" the face's V-extent rather than "out of it" (e.g. an extruded cylinder
|
|
325
|
+
* built from a sketch on z=0 yields mainDir = (0,0,-1) with V-bounds [-50, 0]).
|
|
326
|
+
* Pass through unchanged when V naturally extends in the +mainDir direction;
|
|
327
|
+
* otherwise flip mainDir and negate V-bounds so that V grows along the body's
|
|
328
|
+
* axial extent. This keeps `.startOffset()`/`.endOffset()` semantics intuitive
|
|
329
|
+
* (positive end-offset extends past the cylinder's "top").
|
|
330
|
+
*/
|
|
331
|
+
function canonicalizeAxialBounds(cs, vMin, vMax) {
|
|
332
|
+
if (Math.abs(vMin) <= Math.abs(vMax)) {
|
|
333
|
+
return { cs, vMin, vMax };
|
|
334
|
+
}
|
|
335
|
+
const flipped = new CoordinateSystem(cs.origin, cs.mainDirection.negate(), cs.xDirection);
|
|
336
|
+
return { cs: flipped, vMin: -vMax, vMax: -vMin };
|
|
337
|
+
}
|
|
@@ -4,6 +4,7 @@ import { SceneObject } from "../common/scene-object.js";
|
|
|
4
4
|
import { BelongsToFaceFilter, NotBelongsToFaceFilter } from "../filters/edge/belongs-to-face.js";
|
|
5
5
|
import { FromSceneObjectFilter } from "../filters/from-object.js";
|
|
6
6
|
import { TopologyIndex } from "../oc/topology-index.js";
|
|
7
|
+
import { ShapeHasher } from "../oc/shape-hash.js";
|
|
7
8
|
export class SelectSceneObject extends SceneObject {
|
|
8
9
|
filters;
|
|
9
10
|
constraintObject;
|
|
@@ -26,12 +27,20 @@ export class SelectSceneObject extends SceneObject {
|
|
|
26
27
|
let filters = this.filters;
|
|
27
28
|
let sceneObjects = context.getSceneObjects();
|
|
28
29
|
let excludedObjects = [];
|
|
30
|
+
let narrowedToCloneGroup = false;
|
|
29
31
|
if (transform) {
|
|
30
32
|
filters = filters.map(f => f.transform(transform));
|
|
31
33
|
if (!this.constraintObject && parent) {
|
|
32
34
|
const snapshot = parent.getSnapshot();
|
|
33
35
|
excludedObjects = snapshot ? Array.from(snapshot.values()).flat() : [];
|
|
34
|
-
|
|
36
|
+
// Restrict to this clone instance's own siblings. Other instances of
|
|
37
|
+
// the same repeat share the parent container but carry a different
|
|
38
|
+
// clone transform, and the container itself would re-expose their
|
|
39
|
+
// shapes through getChildShapes.
|
|
40
|
+
const transformRef = this.getTransformRef();
|
|
41
|
+
sceneObjects = context.getSceneObjectsFromTo(parent, this)
|
|
42
|
+
.filter(o => o.getTransformRef() === transformRef);
|
|
43
|
+
narrowedToCloneGroup = true;
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
// Objects passed explicitly via `from(...)` bypass the part scope so that
|
|
@@ -48,12 +57,23 @@ export class SelectSceneObject extends SceneObject {
|
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
59
|
const allShapes = this.constraintObject ? this.constraintObject.getShapes() : this.getAllShapes(sceneObjects, excludedObjects);
|
|
60
|
+
let scopeHasher = null;
|
|
51
61
|
if (this.type === "edge") {
|
|
52
|
-
this.injectScopeFaces(filters, sceneObjects);
|
|
62
|
+
scopeHasher = this.injectScopeFaces(filters, sceneObjects);
|
|
53
63
|
}
|
|
54
64
|
const fromFilters = this.injectFromMembershipSets(filters);
|
|
55
65
|
try {
|
|
56
|
-
|
|
66
|
+
let filteredShapes = this.applyFilters(allShapes, filters);
|
|
67
|
+
if (filteredShapes.length === 0 && narrowedToCloneGroup) {
|
|
68
|
+
// Nothing matched within the cloned group: the original selection
|
|
69
|
+
// resolved to geometry outside the repeated objects (e.g. a wrap
|
|
70
|
+
// target face on a base solid). Reuse that resolution — re-running
|
|
71
|
+
// the transformed filters against base geometry cannot match.
|
|
72
|
+
const source = this.getCloneSource();
|
|
73
|
+
if (source instanceof SelectSceneObject) {
|
|
74
|
+
filteredShapes = source.getAddedShapes();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
57
77
|
this.addShapes(filteredShapes);
|
|
58
78
|
}
|
|
59
79
|
finally {
|
|
@@ -61,6 +81,7 @@ export class SelectSceneObject extends SceneObject {
|
|
|
61
81
|
filter.setMembershipSet(null);
|
|
62
82
|
set.delete();
|
|
63
83
|
}
|
|
84
|
+
scopeHasher?.delete();
|
|
64
85
|
}
|
|
65
86
|
}
|
|
66
87
|
injectFromMembershipSets(filters) {
|
|
@@ -141,6 +162,7 @@ export class SelectSceneObject extends SceneObject {
|
|
|
141
162
|
let scopeSolids = null;
|
|
142
163
|
let extraFaces = null;
|
|
143
164
|
let faceByHash = null;
|
|
165
|
+
let hasher = null;
|
|
144
166
|
for (const builder of filters) {
|
|
145
167
|
for (const filter of builder.getFilters()) {
|
|
146
168
|
if (filter instanceof BelongsToFaceFilter || filter instanceof NotBelongsToFaceFilter) {
|
|
@@ -159,19 +181,21 @@ export class SelectSceneObject extends SceneObject {
|
|
|
159
181
|
extraFaces = [];
|
|
160
182
|
}
|
|
161
183
|
faceByHash = new Map();
|
|
184
|
+
hasher = new ShapeHasher();
|
|
162
185
|
for (const solid of scopeSolids) {
|
|
163
186
|
for (const face of solid.getFaces()) {
|
|
164
|
-
addToBucket(faceByHash, face);
|
|
187
|
+
addToBucket(faceByHash, face, hasher);
|
|
165
188
|
}
|
|
166
189
|
}
|
|
167
190
|
for (const face of extraFaces) {
|
|
168
|
-
addToBucket(faceByHash, face);
|
|
191
|
+
addToBucket(faceByHash, face, hasher);
|
|
169
192
|
}
|
|
170
193
|
}
|
|
171
|
-
filter.setScopeIndex(scopeSolids, extraFaces, faceByHash);
|
|
194
|
+
filter.setScopeIndex(scopeSolids, extraFaces, faceByHash, hasher);
|
|
172
195
|
}
|
|
173
196
|
}
|
|
174
197
|
}
|
|
198
|
+
return hasher;
|
|
175
199
|
}
|
|
176
200
|
applyFilters(shapes, filters) {
|
|
177
201
|
const shapeFilter = new ShapeFilter(shapes, ...filters);
|
|
@@ -220,8 +244,8 @@ export class SelectSceneObject extends SceneObject {
|
|
|
220
244
|
};
|
|
221
245
|
}
|
|
222
246
|
}
|
|
223
|
-
function addToBucket(faceByHash, face) {
|
|
224
|
-
const hash = face.getShape()
|
|
247
|
+
function addToBucket(faceByHash, face, hasher) {
|
|
248
|
+
const hash = hasher.key(face.getShape());
|
|
225
249
|
let bucket = faceByHash.get(hash);
|
|
226
250
|
if (!bucket) {
|
|
227
251
|
bucket = [];
|
|
@@ -18,7 +18,7 @@ export declare class Extruder {
|
|
|
18
18
|
getEndFaces(): Face[];
|
|
19
19
|
getSideFaces(): Face[];
|
|
20
20
|
getInternalFaces(): Face[];
|
|
21
|
-
extrude(): Shape<import("
|
|
21
|
+
extrude(): Shape<import("ocjs-fluidcad").TopoDS_Shape>[];
|
|
22
22
|
private isInternalFace;
|
|
23
23
|
private applyDraft;
|
|
24
24
|
}
|
|
@@ -49,9 +49,14 @@ export class Extruder {
|
|
|
49
49
|
? p.record('Fuse profile faces', () => BooleanOps.fuseFaces(this.faces))
|
|
50
50
|
: BooleanOps.fuseFaces(this.faces);
|
|
51
51
|
for (const face of fusedFaces.result) {
|
|
52
|
+
// Canonicalize the sweep direction (so anti-parallel halves of a
|
|
53
|
+
// symmetric / two-distance extrude fuse with a single merged lateral
|
|
54
|
+
// face) only when undrafted — drafting is sensitive to the sweep
|
|
55
|
+
// parametrization and must keep the literal direction.
|
|
56
|
+
const canonicalizeSweep = !this.draft;
|
|
52
57
|
let { solid, firstFace, lastFace } = p
|
|
53
|
-
? p.record('Make prism from face', () => ExtrudeOps.makePrismFromVec(face, vec))
|
|
54
|
-
: ExtrudeOps.makePrismFromVec(face, vec);
|
|
58
|
+
? p.record('Make prism from face', () => ExtrudeOps.makePrismFromVec(face, vec, canonicalizeSweep))
|
|
59
|
+
: ExtrudeOps.makePrismFromVec(face, vec, canonicalizeSweep);
|
|
55
60
|
if (this.draft) {
|
|
56
61
|
const draftResult = p
|
|
57
62
|
? p.record('Apply draft', () => this.applyDraft(solid, firstFace, lastFace, this.plane))
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { TransformablePrimitive } from "../common/transformable-primitive.js";
|
|
2
|
+
import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
|
|
2
3
|
export declare class Sphere extends TransformablePrimitive {
|
|
3
4
|
radius: number;
|
|
4
5
|
angle: number;
|
|
5
6
|
constructor(radius: number, angle: number);
|
|
6
|
-
build(): void;
|
|
7
|
+
build(context?: BuildSceneObjectContext): void;
|
|
8
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
7
9
|
compareTo(other: Sphere): boolean;
|
|
8
10
|
getType(): string;
|
|
9
11
|
serialize(): {
|
|
@@ -8,9 +8,12 @@ export class Sphere extends TransformablePrimitive {
|
|
|
8
8
|
this.radius = radius;
|
|
9
9
|
this.angle = angle;
|
|
10
10
|
}
|
|
11
|
-
build() {
|
|
11
|
+
build(context) {
|
|
12
12
|
const sphere = Primitives.makeSphere(this.radius, this.angle);
|
|
13
|
-
this.
|
|
13
|
+
this.addPrimitiveShape(sphere, context);
|
|
14
|
+
}
|
|
15
|
+
createCopy(remap) {
|
|
16
|
+
return new Sphere(this.radius, this.angle).syncPrimitiveWith(this);
|
|
14
17
|
}
|
|
15
18
|
compareTo(other) {
|
|
16
19
|
if (!(other instanceof Sphere)) {
|
|
@@ -141,10 +141,15 @@ export class Sweep extends ExtrudeBase {
|
|
|
141
141
|
this.setState('side-faces', classified.sideFaces);
|
|
142
142
|
this.setState('internal-faces', classified.internalFaces);
|
|
143
143
|
this.setState('cap-faces', classified.capFaces);
|
|
144
|
-
cutWithSceneObjects(scope, solids, plane, 0, this, { recordHistoryFor: this });
|
|
144
|
+
cutWithSceneObjects(scope, solids, plane, 0, this, { recordHistoryFor: this, skipSimplify: true });
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
// Sweep paths can produce tangent contact between the swept tube and
|
|
148
|
+
// existing scene shapes (e.g., a helix sweep along a cylinder face).
|
|
149
|
+
// SimplifyResult's face unification can iterate forever on the resulting
|
|
150
|
+
// topology — skip it for sweep ops; downstream classification doesn't
|
|
151
|
+
// need same-domain face merging.
|
|
152
|
+
this.finalizeAndFuse(solids, classified, context, { skipSimplify: true });
|
|
148
153
|
}
|
|
149
154
|
getSpineWire(pathObj) {
|
|
150
155
|
const shapes = pathObj.getShapes({ excludeMeta: false });
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
|
|
2
|
+
import { ExtrudeBase } from "./extrude-base.js";
|
|
3
|
+
import { Extrudable } from "../helpers/types.js";
|
|
4
|
+
import { IWrap } from "../core/interfaces.js";
|
|
5
|
+
/**
|
|
6
|
+
* Wraps a sketch onto a curved face (cylindrical or conical) and thickens it
|
|
7
|
+
* along the surface normal — emboss with `.add()` (default), deboss with
|
|
8
|
+
* `.remove()`, standalone pad with `.new()`. The sketch is developed onto the
|
|
9
|
+
* surface preserving lengths (a true wrap, not a projection).
|
|
10
|
+
*/
|
|
11
|
+
export declare class Wrap extends ExtrudeBase implements IWrap {
|
|
12
|
+
thickness: number;
|
|
13
|
+
face: SceneObject;
|
|
14
|
+
constructor(thickness: number, face: SceneObject, source?: Extrudable | SceneObject);
|
|
15
|
+
build(context: BuildSceneObjectContext): void;
|
|
16
|
+
/** Resolve the planar sketch regions to wrap (same source rules as extrude). */
|
|
17
|
+
private resolveSourceFaces;
|
|
18
|
+
private getTargetFace;
|
|
19
|
+
getDependencies(): SceneObject[];
|
|
20
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
21
|
+
compareTo(other: Wrap): boolean;
|
|
22
|
+
getType(): string;
|
|
23
|
+
getUniqueType(): string;
|
|
24
|
+
serialize(): {
|
|
25
|
+
picking: true;
|
|
26
|
+
pickPoints: number[][];
|
|
27
|
+
trigger: "region-picking";
|
|
28
|
+
pickPlane: {
|
|
29
|
+
origin: import("../math/point.js").Point;
|
|
30
|
+
xDirection: import("../math/vector3d.js").Vector3d;
|
|
31
|
+
yDirection: import("../math/vector3d.js").Vector3d;
|
|
32
|
+
normal: import("../math/vector3d.js").Vector3d;
|
|
33
|
+
};
|
|
34
|
+
extrudable: any;
|
|
35
|
+
thickness: number;
|
|
36
|
+
face: string;
|
|
37
|
+
operationMode: "new" | "remove";
|
|
38
|
+
};
|
|
39
|
+
}
|