fluidcad 0.0.24 → 0.0.26
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 +6 -2
- package/lib/dist/common/scene-object.js +28 -16
- package/lib/dist/common/transformable-primitive.d.ts +16 -0
- package/lib/dist/common/transformable-primitive.js +116 -0
- package/lib/dist/core/2d/tarc.d.ts +2 -2
- package/lib/dist/core/copy.js +5 -4
- package/lib/dist/core/cylinder.d.ts +2 -2
- package/lib/dist/core/index.d.ts +2 -1
- package/lib/dist/core/index.js +1 -0
- package/lib/dist/core/interfaces.d.ts +80 -21
- package/lib/dist/core/local.d.ts +12 -0
- package/lib/dist/core/local.js +18 -0
- package/lib/dist/core/mirror.d.ts +2 -2
- package/lib/dist/core/mirror.js +42 -60
- package/lib/dist/core/plane.d.ts +5 -7
- package/lib/dist/core/sphere.d.ts +3 -3
- package/lib/dist/features/2d/circle.js +2 -1
- 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/cylinder.d.ts +2 -2
- package/lib/dist/features/cylinder.js +2 -2
- package/lib/dist/features/mirror-shape.js +10 -0
- package/lib/dist/features/plane-from-object.d.ts +1 -1
- package/lib/dist/features/plane-from-object.js +11 -6
- package/lib/dist/features/plane-mid.d.ts +1 -1
- package/lib/dist/features/plane.d.ts +1 -1
- package/lib/dist/features/select.js +0 -1
- package/lib/dist/features/sphere.d.ts +2 -2
- package/lib/dist/features/sphere.js +2 -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 +2 -2
- package/lib/dist/math/index.js +1 -1
- package/lib/dist/math/plane.d.ts +1 -2
- package/lib/dist/math/plane.js +20 -19
- package/lib/dist/rendering/render.js +8 -0
- package/lib/dist/tests/features/cut-two-distances.test.js +1 -1
- package/lib/dist/tests/features/cut.test.js +1 -1
- package/lib/dist/tests/features/mirror2d.test.js +34 -0
- package/lib/dist/tests/features/primitive-chain.test.d.ts +1 -0
- package/lib/dist/tests/features/primitive-chain.test.js +45 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- 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-BeLxRMCv.js} +4 -4
- package/ui/dist/index.html +1 -1
package/lib/dist/core/plane.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { PlaneLike, PlaneTransformOptions } from "../math/plane.js";
|
|
2
2
|
import { IPlane, ISceneObject } from "./interfaces.js";
|
|
3
|
-
export type PlaneRenderableOptions = PlaneTransformOptions
|
|
4
|
-
sticky?: boolean;
|
|
5
|
-
};
|
|
3
|
+
export type PlaneRenderableOptions = PlaneTransformOptions;
|
|
6
4
|
interface PlaneFunction {
|
|
7
5
|
/**
|
|
8
6
|
* Creates a plane from a standard plane or normal vector.
|
|
@@ -12,7 +10,7 @@ interface PlaneFunction {
|
|
|
12
10
|
/**
|
|
13
11
|
* Creates a plane with transform options.
|
|
14
12
|
* @param plane - The standard plane or normal vector
|
|
15
|
-
* @param options - Transform options (offset,
|
|
13
|
+
* @param options - Transform options (offset, rotate, etc.)
|
|
16
14
|
*/
|
|
17
15
|
(plane: PlaneLike, options: PlaneRenderableOptions): IPlane;
|
|
18
16
|
/**
|
|
@@ -29,7 +27,7 @@ interface PlaneFunction {
|
|
|
29
27
|
/**
|
|
30
28
|
* Creates a plane from a face selection with transform options.
|
|
31
29
|
* @param selection - The selected face to create a plane from
|
|
32
|
-
* @param options - Transform options (offset,
|
|
30
|
+
* @param options - Transform options (offset, rotate, etc.)
|
|
33
31
|
*/
|
|
34
32
|
(selection: ISceneObject, options: PlaneRenderableOptions): IPlane;
|
|
35
33
|
/**
|
|
@@ -41,14 +39,14 @@ interface PlaneFunction {
|
|
|
41
39
|
/**
|
|
42
40
|
* Transforms an existing plane with options.
|
|
43
41
|
* @param plane - The existing plane to transform
|
|
44
|
-
* @param options - Transform options (offset,
|
|
42
|
+
* @param options - Transform options (offset, rotate, etc.)
|
|
45
43
|
*/
|
|
46
44
|
(plane: IPlane, options: PlaneRenderableOptions): IPlane;
|
|
47
45
|
/**
|
|
48
46
|
* Creates a plane midway between two standard planes or normal vectors.
|
|
49
47
|
* @param p1 - The first standard plane or normal vector
|
|
50
48
|
* @param p2 - The second standard plane or normal vector
|
|
51
|
-
* @param options - Transform options (offset,
|
|
49
|
+
* @param options - Transform options (offset, rotate, etc.)
|
|
52
50
|
*/
|
|
53
51
|
(p1: PlaneLike, p2: PlaneLike, options?: PlaneRenderableOptions): IPlane;
|
|
54
52
|
/**
|
|
@@ -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;
|
|
@@ -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
|
}
|
|
@@ -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
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class Cylinder extends
|
|
1
|
+
import { TransformablePrimitive } from "../common/transformable-primitive.js";
|
|
2
|
+
export declare class Cylinder extends TransformablePrimitive {
|
|
3
3
|
radius: number;
|
|
4
4
|
height: number;
|
|
5
5
|
constructor(radius: number, height: number);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransformablePrimitive } from "../common/transformable-primitive.js";
|
|
2
2
|
import { Primitives } from "../oc/primitives.js";
|
|
3
|
-
export class Cylinder extends
|
|
3
|
+
export class Cylinder extends TransformablePrimitive {
|
|
4
4
|
radius;
|
|
5
5
|
height;
|
|
6
6
|
constructor(radius, height) {
|
|
@@ -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() {
|
|
@@ -23,7 +23,7 @@ export declare class PlaneFromObject extends PlaneObjectBase {
|
|
|
23
23
|
xDirection: import("../math/vector3d.js").Vector3d;
|
|
24
24
|
yDirection: import("../math/vector3d.js").Vector3d;
|
|
25
25
|
normal: import("../math/vector3d.js").Vector3d;
|
|
26
|
-
options:
|
|
26
|
+
options: import("../math/plane.js").PlaneTransformOptions;
|
|
27
27
|
center: any;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
@@ -24,24 +24,29 @@ export class PlaneFromObject extends PlaneObjectBase {
|
|
|
24
24
|
sourceFace = extract.sourceFace;
|
|
25
25
|
}
|
|
26
26
|
this.sourceObject.removeShapes(this);
|
|
27
|
-
if (this.options) {
|
|
28
|
-
plane = plane.transform(this.options);
|
|
29
|
-
}
|
|
30
27
|
if (sourceFace) {
|
|
31
28
|
const bbox = ShapeOps.getBoundingBox(sourceFace.getShape());
|
|
32
29
|
center = new Point(bbox.centerX, bbox.centerY, bbox.centerZ);
|
|
33
30
|
}
|
|
34
|
-
if (
|
|
35
|
-
|
|
31
|
+
if (this.options) {
|
|
32
|
+
// Apply the same transform to the center so the preview face stays on
|
|
33
|
+
// the rotated plane instead of floating at its pre-rotation position.
|
|
34
|
+
const matrix = plane.getTransformMatrix(this.options);
|
|
35
|
+
plane = plane.applyMatrix(matrix);
|
|
36
|
+
if (center) {
|
|
37
|
+
center = center.transform(matrix);
|
|
38
|
+
}
|
|
36
39
|
}
|
|
37
40
|
const transform = context?.getTransform() ?? null;
|
|
38
41
|
if (transform) {
|
|
39
42
|
plane = plane.applyMatrix(transform);
|
|
40
43
|
if (center) {
|
|
41
44
|
center = center.transform(transform);
|
|
42
|
-
this.setState('plane-center', center);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
47
|
+
if (center) {
|
|
48
|
+
this.setState('plane-center', center);
|
|
49
|
+
}
|
|
45
50
|
this.setState('plane', plane);
|
|
46
51
|
const face = FaceOps.planeToFace(plane, center);
|
|
47
52
|
face.markAsMetaShape();
|
|
@@ -13,6 +13,6 @@ export declare class PlaneMiddleRenderable extends PlaneObjectBase {
|
|
|
13
13
|
yDirection: import("../math/vector3d.js").Vector3d;
|
|
14
14
|
normal: import("../math/vector3d.js").Vector3d;
|
|
15
15
|
center: import("../math/point.js").Point;
|
|
16
|
-
options:
|
|
16
|
+
options: import("../math/plane.js").PlaneTransformOptions;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
@@ -16,6 +16,6 @@ export declare class PlaneObject extends PlaneObjectBase {
|
|
|
16
16
|
yDirection: import("../math/vector3d.js").Vector3d;
|
|
17
17
|
normal: import("../math/vector3d.js").Vector3d;
|
|
18
18
|
center: import("../math/point.js").Point;
|
|
19
|
-
options:
|
|
19
|
+
options: import("../math/plane.js").PlaneTransformOptions;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
@@ -65,7 +65,6 @@ export class SelectSceneObject extends SceneObject {
|
|
|
65
65
|
}
|
|
66
66
|
transform(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) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class Sphere extends
|
|
1
|
+
import { TransformablePrimitive } from "../common/transformable-primitive.js";
|
|
2
|
+
export declare class Sphere extends TransformablePrimitive {
|
|
3
3
|
radius: number;
|
|
4
4
|
angle: number;
|
|
5
5
|
constructor(radius: number, angle: number);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransformablePrimitive } from "../common/transformable-primitive.js";
|
|
2
2
|
import { Primitives } from "../oc/primitives.js";
|
|
3
|
-
export class Sphere extends
|
|
3
|
+
export class Sphere extends TransformablePrimitive {
|
|
4
4
|
radius;
|
|
5
5
|
angle;
|
|
6
6
|
constructor(radius, angle) {
|
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";
|
|
6
|
-
export { Plane, PlaneLike, StandardPlane, PlaneTransformOptions,
|
|
5
|
+
export { Axis, AxisLike, StandardAxis, AxisTransformOptions, isAxisLike, isStandardAxis, toAxis } from "./axis.js";
|
|
6
|
+
export { Plane, PlaneLike, StandardPlane, PlaneTransformOptions, 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";
|
package/lib/dist/math/plane.d.ts
CHANGED
|
@@ -4,13 +4,11 @@ import { Axis, AxisLike } from "./axis.js";
|
|
|
4
4
|
import { Matrix4 } from "./matrix4.js";
|
|
5
5
|
import { PlaneObjectBase } from "../features/plane-renderable-base.js";
|
|
6
6
|
import { IPlane } from "../core/interfaces.js";
|
|
7
|
-
export type RotationSpace = 'local' | 'global';
|
|
8
7
|
export interface PlaneTransformOptions {
|
|
9
8
|
offset?: number;
|
|
10
9
|
rotateX?: number;
|
|
11
10
|
rotateY?: number;
|
|
12
11
|
rotateZ?: number;
|
|
13
|
-
rotationSpace?: RotationSpace;
|
|
14
12
|
}
|
|
15
13
|
export declare class Plane {
|
|
16
14
|
readonly origin: Point;
|
|
@@ -25,6 +23,7 @@ export declare class Plane {
|
|
|
25
23
|
localToWorld(point: Point2D): Point;
|
|
26
24
|
offset(distance: number): Plane;
|
|
27
25
|
transform(options: PlaneTransformOptions): Plane;
|
|
26
|
+
getTransformMatrix(options: PlaneTransformOptions): Matrix4;
|
|
28
27
|
applyMatrix(matrix: Matrix4): Plane;
|
|
29
28
|
translateAlongNormal(distance: number): Plane;
|
|
30
29
|
translate(dx: number, dy: number, dz: number): Plane;
|
package/lib/dist/math/plane.js
CHANGED
|
@@ -36,36 +36,37 @@ export class Plane {
|
|
|
36
36
|
return new Plane(newOrigin, this.xDirection, this.normal);
|
|
37
37
|
}
|
|
38
38
|
transform(options) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
return this.applyMatrix(this.getTransformMatrix(options));
|
|
40
|
+
}
|
|
41
|
+
getTransformMatrix(options) {
|
|
42
|
+
const offset = options.offset || 0;
|
|
43
|
+
const offsetVec = this.normal.multiply(offset);
|
|
44
|
+
const offsetMatrix = offset
|
|
45
|
+
? Matrix4.fromTranslation(offsetVec.x, offsetVec.y, offsetVec.z)
|
|
46
|
+
: Matrix4.identity();
|
|
43
47
|
const hasRotation = options.rotateX || options.rotateY || options.rotateZ;
|
|
44
48
|
if (!hasRotation) {
|
|
45
|
-
return
|
|
49
|
+
return offsetMatrix;
|
|
46
50
|
}
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
const xAxis = useGlobal ? Vector3d.unitX() : result.xDirection;
|
|
50
|
-
const yAxis = useGlobal ? Vector3d.unitY() : result.yDirection;
|
|
51
|
-
const zAxis = useGlobal ? Vector3d.unitZ() : result.normal;
|
|
52
|
-
// Compose all rotations into a single quaternion to avoid gimbal lock
|
|
51
|
+
// Compose all rotations into a single quaternion to avoid gimbal lock.
|
|
52
|
+
// Axes are taken from the current plane (offset doesn't change orientation).
|
|
53
53
|
let q = Quaternion.identity();
|
|
54
54
|
if (options.rotateX) {
|
|
55
|
-
q = q.multiply(Quaternion.fromAxisAngle(
|
|
55
|
+
q = q.multiply(Quaternion.fromAxisAngle(this.xDirection, rad(options.rotateX)));
|
|
56
56
|
}
|
|
57
57
|
if (options.rotateY) {
|
|
58
|
-
q = q.multiply(Quaternion.fromAxisAngle(
|
|
58
|
+
q = q.multiply(Quaternion.fromAxisAngle(this.yDirection, rad(options.rotateY)));
|
|
59
59
|
}
|
|
60
60
|
if (options.rotateZ) {
|
|
61
|
-
q = q.multiply(Quaternion.fromAxisAngle(
|
|
61
|
+
q = q.multiply(Quaternion.fromAxisAngle(this.normal, rad(options.rotateZ)));
|
|
62
62
|
}
|
|
63
|
-
//
|
|
64
|
-
const
|
|
63
|
+
// Rotate around the offset-applied origin (the plane's origin after offset).
|
|
64
|
+
const pivot = this.origin.add(offsetVec);
|
|
65
|
+
const toOrigin = Matrix4.fromTranslation(-pivot.x, -pivot.y, -pivot.z);
|
|
65
66
|
const rotation = Matrix4.fromQuaternion(q);
|
|
66
|
-
const fromOrigin = Matrix4.fromTranslation(
|
|
67
|
-
const
|
|
68
|
-
return
|
|
67
|
+
const fromOrigin = Matrix4.fromTranslation(pivot.x, pivot.y, pivot.z);
|
|
68
|
+
const rotationMatrix = fromOrigin.multiply(rotation).multiply(toOrigin);
|
|
69
|
+
return rotationMatrix.multiply(offsetMatrix);
|
|
69
70
|
}
|
|
70
71
|
applyMatrix(matrix) {
|
|
71
72
|
return new Plane(matrix.transformPoint(this.origin), matrix.transformDirection(this.xDirection), matrix.transformDirection(this.normal), matrix.transformDirection(this.yDirection));
|
|
@@ -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);
|
|
@@ -97,7 +97,7 @@ describe("cut two distances", () => {
|
|
|
97
97
|
move([25, 25]);
|
|
98
98
|
rect(50, 50);
|
|
99
99
|
});
|
|
100
|
-
cut(20, 10).
|
|
100
|
+
cut(20, 10).scope(e1);
|
|
101
101
|
const scene = render();
|
|
102
102
|
// First box is cut, second box is untouched — 2 shapes
|
|
103
103
|
expect(countShapes(scene)).toBe(2);
|
|
@@ -154,7 +154,7 @@ describe("cut", () => {
|
|
|
154
154
|
move([25, 25]);
|
|
155
155
|
rect(50, 50);
|
|
156
156
|
});
|
|
157
|
-
cut(20).
|
|
157
|
+
cut(20).scope(e1);
|
|
158
158
|
const scene = render();
|
|
159
159
|
// First box is cut (modified), second box is untouched — 2 shapes
|
|
160
160
|
expect(countShapes(scene)).toBe(2);
|