fluidcad 0.0.28 → 0.0.30
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/lib/dist/common/profiler.d.ts +12 -0
- package/lib/dist/common/profiler.js +35 -0
- package/lib/dist/common/scene-object.d.ts +3 -0
- package/lib/dist/common/scene-object.js +3 -0
- package/lib/dist/common/shape-history-tracker.d.ts +9 -1
- package/lib/dist/common/shape-history-tracker.js +37 -23
- package/lib/dist/core/2d/aline.d.ts +13 -13
- package/lib/dist/core/2d/aline.js +20 -11
- package/lib/dist/core/2d/arc.d.ts +6 -6
- package/lib/dist/core/2d/arc.js +19 -15
- package/lib/dist/core/2d/back.d.ts +12 -0
- package/lib/dist/core/2d/back.js +11 -0
- package/lib/dist/core/2d/circle.d.ts +2 -2
- package/lib/dist/core/2d/circle.js +14 -10
- package/lib/dist/core/2d/ellipse.d.ts +35 -0
- package/lib/dist/core/2d/ellipse.js +65 -0
- package/lib/dist/core/2d/hline.d.ts +20 -13
- package/lib/dist/core/2d/hline.js +33 -15
- package/lib/dist/core/2d/index.d.ts +2 -0
- package/lib/dist/core/2d/index.js +2 -0
- package/lib/dist/core/2d/intersect.d.ts +2 -2
- package/lib/dist/core/2d/intersect.js +7 -3
- package/lib/dist/core/2d/line.d.ts +2 -2
- package/lib/dist/core/2d/line.js +14 -10
- package/lib/dist/core/2d/offset.d.ts +4 -4
- package/lib/dist/core/2d/offset.js +9 -5
- package/lib/dist/core/2d/polygon.d.ts +4 -4
- package/lib/dist/core/2d/polygon.js +24 -20
- package/lib/dist/core/2d/project.d.ts +2 -2
- package/lib/dist/core/2d/project.js +7 -3
- package/lib/dist/core/2d/rect.d.ts +2 -2
- package/lib/dist/core/2d/rect.js +22 -21
- package/lib/dist/core/2d/slot.d.ts +6 -6
- package/lib/dist/core/2d/slot.js +29 -32
- package/lib/dist/core/2d/vline.d.ts +20 -13
- package/lib/dist/core/2d/vline.js +29 -15
- package/lib/dist/core/interfaces.d.ts +62 -0
- package/lib/dist/core/mirror.d.ts +7 -7
- package/lib/dist/core/mirror.js +17 -11
- package/lib/dist/core/part.d.ts +3 -1
- package/lib/dist/core/part.js +1 -1
- package/lib/dist/core/rotate.d.ts +5 -5
- package/lib/dist/core/rotate.js +4 -1
- package/lib/dist/core/sketch.d.ts +3 -1
- package/lib/dist/core/sketch.js +1 -1
- package/lib/dist/core/translate.d.ts +9 -9
- package/lib/dist/features/2d/aline.d.ts +8 -5
- package/lib/dist/features/2d/aline.js +70 -18
- package/lib/dist/features/2d/back.d.ts +14 -0
- package/lib/dist/features/2d/back.js +35 -0
- package/lib/dist/features/2d/ellipse.d.ts +23 -0
- package/lib/dist/features/2d/ellipse.js +75 -0
- package/lib/dist/features/2d/hline.d.ts +9 -4
- package/lib/dist/features/2d/hline.js +65 -14
- package/lib/dist/features/2d/offset.d.ts +3 -0
- package/lib/dist/features/2d/offset.js +27 -3
- package/lib/dist/features/2d/sketch.d.ts +1 -0
- package/lib/dist/features/2d/sketch.js +15 -0
- package/lib/dist/features/2d/vline.d.ts +9 -4
- package/lib/dist/features/2d/vline.js +67 -15
- package/lib/dist/features/common.js +2 -1
- package/lib/dist/features/extrude-base.d.ts +19 -1
- package/lib/dist/features/extrude-base.js +75 -12
- package/lib/dist/features/extrude-two-distances.js +32 -27
- package/lib/dist/features/extrude.d.ts +39 -0
- package/lib/dist/features/extrude.js +196 -156
- package/lib/dist/features/fuse.js +2 -1
- package/lib/dist/features/lazy-scene-object.d.ts +1 -0
- package/lib/dist/features/lazy-scene-object.js +3 -0
- package/lib/dist/features/lazy-vertex.d.ts +1 -0
- package/lib/dist/features/lazy-vertex.js +3 -0
- package/lib/dist/features/loft.js +11 -8
- package/lib/dist/features/mirror-shape.d.ts +2 -0
- package/lib/dist/features/mirror-shape.js +16 -0
- package/lib/dist/features/mirror-shape2d.d.ts +2 -0
- package/lib/dist/features/mirror-shape2d.js +22 -1
- package/lib/dist/features/revolve.d.ts +31 -0
- package/lib/dist/features/revolve.js +178 -95
- package/lib/dist/features/rotate.d.ts +2 -0
- package/lib/dist/features/rotate.js +16 -0
- package/lib/dist/features/rotate2d.d.ts +2 -0
- package/lib/dist/features/rotate2d.js +16 -0
- package/lib/dist/features/select.js +2 -1
- package/lib/dist/features/simple-extruder.d.ts +3 -1
- package/lib/dist/features/simple-extruder.js +13 -9
- package/lib/dist/features/subtract.d.ts +2 -2
- package/lib/dist/features/subtract.js +3 -3
- package/lib/dist/features/sweep.d.ts +14 -0
- package/lib/dist/features/sweep.js +93 -80
- package/lib/dist/features/translate.d.ts +2 -0
- package/lib/dist/features/translate.js +23 -2
- package/lib/dist/filters/edge/edge-filter.d.ts +6 -0
- package/lib/dist/filters/edge/edge-filter.js +11 -0
- package/lib/dist/filters/face/face-filter.d.ts +6 -0
- package/lib/dist/filters/face/face-filter.js +11 -0
- package/lib/dist/filters/filter-base.d.ts +7 -1
- package/lib/dist/filters/filter-base.js +8 -0
- package/lib/dist/filters/filter-builder-base.js +11 -0
- package/lib/dist/filters/from-object.d.ts +14 -0
- package/lib/dist/filters/from-object.js +40 -0
- package/lib/dist/helpers/scene-helpers.d.ts +2 -0
- package/lib/dist/helpers/scene-helpers.js +68 -48
- package/lib/dist/oc/color-transfer.js +6 -0
- package/lib/dist/oc/edge-ops.d.ts +1 -0
- package/lib/dist/oc/edge-ops.js +17 -0
- package/lib/dist/oc/extrude-ops.d.ts +18 -1
- package/lib/dist/oc/extrude-ops.js +34 -1
- package/lib/dist/oc/geometry.d.ts +1 -0
- package/lib/dist/oc/geometry.js +27 -0
- package/lib/dist/oc/mesh.js +11 -9
- package/lib/dist/oc/ray-intersect.d.ts +16 -0
- package/lib/dist/oc/ray-intersect.js +91 -0
- package/lib/dist/oc/thin-face-maker.d.ts +0 -1
- package/lib/dist/oc/thin-face-maker.js +2 -20
- package/lib/dist/rendering/render.d.ts +2 -1
- package/lib/dist/rendering/render.js +72 -33
- package/lib/dist/rendering/scene.d.ts +4 -0
- package/lib/dist/tests/features/2d/back.test.d.ts +1 -0
- package/lib/dist/tests/features/2d/back.test.js +60 -0
- package/lib/dist/tests/features/2d/circle.test.js +1 -1
- package/lib/dist/tests/features/2d/constrained.test.js +4 -4
- package/lib/dist/tests/features/2d/ellipse.test.d.ts +1 -0
- package/lib/dist/tests/features/2d/ellipse.test.js +100 -0
- package/lib/dist/tests/features/2d/line.test.js +89 -3
- package/lib/dist/tests/features/2d/offset.test.js +1 -1
- package/lib/dist/tests/features/2d/polygon.test.js +2 -2
- package/lib/dist/tests/features/2d/rect.test.js +1 -1
- package/lib/dist/tests/features/2d/slot-from-edge.test.js +1 -1
- package/lib/dist/tests/features/2d/slot.test.js +1 -1
- package/lib/dist/tests/features/mirror.test.js +58 -0
- package/lib/dist/tests/features/mirror2d.test.js +63 -0
- package/lib/dist/tests/features/rotate.test.js +62 -0
- package/lib/dist/tests/features/rotate2d.test.js +47 -0
- package/lib/dist/tests/features/thin-revolve.test.js +37 -1
- package/lib/dist/tests/features/translate.test.js +63 -0
- package/lib/dist/tests/perf/record-fusion-history.bench.test.d.ts +1 -0
- package/lib/dist/tests/perf/record-fusion-history.bench.test.js +77 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/server/dist/index.js +77 -45
- package/server/dist/ws-protocol.d.ts +11 -0
- package/ui/dist/assets/{index-BrW_x4uc.js → index-6Ep4GPxf.js} +131 -77
- package/ui/dist/assets/index-DRKfe6N9.css +2 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-gPoNOiIs.css +0 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Geometry } from "../../oc/geometry.js";
|
|
2
|
+
import { ExtrudableGeometryBase } from "./extrudable-base.js";
|
|
3
|
+
export class Ellipse extends ExtrudableGeometryBase {
|
|
4
|
+
rx;
|
|
5
|
+
ry;
|
|
6
|
+
centerOverride;
|
|
7
|
+
constructor(rx, ry, targetPlane = null, centerOverride = null) {
|
|
8
|
+
super(targetPlane);
|
|
9
|
+
this.rx = rx;
|
|
10
|
+
this.ry = ry;
|
|
11
|
+
this.centerOverride = centerOverride;
|
|
12
|
+
}
|
|
13
|
+
getType() {
|
|
14
|
+
return 'ellipse';
|
|
15
|
+
}
|
|
16
|
+
build() {
|
|
17
|
+
if (this.rx <= 0 || this.ry <= 0) {
|
|
18
|
+
throw new Error(`Ellipse radii must be positive (rx=${this.rx}, ry=${this.ry})`);
|
|
19
|
+
}
|
|
20
|
+
const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
|
|
21
|
+
const center = this.centerOverride
|
|
22
|
+
?? (this.targetPlane
|
|
23
|
+
? plane.worldToLocal(this.targetPlane.getPlaneCenter())
|
|
24
|
+
: this.getCurrentPosition());
|
|
25
|
+
// OCC requires majorRadius >= minorRadius. Pick which plane axis carries the major.
|
|
26
|
+
const rxIsMajor = this.rx >= this.ry;
|
|
27
|
+
const major = rxIsMajor ? this.rx : this.ry;
|
|
28
|
+
const minor = rxIsMajor ? this.ry : this.rx;
|
|
29
|
+
const majorAxisDir = rxIsMajor ? plane.xDirection : plane.yDirection;
|
|
30
|
+
const edge = Geometry.makeEllipseEdge(plane.localToWorld(center), major, minor, plane.normal, majorAxisDir);
|
|
31
|
+
this.addShape(edge);
|
|
32
|
+
if (this.sketch) {
|
|
33
|
+
this.setCurrentPosition(center);
|
|
34
|
+
}
|
|
35
|
+
if (this.targetPlane) {
|
|
36
|
+
this.targetPlane.removeShapes(this);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
getDependencies() {
|
|
40
|
+
return this.targetPlane ? [this.targetPlane] : [];
|
|
41
|
+
}
|
|
42
|
+
createCopy(remap) {
|
|
43
|
+
const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
|
|
44
|
+
return new Ellipse(this.rx, this.ry, targetPlane, this.centerOverride);
|
|
45
|
+
}
|
|
46
|
+
compareTo(other) {
|
|
47
|
+
if (!(other instanceof Ellipse)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (!super.compareTo(other)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (this.targetPlane?.constructor !== other.targetPlane?.constructor) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (this.rx !== other.rx || this.ry !== other.ry) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (this.centerOverride && other.centerOverride) {
|
|
63
|
+
return this.centerOverride.x === other.centerOverride.x
|
|
64
|
+
&& this.centerOverride.y === other.centerOverride.y;
|
|
65
|
+
}
|
|
66
|
+
return this.centerOverride === other.centerOverride;
|
|
67
|
+
}
|
|
68
|
+
serialize() {
|
|
69
|
+
return {
|
|
70
|
+
rx: this.rx,
|
|
71
|
+
ry: this.ry,
|
|
72
|
+
...(this.centerOverride ? { center: { x: this.centerOverride.x, y: this.centerOverride.y } } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { PlaneObjectBase } from "../plane-renderable-base.js";
|
|
2
2
|
import { GeometrySceneObject } from "./geometry.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { IHLine } from "../../core/interfaces.js";
|
|
4
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
5
|
+
export declare class HorizontalLine extends GeometrySceneObject implements IHLine {
|
|
6
|
+
distanceOrTarget: number | SceneObject;
|
|
6
7
|
private targetPlane;
|
|
7
|
-
|
|
8
|
+
private _centered;
|
|
9
|
+
constructor(distanceOrTarget: number | SceneObject, targetPlane?: PlaneObjectBase);
|
|
10
|
+
centered(value?: boolean): this;
|
|
8
11
|
build(): void;
|
|
12
|
+
getDependencies(): SceneObject[];
|
|
13
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
9
14
|
compareTo(other: HorizontalLine): boolean;
|
|
10
15
|
getType(): string;
|
|
11
16
|
getUniqueType(): string;
|
|
@@ -2,25 +2,45 @@ import { Vertex } from "../../common/vertex.js";
|
|
|
2
2
|
import { Geometry } from "../../oc/geometry.js";
|
|
3
3
|
import { Point2D } from "../../math/point.js";
|
|
4
4
|
import { GeometrySceneObject } from "./geometry.js";
|
|
5
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
6
|
+
import { findNearestRayIntersection } from "../../oc/ray-intersect.js";
|
|
5
7
|
export class HorizontalLine extends GeometrySceneObject {
|
|
6
|
-
|
|
7
|
-
centered;
|
|
8
|
+
distanceOrTarget;
|
|
8
9
|
targetPlane;
|
|
9
|
-
|
|
10
|
+
_centered = false;
|
|
11
|
+
constructor(distanceOrTarget, targetPlane = null) {
|
|
10
12
|
super();
|
|
11
|
-
this.
|
|
12
|
-
this.centered = centered;
|
|
13
|
+
this.distanceOrTarget = distanceOrTarget;
|
|
13
14
|
this.targetPlane = targetPlane;
|
|
14
15
|
}
|
|
16
|
+
centered(value = true) {
|
|
17
|
+
this._centered = value;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
15
20
|
build() {
|
|
16
21
|
const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
|
|
17
22
|
const currentPos = this.targetPlane
|
|
18
23
|
? plane.worldToLocal(this.targetPlane.getPlaneCenter())
|
|
19
24
|
: this.getCurrentPosition();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
let startPoint;
|
|
26
|
+
let endPoint;
|
|
27
|
+
let signedLength;
|
|
28
|
+
if (typeof this.distanceOrTarget === 'number') {
|
|
29
|
+
const distance = this.distanceOrTarget;
|
|
30
|
+
startPoint = this._centered
|
|
31
|
+
? currentPos.translate(-distance / 2, 0)
|
|
32
|
+
: currentPos;
|
|
33
|
+
endPoint = startPoint.translate(distance, 0);
|
|
34
|
+
signedLength = distance;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
if (this._centered) {
|
|
38
|
+
throw new Error('hLine: .centered() cannot be combined with a target geometry');
|
|
39
|
+
}
|
|
40
|
+
startPoint = currentPos;
|
|
41
|
+
endPoint = findNearestRayIntersection(plane, startPoint, new Point2D(1, 0), this.distanceOrTarget);
|
|
42
|
+
signedLength = endPoint.x - startPoint.x;
|
|
43
|
+
}
|
|
24
44
|
const start = plane.localToWorld(startPoint);
|
|
25
45
|
const end = plane.localToWorld(endPoint);
|
|
26
46
|
let segment = Geometry.makeSegment(start, end);
|
|
@@ -28,13 +48,33 @@ export class HorizontalLine extends GeometrySceneObject {
|
|
|
28
48
|
this.setState('start', Vertex.fromPoint2D(startPoint));
|
|
29
49
|
this.setState('end', Vertex.fromPoint2D(endPoint));
|
|
30
50
|
this.addShape(edge);
|
|
31
|
-
const sign = Math.sign(
|
|
51
|
+
const sign = Math.sign(signedLength) || 1;
|
|
32
52
|
this.setTangent(new Point2D(sign, 0));
|
|
33
53
|
if (this.sketch) {
|
|
34
54
|
this.setCurrentPosition(endPoint);
|
|
35
55
|
}
|
|
36
|
-
if (this.targetPlane)
|
|
56
|
+
if (this.targetPlane) {
|
|
37
57
|
this.targetPlane.removeShapes(this);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getDependencies() {
|
|
61
|
+
const deps = [];
|
|
62
|
+
if (this.targetPlane) {
|
|
63
|
+
deps.push(this.targetPlane);
|
|
64
|
+
}
|
|
65
|
+
if (this.distanceOrTarget instanceof SceneObject) {
|
|
66
|
+
deps.push(this.distanceOrTarget);
|
|
67
|
+
}
|
|
68
|
+
return deps;
|
|
69
|
+
}
|
|
70
|
+
createCopy(remap) {
|
|
71
|
+
const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
|
|
72
|
+
const distanceOrTarget = this.distanceOrTarget instanceof SceneObject
|
|
73
|
+
? (remap.get(this.distanceOrTarget) || this.distanceOrTarget)
|
|
74
|
+
: this.distanceOrTarget;
|
|
75
|
+
const copy = new HorizontalLine(distanceOrTarget, targetPlane);
|
|
76
|
+
copy.centered(this._centered);
|
|
77
|
+
return copy;
|
|
38
78
|
}
|
|
39
79
|
compareTo(other) {
|
|
40
80
|
if (!(other instanceof HorizontalLine)) {
|
|
@@ -49,7 +89,18 @@ export class HorizontalLine extends GeometrySceneObject {
|
|
|
49
89
|
if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
|
|
50
90
|
return false;
|
|
51
91
|
}
|
|
52
|
-
|
|
92
|
+
if (typeof this.distanceOrTarget !== typeof other.distanceOrTarget) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (this.distanceOrTarget instanceof SceneObject && other.distanceOrTarget instanceof SceneObject) {
|
|
96
|
+
if (!this.distanceOrTarget.compareTo(other.distanceOrTarget)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (this.distanceOrTarget !== other.distanceOrTarget) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return this._centered === other._centered;
|
|
53
104
|
}
|
|
54
105
|
getType() {
|
|
55
106
|
return 'line';
|
|
@@ -59,8 +110,8 @@ export class HorizontalLine extends GeometrySceneObject {
|
|
|
59
110
|
}
|
|
60
111
|
serialize() {
|
|
61
112
|
return {
|
|
62
|
-
distance: this.
|
|
63
|
-
centered: this.
|
|
113
|
+
distance: typeof this.distanceOrTarget === 'number' ? this.distanceOrTarget : null,
|
|
114
|
+
centered: this._centered
|
|
64
115
|
};
|
|
65
116
|
}
|
|
66
117
|
}
|
|
@@ -5,7 +5,9 @@ export declare class Offset extends ExtrudableGeometryBase {
|
|
|
5
5
|
private distance;
|
|
6
6
|
private removeOriginal;
|
|
7
7
|
private sourceGeometries;
|
|
8
|
+
private _close;
|
|
8
9
|
constructor(distance: number, removeOriginal?: boolean, sourceGeometries?: SceneObject[], targetPlane?: PlaneObjectBase);
|
|
10
|
+
close(): this;
|
|
9
11
|
build(): void;
|
|
10
12
|
getDependencies(): SceneObject[];
|
|
11
13
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
@@ -14,5 +16,6 @@ export declare class Offset extends ExtrudableGeometryBase {
|
|
|
14
16
|
serialize(): {
|
|
15
17
|
distance: number;
|
|
16
18
|
removeOriginal: boolean;
|
|
19
|
+
close: boolean;
|
|
17
20
|
};
|
|
18
21
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WireOps } from "../../oc/wire-ops.js";
|
|
2
|
+
import { EdgeOps } from "../../oc/edge-ops.js";
|
|
2
3
|
import { Edge } from "../../common/edge.js";
|
|
3
4
|
import { Vertex } from "../../common/vertex.js";
|
|
4
5
|
import { Wire } from "../../common/wire.js";
|
|
@@ -7,13 +8,21 @@ export class Offset extends ExtrudableGeometryBase {
|
|
|
7
8
|
distance;
|
|
8
9
|
removeOriginal;
|
|
9
10
|
sourceGeometries;
|
|
11
|
+
_close = false;
|
|
10
12
|
constructor(distance, removeOriginal = false, sourceGeometries = null, targetPlane = null) {
|
|
11
13
|
super(targetPlane);
|
|
12
14
|
this.distance = distance;
|
|
13
15
|
this.removeOriginal = removeOriginal;
|
|
14
16
|
this.sourceGeometries = sourceGeometries;
|
|
15
17
|
}
|
|
18
|
+
close() {
|
|
19
|
+
this._close = true;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
16
22
|
build() {
|
|
23
|
+
if (this._close && this.removeOriginal) {
|
|
24
|
+
throw new Error("Offset.close() cannot be used with removeOriginal");
|
|
25
|
+
}
|
|
17
26
|
let sourceObjects;
|
|
18
27
|
if (this.sketch) {
|
|
19
28
|
sourceObjects = this.sketch.getEdgesWithOwner();
|
|
@@ -54,6 +63,14 @@ export class Offset extends ExtrudableGeometryBase {
|
|
|
54
63
|
for (const edge of edges) {
|
|
55
64
|
this.addShape(edge);
|
|
56
65
|
}
|
|
66
|
+
if (this._close && !offsetWire.isClosed()) {
|
|
67
|
+
const originalStart = wireInfo.wire.getFirstVertex().toPoint();
|
|
68
|
+
const originalEnd = wireInfo.wire.getLastVertex().toPoint();
|
|
69
|
+
const offsetStart = offsetWire.getFirstVertex().toPoint();
|
|
70
|
+
const offsetEnd = offsetWire.getLastVertex().toPoint();
|
|
71
|
+
this.addShape(EdgeOps.makeLineEdge(originalEnd, offsetEnd));
|
|
72
|
+
this.addShape(EdgeOps.makeLineEdge(offsetStart, originalStart));
|
|
73
|
+
}
|
|
57
74
|
if (this.removeOriginal) {
|
|
58
75
|
for (const [edge, owner] of wireInfo.edges) {
|
|
59
76
|
owner.removeShape(edge, this);
|
|
@@ -83,7 +100,11 @@ export class Offset extends ExtrudableGeometryBase {
|
|
|
83
100
|
const geometriesClone = this.sourceGeometries
|
|
84
101
|
? this.sourceGeometries.map(obj => remap.get(obj) || obj)
|
|
85
102
|
: null;
|
|
86
|
-
|
|
103
|
+
const copy = new Offset(this.distance, this.removeOriginal, geometriesClone, targetPlane);
|
|
104
|
+
if (this._close) {
|
|
105
|
+
copy._close = true;
|
|
106
|
+
}
|
|
107
|
+
return copy;
|
|
87
108
|
}
|
|
88
109
|
compareTo(other) {
|
|
89
110
|
if (!(other instanceof Offset)) {
|
|
@@ -113,7 +134,9 @@ export class Offset extends ExtrudableGeometryBase {
|
|
|
113
134
|
}
|
|
114
135
|
}
|
|
115
136
|
}
|
|
116
|
-
return this.distance === other.distance
|
|
137
|
+
return this.distance === other.distance
|
|
138
|
+
&& this.removeOriginal === other.removeOriginal
|
|
139
|
+
&& this._close === other._close;
|
|
117
140
|
}
|
|
118
141
|
getType() {
|
|
119
142
|
return 'offset';
|
|
@@ -121,7 +144,8 @@ export class Offset extends ExtrudableGeometryBase {
|
|
|
121
144
|
serialize() {
|
|
122
145
|
return {
|
|
123
146
|
distance: this.distance,
|
|
124
|
-
removeOriginal: this.removeOriginal
|
|
147
|
+
removeOriginal: this.removeOriginal,
|
|
148
|
+
close: this._close
|
|
125
149
|
};
|
|
126
150
|
}
|
|
127
151
|
}
|
|
@@ -14,6 +14,7 @@ export declare class Sketch extends SceneObject implements Extrudable {
|
|
|
14
14
|
getStartPoint(): Point2D;
|
|
15
15
|
getTangentAt(currentObj: GeometrySceneObject): Point2D | null;
|
|
16
16
|
getPositionAt(currentObj: GeometrySceneObject): Point2D;
|
|
17
|
+
getPreviousPosition(currentObj: GeometrySceneObject, count?: number): Point2D;
|
|
17
18
|
getLastPosition(scope?: Set<SceneObject>): Point2D;
|
|
18
19
|
build(context?: BuildSceneObjectContext): void;
|
|
19
20
|
getEdges(): Edge[];
|
|
@@ -58,6 +58,21 @@ export class Sketch extends SceneObject {
|
|
|
58
58
|
}
|
|
59
59
|
return this.getStartPoint();
|
|
60
60
|
}
|
|
61
|
+
getPreviousPosition(currentObj, count = 1) {
|
|
62
|
+
const children = this.getChildren();
|
|
63
|
+
const previous = children.slice(0, children.indexOf(currentObj));
|
|
64
|
+
let remaining = count;
|
|
65
|
+
for (let i = previous.length - 1; i >= 0; i--) {
|
|
66
|
+
const pos = previous[i].getState('current-position');
|
|
67
|
+
if (pos) {
|
|
68
|
+
if (remaining === 0) {
|
|
69
|
+
return pos;
|
|
70
|
+
}
|
|
71
|
+
remaining--;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return this.getStartPoint();
|
|
75
|
+
}
|
|
61
76
|
getLastPosition(scope) {
|
|
62
77
|
let children = this.getChildren().slice();
|
|
63
78
|
if (scope) {
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { PlaneObjectBase } from "../plane-renderable-base.js";
|
|
2
2
|
import { GeometrySceneObject } from "./geometry.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { IVLine } from "../../core/interfaces.js";
|
|
4
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
5
|
+
export declare class VerticalLine extends GeometrySceneObject implements IVLine {
|
|
6
|
+
distanceOrTarget: number | SceneObject;
|
|
6
7
|
private targetPlane;
|
|
7
|
-
|
|
8
|
+
private _centered;
|
|
9
|
+
constructor(distanceOrTarget: number | SceneObject, targetPlane?: PlaneObjectBase);
|
|
10
|
+
centered(value?: boolean): this;
|
|
8
11
|
build(): void;
|
|
12
|
+
getDependencies(): SceneObject[];
|
|
13
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
9
14
|
compareTo(other: VerticalLine): boolean;
|
|
10
15
|
getType(): string;
|
|
11
16
|
getUniqueType(): string;
|
|
@@ -2,25 +2,45 @@ import { Vertex } from "../../common/vertex.js";
|
|
|
2
2
|
import { Geometry } from "../../oc/geometry.js";
|
|
3
3
|
import { Point2D } from "../../math/point.js";
|
|
4
4
|
import { GeometrySceneObject } from "./geometry.js";
|
|
5
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
6
|
+
import { findNearestRayIntersection } from "../../oc/ray-intersect.js";
|
|
5
7
|
export class VerticalLine extends GeometrySceneObject {
|
|
6
|
-
|
|
7
|
-
centered;
|
|
8
|
+
distanceOrTarget;
|
|
8
9
|
targetPlane;
|
|
9
|
-
|
|
10
|
+
_centered = false;
|
|
11
|
+
constructor(distanceOrTarget, targetPlane = null) {
|
|
10
12
|
super();
|
|
11
|
-
this.
|
|
12
|
-
this.centered = centered;
|
|
13
|
+
this.distanceOrTarget = distanceOrTarget;
|
|
13
14
|
this.targetPlane = targetPlane;
|
|
14
15
|
}
|
|
16
|
+
centered(value = true) {
|
|
17
|
+
this._centered = value;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
15
20
|
build() {
|
|
16
21
|
const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
|
|
17
22
|
const currentPos = this.targetPlane
|
|
18
23
|
? plane.worldToLocal(this.targetPlane.getPlaneCenter())
|
|
19
24
|
: this.getCurrentPosition();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
let startPoint;
|
|
26
|
+
let endPoint;
|
|
27
|
+
let signedLength;
|
|
28
|
+
if (typeof this.distanceOrTarget === 'number') {
|
|
29
|
+
const distance = this.distanceOrTarget;
|
|
30
|
+
startPoint = this._centered
|
|
31
|
+
? currentPos.translate(0, -distance / 2)
|
|
32
|
+
: currentPos;
|
|
33
|
+
endPoint = startPoint.translate(0, distance);
|
|
34
|
+
signedLength = distance;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
if (this._centered) {
|
|
38
|
+
throw new Error('vLine: .centered() cannot be combined with a target geometry');
|
|
39
|
+
}
|
|
40
|
+
startPoint = currentPos;
|
|
41
|
+
endPoint = findNearestRayIntersection(plane, startPoint, new Point2D(0, 1), this.distanceOrTarget);
|
|
42
|
+
signedLength = endPoint.y - startPoint.y;
|
|
43
|
+
}
|
|
24
44
|
const start = plane.localToWorld(startPoint);
|
|
25
45
|
const end = plane.localToWorld(endPoint);
|
|
26
46
|
let segment = Geometry.makeSegment(start, end);
|
|
@@ -28,12 +48,33 @@ export class VerticalLine extends GeometrySceneObject {
|
|
|
28
48
|
this.setState('start', Vertex.fromPoint2D(startPoint));
|
|
29
49
|
this.setState('end', Vertex.fromPoint2D(endPoint));
|
|
30
50
|
this.addShape(edge);
|
|
31
|
-
const sign = Math.sign(
|
|
51
|
+
const sign = Math.sign(signedLength) || 1;
|
|
32
52
|
this.setTangent(new Point2D(0, sign));
|
|
33
|
-
if (this.sketch)
|
|
53
|
+
if (this.sketch) {
|
|
34
54
|
this.setCurrentPosition(endPoint);
|
|
35
|
-
|
|
55
|
+
}
|
|
56
|
+
if (this.targetPlane) {
|
|
36
57
|
this.targetPlane.removeShapes(this);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getDependencies() {
|
|
61
|
+
const deps = [];
|
|
62
|
+
if (this.targetPlane) {
|
|
63
|
+
deps.push(this.targetPlane);
|
|
64
|
+
}
|
|
65
|
+
if (this.distanceOrTarget instanceof SceneObject) {
|
|
66
|
+
deps.push(this.distanceOrTarget);
|
|
67
|
+
}
|
|
68
|
+
return deps;
|
|
69
|
+
}
|
|
70
|
+
createCopy(remap) {
|
|
71
|
+
const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
|
|
72
|
+
const distanceOrTarget = this.distanceOrTarget instanceof SceneObject
|
|
73
|
+
? (remap.get(this.distanceOrTarget) || this.distanceOrTarget)
|
|
74
|
+
: this.distanceOrTarget;
|
|
75
|
+
const copy = new VerticalLine(distanceOrTarget, targetPlane);
|
|
76
|
+
copy.centered(this._centered);
|
|
77
|
+
return copy;
|
|
37
78
|
}
|
|
38
79
|
compareTo(other) {
|
|
39
80
|
if (!(other instanceof VerticalLine)) {
|
|
@@ -48,7 +89,18 @@ export class VerticalLine extends GeometrySceneObject {
|
|
|
48
89
|
if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
|
|
49
90
|
return false;
|
|
50
91
|
}
|
|
51
|
-
|
|
92
|
+
if (typeof this.distanceOrTarget !== typeof other.distanceOrTarget) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (this.distanceOrTarget instanceof SceneObject && other.distanceOrTarget instanceof SceneObject) {
|
|
96
|
+
if (!this.distanceOrTarget.compareTo(other.distanceOrTarget)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (this.distanceOrTarget !== other.distanceOrTarget) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return this._centered === other._centered;
|
|
52
104
|
}
|
|
53
105
|
getType() {
|
|
54
106
|
return 'line';
|
|
@@ -58,8 +110,8 @@ export class VerticalLine extends GeometrySceneObject {
|
|
|
58
110
|
}
|
|
59
111
|
serialize() {
|
|
60
112
|
return {
|
|
61
|
-
distance: this.
|
|
62
|
-
centered: this.
|
|
113
|
+
distance: typeof this.distanceOrTarget === 'number' ? this.distanceOrTarget : null,
|
|
114
|
+
centered: this._centered
|
|
63
115
|
};
|
|
64
116
|
}
|
|
65
117
|
}
|
|
@@ -15,6 +15,7 @@ export class Common extends SceneObject {
|
|
|
15
15
|
return this._sceneObjects;
|
|
16
16
|
}
|
|
17
17
|
build(context) {
|
|
18
|
+
const p = context.getProfiler();
|
|
18
19
|
let sceneObjects = this.sceneObjects;
|
|
19
20
|
if (sceneObjects?.length === 0) {
|
|
20
21
|
sceneObjects = context.getSceneObjects();
|
|
@@ -29,7 +30,7 @@ export class Common extends SceneObject {
|
|
|
29
30
|
if (allShapes.length < 2) {
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
|
-
const { newShapes, modifiedShapes, result } = BooleanOps.common(allShapes);
|
|
33
|
+
const { newShapes, modifiedShapes, result } = p.record('Common solids', () => BooleanOps.common(allShapes));
|
|
33
34
|
if (!modifiedShapes.length && newShapes.length === 0) {
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Face } from "../common/face.js";
|
|
2
2
|
import { Edge } from "../common/edge.js";
|
|
3
3
|
import { Shape } from "../common/shape.js";
|
|
4
|
-
import { SceneObject } from "../common/scene-object.js";
|
|
4
|
+
import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
|
|
5
5
|
import { Extrudable } from "../helpers/types.js";
|
|
6
6
|
import { IExtrude } from "../core/interfaces.js";
|
|
7
7
|
import { LazyVertex } from "./lazy-vertex.js";
|
|
@@ -10,6 +10,14 @@ import { Plane } from "../math/plane.js";
|
|
|
10
10
|
import { FaceFilterBuilder } from "../filters/face/face-filter.js";
|
|
11
11
|
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
12
12
|
import { ShapeHistory } from "../common/shape-history-tracker.js";
|
|
13
|
+
/** A 3D op's classified face buckets. Each is empty if the op doesn't produce that category. */
|
|
14
|
+
export type ClassifiedFaces = {
|
|
15
|
+
startFaces: Face[];
|
|
16
|
+
endFaces: Face[];
|
|
17
|
+
sideFaces: Face[];
|
|
18
|
+
internalFaces: Face[];
|
|
19
|
+
capFaces: Face[];
|
|
20
|
+
};
|
|
13
21
|
export declare abstract class ExtrudeBase extends SceneObject implements IExtrude {
|
|
14
22
|
protected _extrudable: Extrudable | null;
|
|
15
23
|
protected _faceSource: SceneObject | null;
|
|
@@ -60,6 +68,16 @@ export declare abstract class ExtrudeBase extends SceneObject implements IExtrud
|
|
|
60
68
|
* unchanged in the scene, every face/edge is brand new from this op's POV.
|
|
61
69
|
*/
|
|
62
70
|
protected recordShapeFacesAndEdgesAsAdditions(shapes: Shape[]): void;
|
|
71
|
+
/**
|
|
72
|
+
* Shared post-extrude bookkeeping: store classified face state, fuse with
|
|
73
|
+
* the scene (recording history + remapping classification), and finalize
|
|
74
|
+
* with edge classification. Caller is responsible for removing source
|
|
75
|
+
* shapes (they vary across ops — Revolve also removes the axis, etc.)
|
|
76
|
+
* BEFORE calling this.
|
|
77
|
+
*/
|
|
78
|
+
protected finalizeAndFuse(shapes: Shape[], classified: ClassifiedFaces, context: BuildSceneObjectContext, fuseOpts?: {
|
|
79
|
+
glue?: 'full' | 'shift';
|
|
80
|
+
}): void;
|
|
63
81
|
/**
|
|
64
82
|
* One-shot edge classification: derive start/end/side/internal/cap edges
|
|
65
83
|
* from the already-classified face arrays in state and store them as
|
|
@@ -12,13 +12,41 @@ import { EdgeOps } from "../oc/edge-ops.js";
|
|
|
12
12
|
import { Explorer } from "../oc/explorer.js";
|
|
13
13
|
import { getOC } from "../oc/init.js";
|
|
14
14
|
import { ShapeHistoryTracker } from "../common/shape-history-tracker.js";
|
|
15
|
-
|
|
15
|
+
import { fuseWithSceneObjects } from "../helpers/scene-helpers.js";
|
|
16
|
+
function dedupEdgesByMap(edges) {
|
|
17
|
+
if (edges.length === 0) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const oc = getOC();
|
|
21
|
+
const seen = new oc.TopTools_MapOfShape();
|
|
22
|
+
const result = [];
|
|
23
|
+
for (const edge of edges) {
|
|
24
|
+
if (seen.Add(edge.getShape())) {
|
|
25
|
+
result.push(edge);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
seen.delete();
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
// Dedup `edges` by TShape pointer while also excluding any edge that shares a
|
|
32
|
+
// TShape with one in `excluded`. Single TopTools_MapOfShape pre-seeded with the
|
|
33
|
+
// excluded set; Add returns false for duplicates and for already-excluded edges.
|
|
34
|
+
function dedupEdgesByMapExcluding(edges, excluded) {
|
|
35
|
+
if (edges.length === 0) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const oc = getOC();
|
|
39
|
+
const map = new oc.TopTools_MapOfShape();
|
|
40
|
+
for (const e of excluded) {
|
|
41
|
+
map.Add(e.getShape());
|
|
42
|
+
}
|
|
16
43
|
const result = [];
|
|
17
44
|
for (const edge of edges) {
|
|
18
|
-
if (
|
|
45
|
+
if (map.Add(edge.getShape())) {
|
|
19
46
|
result.push(edge);
|
|
20
47
|
}
|
|
21
48
|
}
|
|
49
|
+
map.delete();
|
|
22
50
|
return result;
|
|
23
51
|
}
|
|
24
52
|
export class ExtrudeBase extends SceneObject {
|
|
@@ -135,8 +163,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
135
163
|
const startFaces = parent.getState('start-faces') || [];
|
|
136
164
|
const endFaces = parent.getState('end-faces') || [];
|
|
137
165
|
const excludedEdges = [...startFaces, ...endFaces].flatMap(f => f.getEdges());
|
|
138
|
-
const edges =
|
|
139
|
-
.filter(e => !excludedEdges.some(ex => e.getShape().IsSame(ex.getShape()))));
|
|
166
|
+
const edges = dedupEdgesByMapExcluding(sideFaces.flatMap(f => f.getEdges()), excludedEdges);
|
|
140
167
|
return this.resolveEdges(edges, args);
|
|
141
168
|
}, this);
|
|
142
169
|
}
|
|
@@ -202,7 +229,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
202
229
|
return classified;
|
|
203
230
|
}
|
|
204
231
|
const faces = source.getState(faceKey) || [];
|
|
205
|
-
return
|
|
232
|
+
return dedupEdgesByMap(faces.flatMap(f => f.getEdges()));
|
|
206
233
|
}
|
|
207
234
|
/**
|
|
208
235
|
* Remap the state-stored face category arrays through a fusion's tool-side
|
|
@@ -236,6 +263,44 @@ export class ExtrudeBase extends SceneObject {
|
|
|
236
263
|
}
|
|
237
264
|
}
|
|
238
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Shared post-extrude bookkeeping: store classified face state, fuse with
|
|
268
|
+
* the scene (recording history + remapping classification), and finalize
|
|
269
|
+
* with edge classification. Caller is responsible for removing source
|
|
270
|
+
* shapes (they vary across ops — Revolve also removes the axis, etc.)
|
|
271
|
+
* BEFORE calling this.
|
|
272
|
+
*/
|
|
273
|
+
finalizeAndFuse(shapes, classified, context, fuseOpts) {
|
|
274
|
+
const p = context.getProfiler();
|
|
275
|
+
const sceneObjects = this.resolveFusionScope(context.getSceneObjects());
|
|
276
|
+
this.setState('start-faces', classified.startFaces);
|
|
277
|
+
this.setState('end-faces', classified.endFaces);
|
|
278
|
+
this.setState('side-faces', classified.sideFaces);
|
|
279
|
+
this.setState('internal-faces', classified.internalFaces);
|
|
280
|
+
this.setState('cap-faces', classified.capFaces);
|
|
281
|
+
if (shapes.length === 0 || sceneObjects.length === 0) {
|
|
282
|
+
this.addShapes(shapes);
|
|
283
|
+
p.record('Record additions', () => this.recordShapeFacesAndEdgesAsAdditions(shapes));
|
|
284
|
+
p.record('Classify edges', () => this.classifyExtrudeEdges());
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const fusionResult = fuseWithSceneObjects(sceneObjects, shapes, {
|
|
288
|
+
...fuseOpts,
|
|
289
|
+
recordHistoryFor: this,
|
|
290
|
+
profiler: p,
|
|
291
|
+
});
|
|
292
|
+
for (const modifiedShape of fusionResult.modifiedShapes) {
|
|
293
|
+
if (!modifiedShape.object) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
modifiedShape.object.removeShape(modifiedShape.shape, this);
|
|
297
|
+
}
|
|
298
|
+
this.addShapes(fusionResult.newShapes);
|
|
299
|
+
if (fusionResult.toolHistory) {
|
|
300
|
+
p.record('Remap classified faces', () => this.remapClassifiedFaces(fusionResult.toolHistory));
|
|
301
|
+
}
|
|
302
|
+
p.record('Classify edges', () => this.classifyExtrudeEdges());
|
|
303
|
+
}
|
|
239
304
|
/**
|
|
240
305
|
* One-shot edge classification: derive start/end/side/internal/cap edges
|
|
241
306
|
* from the already-classified face arrays in state and store them as
|
|
@@ -256,13 +321,11 @@ export class ExtrudeBase extends SceneObject {
|
|
|
256
321
|
const sideFaces = this.getState('side-faces') || [];
|
|
257
322
|
const internalFaces = this.getState('internal-faces') || [];
|
|
258
323
|
const capFaces = this.getState('cap-faces') || [];
|
|
259
|
-
const startEdges =
|
|
260
|
-
const endEdges =
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
const internalEdges = dedupEdgesByIsSame(internalFaces.flatMap(f => f.getEdges()));
|
|
265
|
-
const capEdges = dedupEdgesByIsSame(capFaces.flatMap(f => f.getEdges()));
|
|
324
|
+
const startEdges = dedupEdgesByMap(startFaces.flatMap(f => f.getEdges()));
|
|
325
|
+
const endEdges = dedupEdgesByMap(endFaces.flatMap(f => f.getEdges()));
|
|
326
|
+
const sideEdges = dedupEdgesByMapExcluding(sideFaces.flatMap(f => f.getEdges()), [...startEdges, ...endEdges]);
|
|
327
|
+
const internalEdges = dedupEdgesByMap(internalFaces.flatMap(f => f.getEdges()));
|
|
328
|
+
const capEdges = dedupEdgesByMap(capFaces.flatMap(f => f.getEdges()));
|
|
266
329
|
this.setState('start-edges', startEdges);
|
|
267
330
|
this.setState('end-edges', endEdges);
|
|
268
331
|
this.setState('side-edges', sideEdges);
|