fluidcad 0.0.23 → 0.0.25
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/scene-object.d.ts +14 -0
- package/lib/dist/common/scene-object.js +129 -0
- package/lib/dist/core/2d/tarc.d.ts +2 -2
- package/lib/dist/core/chamfer.d.ts +5 -5
- package/lib/dist/core/color.d.ts +3 -3
- package/lib/dist/core/copy.js +5 -4
- package/lib/dist/core/cylinder.d.ts +2 -2
- package/lib/dist/core/fillet.d.ts +6 -6
- package/lib/dist/core/index.d.ts +2 -1
- package/lib/dist/core/index.js +1 -0
- package/lib/dist/core/interfaces.d.ts +68 -17
- package/lib/dist/core/load.d.ts +2 -2
- package/lib/dist/core/local.d.ts +12 -0
- package/lib/dist/core/local.js +18 -0
- package/lib/dist/core/mirror.d.ts +9 -9
- package/lib/dist/core/mirror.js +0 -3
- package/lib/dist/core/remove.d.ts +2 -2
- package/lib/dist/core/rotate.d.ts +5 -5
- package/lib/dist/core/sphere.d.ts +3 -3
- package/lib/dist/core/subtract.d.ts +2 -2
- package/lib/dist/core/translate.d.ts +9 -9
- package/lib/dist/features/2d/circle.js +2 -1
- package/lib/dist/features/2d/slot.d.ts +0 -3
- package/lib/dist/features/2d/slot.js +7 -28
- package/lib/dist/features/2d/tarc-with-tangent.js +4 -2
- package/lib/dist/features/2d/tarc.js +4 -2
- package/lib/dist/features/axis-from-edge.js +2 -2
- package/lib/dist/features/axis-from-sketch.d.ts +18 -0
- package/lib/dist/features/axis-from-sketch.js +58 -0
- package/lib/dist/features/copy-linear2d.d.ts +4 -2
- package/lib/dist/features/copy-linear2d.js +23 -9
- package/lib/dist/features/mirror-shape.js +10 -0
- package/lib/dist/features/select.d.ts +1 -1
- package/lib/dist/features/select.js +1 -2
- package/lib/dist/math/axis.d.ts +1 -0
- package/lib/dist/math/axis.js +4 -1
- package/lib/dist/math/index.d.ts +1 -1
- package/lib/dist/math/index.js +1 -1
- package/lib/dist/rendering/render.js +8 -0
- package/lib/dist/tests/features/mirror2d.test.js +34 -0
- package/lib/dist/tests/features/scene-object-chain.test.d.ts +1 -0
- package/lib/dist/tests/features/scene-object-chain.test.js +64 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/server/dist/code-editor.d.ts +3 -2
- package/server/dist/code-editor.js +244 -121
- package/ui/dist/assets/{index-CqP_mgZk.js → index-D2eOzshJ.js} +3 -3
- package/ui/dist/index.html +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ITransformable } from "./interfaces.js";
|
|
2
2
|
interface SphereFunction {
|
|
3
3
|
/**
|
|
4
4
|
* Creates a full sphere with the given radius.
|
|
5
5
|
* @param radius - The sphere radius
|
|
6
6
|
*/
|
|
7
|
-
(radius: number):
|
|
7
|
+
(radius: number): ITransformable;
|
|
8
8
|
/**
|
|
9
9
|
* Creates a partial sphere with the given radius and sweep angle.
|
|
10
10
|
* @param radius - The sphere radius
|
|
11
11
|
* @param angle - The sweep angle in degrees
|
|
12
12
|
*/
|
|
13
|
-
(radius: number, angle: number):
|
|
13
|
+
(radius: number, angle: number): ITransformable;
|
|
14
14
|
}
|
|
15
15
|
declare const _default: SphereFunction;
|
|
16
16
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISceneObject } from "./interfaces.js";
|
|
1
|
+
import { ISceneObject, ITransformable } from "./interfaces.js";
|
|
2
2
|
interface SubtractFunction {
|
|
3
3
|
/**
|
|
4
4
|
* Subtracts the second shape from the first (boolean difference).
|
|
@@ -6,7 +6,7 @@ interface SubtractFunction {
|
|
|
6
6
|
* @param object1 - The base shape
|
|
7
7
|
* @param object2 - The shape to subtract
|
|
8
8
|
*/
|
|
9
|
-
(object1: ISceneObject, object2: ISceneObject):
|
|
9
|
+
(object1: ISceneObject, object2: ISceneObject): ITransformable;
|
|
10
10
|
}
|
|
11
11
|
declare const _default: SubtractFunction;
|
|
12
12
|
export default _default;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { PointLike } from "../math/point.js";
|
|
2
|
-
import { ISceneObject } from "./interfaces.js";
|
|
2
|
+
import { ISceneObject, ITransformable } from "./interfaces.js";
|
|
3
3
|
interface TranslateFunction {
|
|
4
4
|
/**
|
|
5
5
|
* Translates objects along the X axis.
|
|
6
6
|
* @param x - The X distance
|
|
7
7
|
* @param targets - The objects to translate (defaults to last object)
|
|
8
8
|
*/
|
|
9
|
-
(x: number, ...targets: ISceneObject[]):
|
|
9
|
+
(x: number, ...targets: ISceneObject[]): ITransformable;
|
|
10
10
|
/**
|
|
11
11
|
* Translates objects along the X axis, optionally making a copy.
|
|
12
12
|
* @param x - The X distance
|
|
13
13
|
* @param copy - Whether to copy instead of move
|
|
14
14
|
* @param targets - The objects to translate (defaults to last object)
|
|
15
15
|
*/
|
|
16
|
-
(x: number, copy: boolean, ...targets: ISceneObject[]):
|
|
16
|
+
(x: number, copy: boolean, ...targets: ISceneObject[]): ITransformable;
|
|
17
17
|
/**
|
|
18
18
|
* Translates objects along the X and Y axes.
|
|
19
19
|
* @param x - The X distance
|
|
20
20
|
* @param y - The Y distance
|
|
21
21
|
* @param targets - The objects to translate (defaults to last object)
|
|
22
22
|
*/
|
|
23
|
-
(x: number, y: number, ...targets: ISceneObject[]):
|
|
23
|
+
(x: number, y: number, ...targets: ISceneObject[]): ITransformable;
|
|
24
24
|
/**
|
|
25
25
|
* Translates objects along the X and Y axes, optionally making a copy.
|
|
26
26
|
* @param x - The X distance
|
|
@@ -28,7 +28,7 @@ interface TranslateFunction {
|
|
|
28
28
|
* @param copy - Whether to copy instead of move
|
|
29
29
|
* @param targets - The objects to translate (defaults to last object)
|
|
30
30
|
*/
|
|
31
|
-
(x: number, y: number, copy: boolean, ...targets: ISceneObject[]):
|
|
31
|
+
(x: number, y: number, copy: boolean, ...targets: ISceneObject[]): ITransformable;
|
|
32
32
|
/**
|
|
33
33
|
* Translates objects along all three axes.
|
|
34
34
|
* @param x - The X distance
|
|
@@ -36,7 +36,7 @@ interface TranslateFunction {
|
|
|
36
36
|
* @param z - The Z distance
|
|
37
37
|
* @param targets - The objects to translate (defaults to last object)
|
|
38
38
|
*/
|
|
39
|
-
(x: number, y: number, z: number, ...targets: ISceneObject[]):
|
|
39
|
+
(x: number, y: number, z: number, ...targets: ISceneObject[]): ITransformable;
|
|
40
40
|
/**
|
|
41
41
|
* Translates objects along all three axes, optionally making a copy.
|
|
42
42
|
* @param x - The X distance
|
|
@@ -45,20 +45,20 @@ interface TranslateFunction {
|
|
|
45
45
|
* @param copy - Whether to copy instead of move
|
|
46
46
|
* @param targets - The objects to translate (defaults to last object)
|
|
47
47
|
*/
|
|
48
|
-
(x: number, y: number, z: number, copy: boolean, ...targets: ISceneObject[]):
|
|
48
|
+
(x: number, y: number, z: number, copy: boolean, ...targets: ISceneObject[]): ITransformable;
|
|
49
49
|
/**
|
|
50
50
|
* Translates objects by a point-like offset.
|
|
51
51
|
* @param distance - The offset as a point
|
|
52
52
|
* @param targets - The objects to translate (defaults to last object)
|
|
53
53
|
*/
|
|
54
|
-
(distance: PointLike, ...targets: ISceneObject[]):
|
|
54
|
+
(distance: PointLike, ...targets: ISceneObject[]): ITransformable;
|
|
55
55
|
/**
|
|
56
56
|
* Translates objects by a point-like offset, optionally making a copy.
|
|
57
57
|
* @param distance - The offset as a point
|
|
58
58
|
* @param copy - Whether to copy instead of move
|
|
59
59
|
* @param targets - The objects to translate (defaults to last object)
|
|
60
60
|
*/
|
|
61
|
-
(distance: PointLike, copy: boolean, ...targets: ISceneObject[]):
|
|
61
|
+
(distance: PointLike, copy: boolean, ...targets: ISceneObject[]): ITransformable;
|
|
62
62
|
}
|
|
63
63
|
declare const _default: TranslateFunction;
|
|
64
64
|
export default _default;
|
|
@@ -21,8 +21,9 @@ export class Circle extends ExtrudableGeometryBase {
|
|
|
21
21
|
const circle = Geometry.makeCircle(plane.localToWorld(center), radius, plane.normal);
|
|
22
22
|
let edge = Geometry.makeEdgeFromCircle(circle);
|
|
23
23
|
this.addShape(edge);
|
|
24
|
-
if (this.sketch)
|
|
24
|
+
if (this.sketch) {
|
|
25
25
|
this.setCurrentPosition(center);
|
|
26
|
+
}
|
|
26
27
|
if (this.targetPlane) {
|
|
27
28
|
this.targetPlane.removeShapes(this);
|
|
28
29
|
}
|
|
@@ -6,10 +6,8 @@ export declare class Slot extends ExtrudableGeometryBase implements ISlot {
|
|
|
6
6
|
distance: number;
|
|
7
7
|
radius: number;
|
|
8
8
|
private _center;
|
|
9
|
-
private _angle;
|
|
10
9
|
constructor(distance: number, radius: number, targetPlane?: PlaneObjectBase);
|
|
11
10
|
centered(value?: boolean): this;
|
|
12
|
-
rotate(angle: number): this;
|
|
13
11
|
build(): void;
|
|
14
12
|
getType(): string;
|
|
15
13
|
getDependencies(): SceneObject[];
|
|
@@ -19,6 +17,5 @@ export declare class Slot extends ExtrudableGeometryBase implements ISlot {
|
|
|
19
17
|
distance: number;
|
|
20
18
|
radius: number;
|
|
21
19
|
centered: boolean;
|
|
22
|
-
angle: number;
|
|
23
20
|
};
|
|
24
21
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Point2D } from "../../math/point.js";
|
|
2
2
|
import { Geometry } from "../../oc/geometry.js";
|
|
3
|
-
import { rad } from "../../helpers/math-helpers.js";
|
|
4
3
|
import { ExtrudableGeometryBase } from "./extrudable-base.js";
|
|
5
4
|
export class Slot extends ExtrudableGeometryBase {
|
|
6
5
|
distance;
|
|
7
6
|
radius;
|
|
8
7
|
_center = false;
|
|
9
|
-
_angle = 0;
|
|
10
8
|
constructor(distance, radius, targetPlane = null) {
|
|
11
9
|
super(targetPlane);
|
|
12
10
|
this.distance = distance;
|
|
@@ -16,10 +14,6 @@ export class Slot extends ExtrudableGeometryBase {
|
|
|
16
14
|
this._center = value;
|
|
17
15
|
return this;
|
|
18
16
|
}
|
|
19
|
-
rotate(angle) {
|
|
20
|
-
this._angle = angle;
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
17
|
build() {
|
|
24
18
|
if (this.distance < 0) {
|
|
25
19
|
throw new Error("Slot distance must be positive");
|
|
@@ -30,26 +24,14 @@ export class Slot extends ExtrudableGeometryBase {
|
|
|
30
24
|
? plane.worldToLocal(this.targetPlane.getPlaneCenter())
|
|
31
25
|
: this.getCurrentPosition();
|
|
32
26
|
if (this._center) {
|
|
33
|
-
|
|
34
|
-
const cos = Math.cos(angleRad);
|
|
35
|
-
const sin = Math.sin(angleRad);
|
|
36
|
-
leftCenter = leftCenter.translate(-this.distance / 2 * cos, -this.distance / 2 * sin);
|
|
27
|
+
leftCenter = leftCenter.translate(-this.distance / 2, 0);
|
|
37
28
|
}
|
|
38
|
-
const
|
|
39
|
-
const cos = Math.cos(angleRad);
|
|
40
|
-
const sin = Math.sin(angleRad);
|
|
41
|
-
// Direction along the slot axis
|
|
42
|
-
const dirX = cos;
|
|
43
|
-
const dirY = sin;
|
|
44
|
-
// Perpendicular direction (90 degrees CCW)
|
|
45
|
-
const perpX = -sin;
|
|
46
|
-
const perpY = cos;
|
|
47
|
-
const rightCenter = new Point2D(leftCenter.x + this.distance * dirX, leftCenter.y + this.distance * dirY);
|
|
29
|
+
const rightCenter = new Point2D(leftCenter.x + this.distance, leftCenter.y);
|
|
48
30
|
// Four key points where lines meet arcs
|
|
49
|
-
const topLeft = new Point2D(leftCenter.x
|
|
50
|
-
const topRight = new Point2D(rightCenter.x
|
|
51
|
-
const bottomRight = new Point2D(rightCenter.x
|
|
52
|
-
const bottomLeft = new Point2D(leftCenter.x
|
|
31
|
+
const topLeft = new Point2D(leftCenter.x, leftCenter.y + this.radius);
|
|
32
|
+
const topRight = new Point2D(rightCenter.x, rightCenter.y + this.radius);
|
|
33
|
+
const bottomRight = new Point2D(rightCenter.x, rightCenter.y - this.radius);
|
|
34
|
+
const bottomLeft = new Point2D(leftCenter.x, leftCenter.y - this.radius);
|
|
53
35
|
// Top line: topLeft -> topRight
|
|
54
36
|
const topSegment = Geometry.makeSegment(localToWorld(topLeft), localToWorld(topRight));
|
|
55
37
|
// Right arc: topRight -> bottomRight (CW semicircle around rightCenter)
|
|
@@ -87,7 +69,6 @@ export class Slot extends ExtrudableGeometryBase {
|
|
|
87
69
|
const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
|
|
88
70
|
const s = new Slot(this.distance, this.radius, targetPlane);
|
|
89
71
|
s.centered(this._center);
|
|
90
|
-
s.rotate(this._angle);
|
|
91
72
|
return s;
|
|
92
73
|
}
|
|
93
74
|
compareTo(other) {
|
|
@@ -105,15 +86,13 @@ export class Slot extends ExtrudableGeometryBase {
|
|
|
105
86
|
}
|
|
106
87
|
return this.distance === other.distance &&
|
|
107
88
|
this.radius === other.radius &&
|
|
108
|
-
this._center === other._center
|
|
109
|
-
this._angle === other._angle;
|
|
89
|
+
this._center === other._center;
|
|
110
90
|
}
|
|
111
91
|
serialize() {
|
|
112
92
|
return {
|
|
113
93
|
distance: this.distance,
|
|
114
94
|
radius: this.radius,
|
|
115
95
|
centered: this._center,
|
|
116
|
-
angle: this._angle,
|
|
117
96
|
};
|
|
118
97
|
}
|
|
119
98
|
}
|
|
@@ -15,13 +15,15 @@ export class TangentArcWithTangent extends GeometrySceneObject {
|
|
|
15
15
|
}
|
|
16
16
|
build() {
|
|
17
17
|
const plane = this.sketch.getPlane();
|
|
18
|
-
|
|
18
|
+
// Negative radius flips the sweep direction.
|
|
19
|
+
const radius = Math.abs(this.radius);
|
|
20
|
+
const signedEndAngle = this.radius < 0 ? -this.endAngle : this.endAngle;
|
|
19
21
|
const tangent = this.startTangent.asPoint2D();
|
|
20
22
|
// Derive the base angle from the provided tangent.
|
|
21
23
|
// Tangent at angle θ on a circle is (-sin θ, cos θ),
|
|
22
24
|
// so θ = atan2(-tx, ty).
|
|
23
25
|
const baseAngle = Math.atan2(-tangent.x, tangent.y);
|
|
24
|
-
const clampedEndAngle = Math.max(
|
|
26
|
+
const clampedEndAngle = Math.max(signedEndAngle, -359.9999);
|
|
25
27
|
const cw = clampedEndAngle < 0;
|
|
26
28
|
const startAngleRad = cw ? baseAngle + Math.PI : baseAngle;
|
|
27
29
|
const endAngleRad = startAngleRad + rad(clampedEndAngle);
|
|
@@ -17,7 +17,9 @@ export class TangentArc extends GeometrySceneObject {
|
|
|
17
17
|
throw new Error('TangentArc requires a previous sibling with a tangent');
|
|
18
18
|
}
|
|
19
19
|
const plane = this.sketch.getPlane();
|
|
20
|
-
|
|
20
|
+
// Negative radius flips the sweep direction.
|
|
21
|
+
const radius = Math.abs(this.radius);
|
|
22
|
+
const signedEndAngle = this.radius < 0 ? -this.endAngle : this.endAngle;
|
|
21
23
|
// Derive the base angle from the previous sibling's tangent.
|
|
22
24
|
// Tangent at angle θ on a circle is (-sin θ, cos θ),
|
|
23
25
|
// so θ = atan2(-tx, ty).
|
|
@@ -25,7 +27,7 @@ export class TangentArc extends GeometrySceneObject {
|
|
|
25
27
|
// For CW (negative endAngle), flip the center to the opposite side
|
|
26
28
|
// and negate the normal so makeArc sweeps in reverse
|
|
27
29
|
// Clamp to avoid coincident start/end points at exactly ±360°
|
|
28
|
-
const clampedEndAngle = Math.max(
|
|
30
|
+
const clampedEndAngle = Math.max(signedEndAngle, -359.9999);
|
|
29
31
|
const cw = clampedEndAngle < 0;
|
|
30
32
|
const startAngleRad = cw ? baseAngle + Math.PI : baseAngle;
|
|
31
33
|
const endAngleRad = startAngleRad + rad(clampedEndAngle);
|
|
@@ -13,7 +13,7 @@ export class AxisFromEdge extends AxisObjectBase {
|
|
|
13
13
|
build() {
|
|
14
14
|
let axis;
|
|
15
15
|
if (this.sourceObject instanceof SelectSceneObject) {
|
|
16
|
-
const shapes = this.sourceObject.getShapes();
|
|
16
|
+
const shapes = this.sourceObject.getShapes({ excludeGuide: false });
|
|
17
17
|
console.log(`Axis: Retrieved ${shapes.length} shapes from selection`);
|
|
18
18
|
if (shapes.length === 0) {
|
|
19
19
|
throw new Error("Axis: Selected object has no shapes to extract axis from");
|
|
@@ -34,7 +34,7 @@ export class AxisFromEdge extends AxisObjectBase {
|
|
|
34
34
|
axis = this.sourceObject.getAxis();
|
|
35
35
|
}
|
|
36
36
|
else if (this.sourceObject instanceof SceneObject) {
|
|
37
|
-
const shapes = this.sourceObject.getShapes();
|
|
37
|
+
const shapes = this.sourceObject.getShapes({ excludeGuide: false });
|
|
38
38
|
console.log(`Axis: Retrieved ${shapes.length} shapes from source object`);
|
|
39
39
|
if (shapes.length === 0) {
|
|
40
40
|
throw new Error("Axis: Source object has no shapes to extract axis from");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AxisTransformOptions, StandardAxis } from "../math/axis.js";
|
|
2
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
3
|
+
import { SceneObject } from "../common/scene-object.js";
|
|
4
|
+
import { Sketch } from "./2d/sketch.js";
|
|
5
|
+
export declare class AxisFromSketch extends AxisObjectBase {
|
|
6
|
+
private sketch;
|
|
7
|
+
private direction;
|
|
8
|
+
private options?;
|
|
9
|
+
constructor(sketch: Sketch, direction: StandardAxis, options?: AxisTransformOptions);
|
|
10
|
+
build(): void;
|
|
11
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
12
|
+
compareTo(other: AxisFromSketch): boolean;
|
|
13
|
+
getUniqueType(): string;
|
|
14
|
+
serialize(): {
|
|
15
|
+
direction: StandardAxis;
|
|
16
|
+
options: AxisTransformOptions;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
2
|
+
import { EdgeOps } from "../oc/edge-ops.js";
|
|
3
|
+
export class AxisFromSketch extends AxisObjectBase {
|
|
4
|
+
sketch;
|
|
5
|
+
direction;
|
|
6
|
+
options;
|
|
7
|
+
constructor(sketch, direction, options) {
|
|
8
|
+
super();
|
|
9
|
+
this.sketch = sketch;
|
|
10
|
+
this.direction = direction;
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
build() {
|
|
14
|
+
const plane = this.sketch.getPlane();
|
|
15
|
+
let axis = plane.normalizeAxis(this.direction);
|
|
16
|
+
if (!axis) {
|
|
17
|
+
throw new Error(`AxisFromSketch: invalid direction '${this.direction}'`);
|
|
18
|
+
}
|
|
19
|
+
if (this.options) {
|
|
20
|
+
axis = axis.transform(this.options);
|
|
21
|
+
}
|
|
22
|
+
this.setState('axis', axis);
|
|
23
|
+
const edge = EdgeOps.axisToEdge(axis);
|
|
24
|
+
edge.markAsMetaShape();
|
|
25
|
+
this.addShape(edge);
|
|
26
|
+
}
|
|
27
|
+
createCopy(remap) {
|
|
28
|
+
const sketch = remap.get(this.sketch) || this.sketch;
|
|
29
|
+
return new AxisFromSketch(sketch, this.direction, this.options);
|
|
30
|
+
}
|
|
31
|
+
compareTo(other) {
|
|
32
|
+
if (!(other instanceof AxisFromSketch)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (!super.compareTo(other)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (!this.sketch.compareTo(other.sketch)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (this.direction !== other.direction) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (JSON.stringify(this.options) !== JSON.stringify(other.options)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
getUniqueType() {
|
|
50
|
+
return 'axis-from-sketch';
|
|
51
|
+
}
|
|
52
|
+
serialize() {
|
|
53
|
+
return {
|
|
54
|
+
direction: this.direction,
|
|
55
|
+
options: this.options,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -2,11 +2,13 @@ import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js"
|
|
|
2
2
|
import { Axis } from "../math/axis.js";
|
|
3
3
|
import { GeometrySceneObject } from "./2d/geometry.js";
|
|
4
4
|
import { LinearCopyOptions } from "./copy-linear.js";
|
|
5
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
6
|
+
export type CopyLinear2DAxis = Axis | AxisObjectBase;
|
|
5
7
|
export declare class CopyLinear2D extends GeometrySceneObject {
|
|
6
|
-
axes:
|
|
8
|
+
axes: CopyLinear2DAxis[];
|
|
7
9
|
options: LinearCopyOptions;
|
|
8
10
|
targetObjects: SceneObject[] | null;
|
|
9
|
-
constructor(axes:
|
|
11
|
+
constructor(axes: CopyLinear2DAxis[], options: LinearCopyOptions, targetObjects?: SceneObject[] | null);
|
|
10
12
|
build(context: BuildSceneObjectContext): void;
|
|
11
13
|
compareTo(other: CopyLinear2D): boolean;
|
|
12
14
|
getType(): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Matrix4 } from "../math/matrix4.js";
|
|
2
2
|
import { ShapeOps } from "../oc/shape-ops.js";
|
|
3
3
|
import { GeometrySceneObject } from "./2d/geometry.js";
|
|
4
|
+
import { AxisObjectBase } from "./axis-renderable-base.js";
|
|
4
5
|
export class CopyLinear2D extends GeometrySceneObject {
|
|
5
6
|
axes;
|
|
6
7
|
options;
|
|
@@ -20,17 +21,18 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
20
21
|
else {
|
|
21
22
|
objects = allSiblings;
|
|
22
23
|
}
|
|
24
|
+
const resolvedAxes = this.axes.map(a => a instanceof AxisObjectBase ? a.getAxis() : a);
|
|
23
25
|
const { count, centered, skip } = this.options;
|
|
24
26
|
const counts = Array.isArray(count)
|
|
25
27
|
? count
|
|
26
|
-
:
|
|
28
|
+
: resolvedAxes.map(() => count);
|
|
27
29
|
const offsets = 'offset' in this.options && this.options.offset !== undefined
|
|
28
|
-
? (Array.isArray(this.options.offset) ? this.options.offset :
|
|
30
|
+
? (Array.isArray(this.options.offset) ? this.options.offset : resolvedAxes.map(() => this.options.offset))
|
|
29
31
|
: null;
|
|
30
32
|
const lengths = 'length' in this.options && this.options.length !== undefined
|
|
31
|
-
? (Array.isArray(this.options.length) ? this.options.length :
|
|
33
|
+
? (Array.isArray(this.options.length) ? this.options.length : resolvedAxes.map(() => this.options.length))
|
|
32
34
|
: null;
|
|
33
|
-
const axisOffsets =
|
|
35
|
+
const axisOffsets = resolvedAxes.map((_, a) => {
|
|
34
36
|
if (offsets) {
|
|
35
37
|
return offsets[a] ?? offsets[0];
|
|
36
38
|
}
|
|
@@ -38,10 +40,10 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
38
40
|
const axisCount = counts[a];
|
|
39
41
|
return axisCount > 1 ? len / (axisCount - 1) : 0;
|
|
40
42
|
});
|
|
41
|
-
const centerIndices =
|
|
43
|
+
const centerIndices = resolvedAxes.map((_, a) => centered ? Math.floor(counts[a] / 2) : 0);
|
|
42
44
|
// Build grid positions as cartesian product of per-axis indices (0..counts[a]-1)
|
|
43
45
|
let positions = [[]];
|
|
44
|
-
for (let a = 0; a <
|
|
46
|
+
for (let a = 0; a < resolvedAxes.length; a++) {
|
|
45
47
|
const next = [];
|
|
46
48
|
for (const pos of positions) {
|
|
47
49
|
for (let i = 0; i < counts[a]; i++) {
|
|
@@ -57,9 +59,9 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
61
|
let matrix = Matrix4.identity();
|
|
60
|
-
for (let a = 0; a <
|
|
62
|
+
for (let a = 0; a < resolvedAxes.length; a++) {
|
|
61
63
|
const distance = (pos[a] - centerIndices[a]) * axisOffsets[a];
|
|
62
|
-
const translation =
|
|
64
|
+
const translation = resolvedAxes[a].direction.multiply(distance);
|
|
63
65
|
matrix = matrix.multiply(Matrix4.fromTranslationVector(translation));
|
|
64
66
|
}
|
|
65
67
|
for (const obj of objects) {
|
|
@@ -82,7 +84,19 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
82
84
|
return false;
|
|
83
85
|
}
|
|
84
86
|
for (let i = 0; i < this.axes.length; i++) {
|
|
85
|
-
|
|
87
|
+
const a = this.axes[i];
|
|
88
|
+
const b = other.axes[i];
|
|
89
|
+
const aIsObj = a instanceof AxisObjectBase;
|
|
90
|
+
const bIsObj = b instanceof AxisObjectBase;
|
|
91
|
+
if (aIsObj !== bIsObj) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (aIsObj) {
|
|
95
|
+
if (!a.compareTo(b)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (!a.equals(b)) {
|
|
86
100
|
return false;
|
|
87
101
|
}
|
|
88
102
|
}
|
|
@@ -70,6 +70,16 @@ export class MirrorShape extends SceneObject {
|
|
|
70
70
|
if (!this.plane.compareTo(other.plane)) {
|
|
71
71
|
return false;
|
|
72
72
|
}
|
|
73
|
+
const thisTargetObjects = this.targetObjects || [];
|
|
74
|
+
const otherTargetObjects = other.targetObjects || [];
|
|
75
|
+
if (thisTargetObjects.length !== otherTargetObjects.length) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
for (let i = 0; i < thisTargetObjects.length; i++) {
|
|
79
|
+
if (!thisTargetObjects[i].compareTo(otherTargetObjects[i])) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
73
83
|
return true;
|
|
74
84
|
}
|
|
75
85
|
getType() {
|
|
@@ -14,7 +14,7 @@ export declare class SelectSceneObject extends SceneObject implements ISelect {
|
|
|
14
14
|
private getAllShapes;
|
|
15
15
|
getDependencies(): SceneObject[];
|
|
16
16
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
17
|
-
|
|
17
|
+
withTransformedFilters(matrix: Matrix4): SelectSceneObject;
|
|
18
18
|
private injectScopeFaces;
|
|
19
19
|
applyFilters(shapes: Shape[], filters: FilterBuilderBase<Shape>[]): Shape[];
|
|
20
20
|
compareTo(other: SelectSceneObject): boolean;
|
|
@@ -63,9 +63,8 @@ export class SelectSceneObject extends SceneObject {
|
|
|
63
63
|
: undefined;
|
|
64
64
|
return new SelectSceneObject(this.filters, remappedConstraint);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
withTransformedFilters(matrix) {
|
|
67
67
|
const mirroredFilters = this.filters.map(f => f.transform(matrix));
|
|
68
|
-
console.log('SelectSceneObject: transform applied to selection filters.', mirroredFilters);
|
|
69
68
|
return new SelectSceneObject(mirroredFilters, this.constraintObject);
|
|
70
69
|
}
|
|
71
70
|
injectScopeFaces(filters, sceneObjects) {
|
package/lib/dist/math/axis.d.ts
CHANGED
|
@@ -69,4 +69,5 @@ export declare class Axis {
|
|
|
69
69
|
export type StandardAxis = "x" | "y" | "z";
|
|
70
70
|
export type AxisLike = StandardAxis | Axis | IAxis | AxisObjectBase;
|
|
71
71
|
export declare function isAxisLike(value: unknown): value is AxisLike;
|
|
72
|
+
export declare function isStandardAxis(value: unknown): value is StandardAxis;
|
|
72
73
|
export declare function toAxis(value: AxisLike): Axis;
|
package/lib/dist/math/axis.js
CHANGED
|
@@ -140,7 +140,10 @@ export class Axis {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
export function isAxisLike(value) {
|
|
143
|
-
return value instanceof AxisObjectBase || value instanceof Axis || value
|
|
143
|
+
return value instanceof AxisObjectBase || value instanceof Axis || isStandardAxis(value);
|
|
144
|
+
}
|
|
145
|
+
export function isStandardAxis(value) {
|
|
146
|
+
return value === "x" || value === "y" || value === "z";
|
|
144
147
|
}
|
|
145
148
|
export function toAxis(value) {
|
|
146
149
|
if (value instanceof Axis) {
|
package/lib/dist/math/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { Vector3d, Vector3dLike, normalizeVector as toVector3d } from "./vector3
|
|
|
2
2
|
export { Point, Point2D, PointLike, Point2DLike, isPointLike, isPoint2DLike } from "./point.js";
|
|
3
3
|
export { Quaternion } from "./quaternion.js";
|
|
4
4
|
export { Matrix4 } from "./matrix4.js";
|
|
5
|
-
export { Axis, AxisLike, StandardAxis, AxisTransformOptions, isAxisLike, toAxis } from "./axis.js";
|
|
5
|
+
export { Axis, AxisLike, StandardAxis, AxisTransformOptions, isAxisLike, isStandardAxis, toAxis } from "./axis.js";
|
|
6
6
|
export { Plane, PlaneLike, StandardPlane, PlaneTransformOptions, RotationSpace, isPlaneLike, toPlane } from "./plane.js";
|
|
7
7
|
export { CoordinateSystem } from "./coordinate-system.js";
|
|
8
8
|
export { Convert } from "./convert.js";
|
package/lib/dist/math/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { Vector3d, normalizeVector as toVector3d } from "./vector3d.js";
|
|
|
2
2
|
export { Point, Point2D, isPointLike, isPoint2DLike } from "./point.js";
|
|
3
3
|
export { Quaternion } from "./quaternion.js";
|
|
4
4
|
export { Matrix4 } from "./matrix4.js";
|
|
5
|
-
export { Axis, isAxisLike, toAxis } from "./axis.js";
|
|
5
|
+
export { Axis, isAxisLike, isStandardAxis, toAxis } from "./axis.js";
|
|
6
6
|
export { Plane, isPlaneLike, toPlane } from "./plane.js";
|
|
7
7
|
export { CoordinateSystem } from "./coordinate-system.js";
|
|
8
8
|
export { Convert } from "./convert.js";
|
|
@@ -3,6 +3,7 @@ import { PlaneObjectBase } from "../features/plane-renderable-base.js";
|
|
|
3
3
|
import { AxisObjectBase } from "../features/axis-renderable-base.js";
|
|
4
4
|
import { Sketch } from "../features/2d/sketch.js";
|
|
5
5
|
import { transformMeshes } from "./mesh-transform.js";
|
|
6
|
+
import { ShapeOps } from "../oc/shape-ops.js";
|
|
6
7
|
const meshBuilder = new MeshBuilder();
|
|
7
8
|
function renderSceneObject(obj, scene, buildDurationMs) {
|
|
8
9
|
const hasError = !!obj.getError();
|
|
@@ -199,6 +200,13 @@ export function renderScene(scene) {
|
|
|
199
200
|
return null;
|
|
200
201
|
},
|
|
201
202
|
});
|
|
203
|
+
const appliedTransform = object.getAppliedTransform();
|
|
204
|
+
if (appliedTransform && !object.isContainer()) {
|
|
205
|
+
const shapes = object.getAddedShapes();
|
|
206
|
+
for (let i = 0; i < shapes.length; i++) {
|
|
207
|
+
shapes[i] = ShapeOps.transform(shapes[i], appliedTransform);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
202
210
|
}
|
|
203
211
|
catch (error) {
|
|
204
212
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3,6 +3,7 @@ import { setupOC, render } from "../setup.js";
|
|
|
3
3
|
import sketch from "../../core/sketch.js";
|
|
4
4
|
import extrude from "../../core/extrude.js";
|
|
5
5
|
import mirror from "../../core/mirror.js";
|
|
6
|
+
import local from "../../core/local.js";
|
|
6
7
|
import { move, rect, circle } from "../../core/2d/index.js";
|
|
7
8
|
import { ShapeOps } from "../../oc/shape-ops.js";
|
|
8
9
|
describe("mirror (2D)", () => {
|
|
@@ -72,4 +73,37 @@ describe("mirror (2D)", () => {
|
|
|
72
73
|
expect(shapes.length).toBeGreaterThanOrEqual(2);
|
|
73
74
|
});
|
|
74
75
|
});
|
|
76
|
+
describe("local() axis wrapper", () => {
|
|
77
|
+
it("should fail when mirroring across a world axis parallel to the sketch plane normal", () => {
|
|
78
|
+
let mirrorRef;
|
|
79
|
+
sketch("front", () => {
|
|
80
|
+
move([30, 0]);
|
|
81
|
+
const c = circle(20);
|
|
82
|
+
mirrorRef = mirror("y", c);
|
|
83
|
+
});
|
|
84
|
+
render();
|
|
85
|
+
expect(mirrorRef.getError()).toBeTruthy();
|
|
86
|
+
});
|
|
87
|
+
it("should mirror across sketch-local Y on a non-XY plane via local()", () => {
|
|
88
|
+
sketch("front", () => {
|
|
89
|
+
move([30, 0]);
|
|
90
|
+
const c = circle(20);
|
|
91
|
+
mirror(local("y"), c);
|
|
92
|
+
});
|
|
93
|
+
const e = extrude(10);
|
|
94
|
+
render();
|
|
95
|
+
const shapes = e.getShapes();
|
|
96
|
+
expect(shapes.length).toBeGreaterThanOrEqual(1);
|
|
97
|
+
// Mirroring across sketch-local Y on the "front" plane flips world X,
|
|
98
|
+
// so bbox should span both sides of X=0.
|
|
99
|
+
const allBBoxes = shapes.map(s => ShapeOps.getBoundingBox(s));
|
|
100
|
+
const minX = Math.min(...allBBoxes.map(b => b.minX));
|
|
101
|
+
const maxX = Math.max(...allBBoxes.map(b => b.maxX));
|
|
102
|
+
expect(minX).toBeLessThan(0);
|
|
103
|
+
expect(maxX).toBeGreaterThan(0);
|
|
104
|
+
});
|
|
105
|
+
it("should throw when local() is called outside a sketch", () => {
|
|
106
|
+
expect(() => local("y")).toThrow();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
75
109
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|