fluidcad 0.0.21 → 0.0.23
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/README.md +7 -2
- package/lib/dist/common/shape.d.ts +7 -0
- package/lib/dist/common/shape.js +7 -0
- package/lib/dist/core/copy.js +1 -1
- package/lib/dist/core/draft.d.ts +16 -0
- package/lib/dist/core/draft.js +29 -0
- package/lib/dist/core/index.d.ts +2 -3
- package/lib/dist/core/index.js +1 -1
- package/lib/dist/core/interfaces.d.ts +2 -0
- package/lib/dist/core/part.d.ts +2 -6
- package/lib/dist/core/part.js +12 -22
- package/lib/dist/core/trim.d.ts +12 -5
- package/lib/dist/core/trim.js +7 -2
- package/lib/dist/features/2d/sketch.js +23 -17
- package/lib/dist/features/copy-circular.js +1 -0
- package/lib/dist/features/copy-circular2d.js +1 -0
- package/lib/dist/features/copy-linear.js +4 -5
- package/lib/dist/features/copy-linear2d.js +4 -5
- package/lib/dist/features/draft.d.ts +15 -0
- package/lib/dist/features/draft.js +88 -0
- package/lib/dist/features/extrude-base.js +1 -0
- package/lib/dist/features/remove.d.ts +2 -0
- package/lib/dist/features/remove.js +7 -0
- package/lib/dist/features/trim2d.d.ts +16 -4
- package/lib/dist/features/trim2d.js +80 -29
- package/lib/dist/filters/edge/above-below.d.ts +20 -0
- package/lib/dist/filters/edge/above-below.js +57 -0
- package/lib/dist/filters/edge/edge-filter.d.ts +40 -6
- package/lib/dist/filters/edge/edge-filter.js +76 -8
- package/lib/dist/filters/edge/intersects-with.d.ts +18 -0
- package/lib/dist/filters/edge/intersects-with.js +38 -0
- package/lib/dist/filters/edge/on-plane.d.ts +4 -2
- package/lib/dist/filters/edge/on-plane.js +37 -12
- package/lib/dist/filters/filter-builder-base.d.ts +0 -5
- package/lib/dist/filters/filter-builder-base.js +12 -0
- package/lib/dist/helpers/clone-transform.js +3 -0
- package/lib/dist/oc/draft-ops.d.ts +5 -0
- package/lib/dist/oc/draft-ops.js +51 -0
- package/lib/dist/oc/edge-query.d.ts +5 -1
- package/lib/dist/oc/edge-query.js +40 -0
- package/lib/dist/oc/mesh.d.ts +2 -0
- package/lib/dist/oc/mesh.js +14 -6
- package/lib/dist/rendering/mesh-transform.d.ts +3 -0
- package/lib/dist/rendering/mesh-transform.js +22 -0
- package/lib/dist/rendering/render-solid.js +3 -2
- package/lib/dist/rendering/render.js +28 -6
- package/lib/dist/tests/features/chamfer.test.js +1 -1
- package/lib/dist/tests/features/draft.test.d.ts +1 -0
- package/lib/dist/tests/features/draft.test.js +147 -0
- package/lib/dist/tests/features/fillet.test.js +1 -1
- package/lib/dist/tests/features/part.test.js +69 -114
- package/lib/dist/tests/features/select.test.js +101 -3
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -3
- package/server/dist/fluidcad-server.d.ts +2 -0
- package/server/dist/fluidcad-server.js +10 -0
- package/server/dist/routes/actions.js +20 -0
- package/server/dist/vite-manager.js +7 -1
- package/ui/dist/assets/{index-B1LkrBga.js → index-CqP_mgZk.js} +23 -12
- package/ui/dist/assets/{index-BfcNNxXr.css → index-gPoNOiIs.css} +1 -1
- package/ui/dist/index.html +2 -2
- package/lib/dist/core/use.d.ts +0 -5
- package/lib/dist/core/use.js +0 -22
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Re-apply modeling features based on matrix transformations. Move, rotate, or mir
|
|
|
71
71
|
|
|
72
72
|
```javascript
|
|
73
73
|
sketch("xy", () => {
|
|
74
|
-
rect(200, 100).
|
|
74
|
+
rect(200, 100).centered()
|
|
75
75
|
})
|
|
76
76
|
|
|
77
77
|
const e1 = extrude(20)
|
|
@@ -155,7 +155,12 @@ Step-by-step tutorials from simple shapes to exam-level parts. [Browse all tutor
|
|
|
155
155
|
<strong>CSWP Sample Exam</strong>
|
|
156
156
|
</a>
|
|
157
157
|
</td>
|
|
158
|
-
<td width="33%"
|
|
158
|
+
<td align="center" width="33%">
|
|
159
|
+
<a href="https://fluidcad.io/docs/tutorials/hinge-bracket">
|
|
160
|
+
<img src="https://fluidcad.io/assets/images/hinge-bracket-final-137547b475db21736d78b5b13f8db48b.png" alt="Hinge Bracket" height="180" /><br />
|
|
161
|
+
<strong>Hinge Bracket</strong>
|
|
162
|
+
</a>
|
|
163
|
+
</td>
|
|
159
164
|
</tr>
|
|
160
165
|
</table>
|
|
161
166
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TopoDS_Shape } from "occjs-wrapper";
|
|
2
2
|
import { ShapeType } from "./shape-type.js";
|
|
3
3
|
import { SceneObjectMesh } from "../rendering/scene.js";
|
|
4
|
+
import { Matrix4 } from "../math/matrix4.js";
|
|
4
5
|
export interface ShapeFilter {
|
|
5
6
|
excludeMeta?: boolean;
|
|
6
7
|
excludeGuide?: boolean;
|
|
@@ -17,6 +18,7 @@ export declare abstract class Shape<T extends TopoDS_Shape = TopoDS_Shape> {
|
|
|
17
18
|
color: string;
|
|
18
19
|
}>;
|
|
19
20
|
private meshes;
|
|
21
|
+
private _meshSource;
|
|
20
22
|
constructor(shape: T);
|
|
21
23
|
abstract getType(): ShapeType;
|
|
22
24
|
getShape(): T;
|
|
@@ -36,6 +38,11 @@ export declare abstract class Shape<T extends TopoDS_Shape = TopoDS_Shape> {
|
|
|
36
38
|
isGuideShape(): boolean;
|
|
37
39
|
getMeshes(): SceneObjectMesh[];
|
|
38
40
|
setMeshes(meshes: SceneObjectMesh[]): void;
|
|
41
|
+
setMeshSource(source: Shape, matrix: Matrix4): void;
|
|
42
|
+
getMeshSource(): {
|
|
43
|
+
shape: Shape;
|
|
44
|
+
matrix: Matrix4;
|
|
45
|
+
} | null;
|
|
39
46
|
setColor(face: TopoDS_Shape, color: string): void;
|
|
40
47
|
getColor(face: TopoDS_Shape): string | undefined;
|
|
41
48
|
hasColors(): boolean;
|
package/lib/dist/common/shape.js
CHANGED
|
@@ -8,6 +8,7 @@ export class Shape {
|
|
|
8
8
|
id;
|
|
9
9
|
colorMap = [];
|
|
10
10
|
meshes;
|
|
11
|
+
_meshSource = null;
|
|
11
12
|
constructor(shape) {
|
|
12
13
|
this.shape = shape;
|
|
13
14
|
this.id = randomUUID();
|
|
@@ -70,6 +71,12 @@ export class Shape {
|
|
|
70
71
|
setMeshes(meshes) {
|
|
71
72
|
this.meshes = meshes;
|
|
72
73
|
}
|
|
74
|
+
setMeshSource(source, matrix) {
|
|
75
|
+
this._meshSource = { shape: source, matrix };
|
|
76
|
+
}
|
|
77
|
+
getMeshSource() {
|
|
78
|
+
return this._meshSource;
|
|
79
|
+
}
|
|
73
80
|
setColor(face, color) {
|
|
74
81
|
if (this.isEdge()) {
|
|
75
82
|
throw new Error("Cannot set color on edge shape");
|
package/lib/dist/core/copy.js
CHANGED
|
@@ -16,7 +16,7 @@ function build(context) {
|
|
|
16
16
|
const restObjects = args.slice(3);
|
|
17
17
|
const objects = restObjects.length > 0
|
|
18
18
|
? restObjects
|
|
19
|
-
:
|
|
19
|
+
: null;
|
|
20
20
|
if (type === 'linear') {
|
|
21
21
|
const axisArg = args[1];
|
|
22
22
|
const axes = Array.isArray(axisArg)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ISceneObject, IDraft } from "./interfaces.js";
|
|
2
|
+
interface DraftFunction {
|
|
3
|
+
/**
|
|
4
|
+
* Applies a draft angle to the last selected faces.
|
|
5
|
+
* @param angle - The draft angle in degrees
|
|
6
|
+
*/
|
|
7
|
+
(angle: number): IDraft;
|
|
8
|
+
/**
|
|
9
|
+
* Applies a draft angle to the given face selections.
|
|
10
|
+
* @param angle - The draft angle in degrees
|
|
11
|
+
* @param selections - The face selections to draft
|
|
12
|
+
*/
|
|
13
|
+
(angle: number, ...selections: ISceneObject[]): IDraft;
|
|
14
|
+
}
|
|
15
|
+
declare const _default: DraftFunction;
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Draft } from "../features/draft.js";
|
|
2
|
+
import { SceneObject } from "../common/scene-object.js";
|
|
3
|
+
import { registerBuilder } from "../index.js";
|
|
4
|
+
import { rad } from "../helpers/math-helpers.js";
|
|
5
|
+
function build(context) {
|
|
6
|
+
return function draft() {
|
|
7
|
+
const args = Array.from(arguments);
|
|
8
|
+
const selections = [];
|
|
9
|
+
while (args.length > 0 && args[args.length - 1] instanceof SceneObject) {
|
|
10
|
+
selections.unshift(args.pop());
|
|
11
|
+
}
|
|
12
|
+
if (selections.length === 0) {
|
|
13
|
+
const implicit = context.getLastSelection() || undefined;
|
|
14
|
+
if (implicit) {
|
|
15
|
+
selections.push(implicit);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const angleDeg = (args.length >= 1 && typeof args[0] === 'number')
|
|
19
|
+
? args[0]
|
|
20
|
+
: 5;
|
|
21
|
+
for (const sel of selections) {
|
|
22
|
+
context.addSceneObject(sel);
|
|
23
|
+
}
|
|
24
|
+
const draft = new Draft(rad(angleDeg), selections);
|
|
25
|
+
context.addSceneObject(draft);
|
|
26
|
+
return draft;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export default registerBuilder(build);
|
package/lib/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ISceneObject, IFuseable, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve } from "./interfaces.js";
|
|
1
|
+
export type { ISceneObject, IFuseable, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft } from "./interfaces.js";
|
|
2
2
|
export { default as axis } from "./axis.js";
|
|
3
3
|
export { default as plane } from "./plane.js";
|
|
4
4
|
export { default as sketch } from "./sketch.js";
|
|
@@ -23,11 +23,10 @@ export { default as load } from "./load.js";
|
|
|
23
23
|
export { default as loft } from "./loft.js";
|
|
24
24
|
export { default as sweep } from "./sweep.js";
|
|
25
25
|
export { default as color } from "./color.js";
|
|
26
|
+
export { default as draft } from "./draft.js";
|
|
26
27
|
export { default as remove } from "./remove.js";
|
|
27
28
|
export { default as split } from "./split.js";
|
|
28
29
|
export { default as trim } from "./trim.js";
|
|
29
30
|
export { default as part } from "./part.js";
|
|
30
|
-
export { default as use } from "./use.js";
|
|
31
|
-
export type { PartHandle } from "./part.js";
|
|
32
31
|
export * from "./2d/index.js";
|
|
33
32
|
export { breakpoint } from "./breakpoint.js";
|
package/lib/dist/core/index.js
CHANGED
|
@@ -22,10 +22,10 @@ export { default as load } from "./load.js";
|
|
|
22
22
|
export { default as loft } from "./loft.js";
|
|
23
23
|
export { default as sweep } from "./sweep.js";
|
|
24
24
|
export { default as color } from "./color.js";
|
|
25
|
+
export { default as draft } from "./draft.js";
|
|
25
26
|
export { default as remove } from "./remove.js";
|
|
26
27
|
export { default as split } from "./split.js";
|
|
27
28
|
export { default as trim } from "./trim.js";
|
|
28
29
|
export { default as part } from "./part.js";
|
|
29
|
-
export { default as use } from "./use.js";
|
|
30
30
|
export * from "./2d/index.js";
|
|
31
31
|
export { breakpoint } from "./breakpoint.js";
|
|
@@ -562,6 +562,8 @@ export interface ISweep extends IFuseable {
|
|
|
562
562
|
*/
|
|
563
563
|
capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
|
|
564
564
|
}
|
|
565
|
+
export interface IDraft extends ISceneObject {
|
|
566
|
+
}
|
|
565
567
|
export interface IShell extends ISceneObject {
|
|
566
568
|
/**
|
|
567
569
|
* Selects the inner wall faces created by the shell operation (from thickness removal).
|
package/lib/dist/core/part.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
name: string;
|
|
4
|
-
_callback: (options: T) => void;
|
|
5
|
-
};
|
|
6
|
-
declare function part<T = any>(name: string, callback: (options: T) => void): PartHandle<T>;
|
|
1
|
+
import { ISceneObject } from "./interfaces.js";
|
|
2
|
+
declare function part(name: string, callback: () => void): ISceneObject;
|
|
7
3
|
export default part;
|
package/lib/dist/core/part.js
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
import { captureSourceLocation } from "../index.js";
|
|
2
|
-
import { getCurrentScene
|
|
2
|
+
import { getCurrentScene } from "../scene-manager.js";
|
|
3
3
|
import { Part } from "../features/part.js";
|
|
4
4
|
function part(name, callback) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
5
|
+
const scene = getCurrentScene();
|
|
6
|
+
if (!scene) {
|
|
7
|
+
throw new Error("part() must be called within a scene context");
|
|
8
|
+
}
|
|
10
9
|
const sourceLocation = captureSourceLocation();
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
&& sourceLocation.filePath === currentFile;
|
|
15
|
-
if (isDirectEdit) {
|
|
16
|
-
const scene = getCurrentScene();
|
|
17
|
-
if (scene) {
|
|
18
|
-
const partObj = new Part(name);
|
|
19
|
-
if (sourceLocation) {
|
|
20
|
-
partObj.setSourceLocation(sourceLocation);
|
|
21
|
-
}
|
|
22
|
-
scene.startProgressiveContainer(partObj);
|
|
23
|
-
callback(undefined);
|
|
24
|
-
scene.endProgressiveContainer();
|
|
25
|
-
}
|
|
10
|
+
const partObj = new Part(name);
|
|
11
|
+
if (sourceLocation) {
|
|
12
|
+
partObj.setSourceLocation(sourceLocation);
|
|
26
13
|
}
|
|
27
|
-
|
|
14
|
+
scene.startProgressiveContainer(partObj);
|
|
15
|
+
callback();
|
|
16
|
+
scene.endProgressiveContainer();
|
|
17
|
+
return partObj;
|
|
28
18
|
}
|
|
29
19
|
export default part;
|
package/lib/dist/core/trim.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { Point2DLike } from "../math/point.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
3
|
+
interface ITrim {
|
|
4
|
+
/**
|
|
5
|
+
* Enters interactive trimming mode, optionally trimming edges at the given points.
|
|
6
|
+
* @param points - Points where geometry should be trimmed; the nearest edge segment to each point is removed.
|
|
7
|
+
*/
|
|
8
|
+
pick(...points: Point2DLike[]): ITrim;
|
|
9
|
+
}
|
|
3
10
|
interface TrimFunction {
|
|
4
11
|
/** Trims all sketch geometry segments. */
|
|
5
|
-
():
|
|
12
|
+
(): ITrim;
|
|
6
13
|
/**
|
|
7
|
-
* Trims sketch geometry segments
|
|
8
|
-
* @param
|
|
14
|
+
* Trims sketch geometry segments matching the given edge filters.
|
|
15
|
+
* @param filters - Edge filters that select which edges to remove
|
|
9
16
|
*/
|
|
10
|
-
(...
|
|
17
|
+
(...filters: EdgeFilterBuilder[]): ITrim;
|
|
11
18
|
}
|
|
12
19
|
declare const _default: TrimFunction;
|
|
13
20
|
export default _default;
|
package/lib/dist/core/trim.js
CHANGED
|
@@ -9,10 +9,15 @@ function build(context) {
|
|
|
9
9
|
}
|
|
10
10
|
const trim2d = new Trim2D();
|
|
11
11
|
if (args.length > 0) {
|
|
12
|
-
trim2d.
|
|
12
|
+
trim2d.setFilters(...args);
|
|
13
13
|
}
|
|
14
14
|
context.addSceneObject(trim2d);
|
|
15
|
-
return
|
|
15
|
+
return {
|
|
16
|
+
pick(...points) {
|
|
17
|
+
trim2d.pick(...points.map(p => normalizePoint2D(p)));
|
|
18
|
+
return this;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
16
21
|
};
|
|
17
22
|
}
|
|
18
23
|
export default registerBuilder(build);
|
|
@@ -83,33 +83,39 @@ export class Sketch extends SceneObject {
|
|
|
83
83
|
const source = this.getCloneSource();
|
|
84
84
|
const transform = context?.getTransform();
|
|
85
85
|
if (source instanceof Sketch && transform) {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
86
|
+
const sourceChildren = source.getChildren();
|
|
87
|
+
const clonedChildren = this.getChildren();
|
|
88
|
+
for (let i = 0; i < sourceChildren.length; i++) {
|
|
89
|
+
const sourceChild = sourceChildren[i];
|
|
90
|
+
const clonedChild = clonedChildren[i];
|
|
91
|
+
if (!clonedChild) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const shapes = sourceChild.getAddedShapes();
|
|
95
|
+
const removedShapes = sourceChild.getRemovedShapes();
|
|
96
|
+
for (const shape of shapes) {
|
|
97
|
+
if (shape.isMetaShape() || shape.isGuideShape()) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const isRemovedBySibling = removedShapes.some(s => s.shape === shape && s.removedBy?.parentId === source.id);
|
|
101
|
+
if (isRemovedBySibling) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const transformed = ShapeOps.transform(shape, transform);
|
|
105
|
+
clonedChild.addShape(transformed);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
110
|
getEdges() {
|
|
92
|
-
const clonedEdges = this.getState('cloned-edges');
|
|
93
|
-
if (clonedEdges) {
|
|
94
|
-
return clonedEdges;
|
|
95
|
-
}
|
|
96
111
|
return [...this.getEdgesWithOwner().keys()];
|
|
97
112
|
}
|
|
98
113
|
getEdgesWithOwner() {
|
|
99
114
|
const children = this.getChildren();
|
|
100
115
|
const result = new Map();
|
|
101
|
-
// get edges and filter out the ones that were removed by non-siblings
|
|
102
116
|
for (const child of children) {
|
|
103
|
-
const shapes = child.
|
|
104
|
-
const removedShapes = child.getRemovedShapes();
|
|
117
|
+
const shapes = child.getShapes();
|
|
105
118
|
for (const shape of shapes) {
|
|
106
|
-
if (shape.isMetaShape() || shape.isGuideShape()) {
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
const isRemovedBySibling = removedShapes.some(s => s.shape === shape && s.removedBy?.parentId === this.id);
|
|
110
|
-
if (isRemovedBySibling) {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
119
|
if (shape instanceof Edge) {
|
|
114
120
|
result.set(shape, child);
|
|
115
121
|
}
|
|
@@ -34,6 +34,7 @@ export class CopyCircular extends SceneObject {
|
|
|
34
34
|
for (const obj of objects) {
|
|
35
35
|
for (const shape of obj.getShapes()) {
|
|
36
36
|
const transformed = ShapeOps.transform(shape, matrix);
|
|
37
|
+
transformed.setMeshSource(shape, matrix);
|
|
37
38
|
this.addShape(transformed);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -41,6 +41,7 @@ export class CopyCircular2D extends GeometrySceneObject {
|
|
|
41
41
|
for (const obj of objects) {
|
|
42
42
|
for (const shape of obj.getShapes()) {
|
|
43
43
|
const transformed = ShapeOps.transform(shape, matrix);
|
|
44
|
+
transformed.setMeshSource(shape, matrix);
|
|
44
45
|
this.addShape(transformed);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -34,6 +34,7 @@ export class CopyLinear extends SceneObject {
|
|
|
34
34
|
const axisCount = counts[a];
|
|
35
35
|
return axisCount > 1 ? len / (axisCount - 1) : 0;
|
|
36
36
|
});
|
|
37
|
+
const centerIndices = this.axes.map((_, a) => centered ? Math.floor(counts[a] / 2) : 0);
|
|
37
38
|
// Build grid positions as cartesian product of per-axis indices (0..counts[a]-1)
|
|
38
39
|
let positions = [[]];
|
|
39
40
|
for (let a = 0; a < this.axes.length; a++) {
|
|
@@ -46,23 +47,21 @@ export class CopyLinear extends SceneObject {
|
|
|
46
47
|
positions = next;
|
|
47
48
|
}
|
|
48
49
|
for (const pos of positions) {
|
|
49
|
-
if (pos.every(idx => idx ===
|
|
50
|
+
if (pos.every((idx, a) => idx === centerIndices[a]))
|
|
50
51
|
continue;
|
|
51
52
|
if (skip?.some(coord => coord.every((v, a) => v === pos[a]))) {
|
|
52
53
|
continue;
|
|
53
54
|
}
|
|
54
55
|
let matrix = Matrix4.identity();
|
|
55
56
|
for (let a = 0; a < this.axes.length; a++) {
|
|
56
|
-
const
|
|
57
|
-
const axisOffset = axisOffsets[a];
|
|
58
|
-
const startOffset = centered ? -(axisCount * axisOffset) / 2 : 0;
|
|
59
|
-
const distance = startOffset + axisOffset * pos[a];
|
|
57
|
+
const distance = (pos[a] - centerIndices[a]) * axisOffsets[a];
|
|
60
58
|
const translation = this.axes[a].direction.multiply(distance);
|
|
61
59
|
matrix = matrix.multiply(Matrix4.fromTranslationVector(translation));
|
|
62
60
|
}
|
|
63
61
|
for (const obj of objects) {
|
|
64
62
|
for (const shape of obj.getShapes()) {
|
|
65
63
|
const transformed = ShapeOps.transform(shape, matrix);
|
|
64
|
+
transformed.setMeshSource(shape, matrix);
|
|
66
65
|
this.addShape(transformed);
|
|
67
66
|
}
|
|
68
67
|
}
|
|
@@ -38,6 +38,7 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
38
38
|
const axisCount = counts[a];
|
|
39
39
|
return axisCount > 1 ? len / (axisCount - 1) : 0;
|
|
40
40
|
});
|
|
41
|
+
const centerIndices = this.axes.map((_, a) => centered ? Math.floor(counts[a] / 2) : 0);
|
|
41
42
|
// Build grid positions as cartesian product of per-axis indices (0..counts[a]-1)
|
|
42
43
|
let positions = [[]];
|
|
43
44
|
for (let a = 0; a < this.axes.length; a++) {
|
|
@@ -50,23 +51,21 @@ export class CopyLinear2D extends GeometrySceneObject {
|
|
|
50
51
|
positions = next;
|
|
51
52
|
}
|
|
52
53
|
for (const pos of positions) {
|
|
53
|
-
if (pos.every(idx => idx ===
|
|
54
|
+
if (pos.every((idx, a) => idx === centerIndices[a]))
|
|
54
55
|
continue;
|
|
55
56
|
if (skip?.some(coord => coord.every((v, a) => v === pos[a]))) {
|
|
56
57
|
continue;
|
|
57
58
|
}
|
|
58
59
|
let matrix = Matrix4.identity();
|
|
59
60
|
for (let a = 0; a < this.axes.length; a++) {
|
|
60
|
-
const
|
|
61
|
-
const axisOffset = axisOffsets[a];
|
|
62
|
-
const startOffset = centered ? -(axisCount * axisOffset) / 2 : 0;
|
|
63
|
-
const distance = startOffset + axisOffset * pos[a];
|
|
61
|
+
const distance = (pos[a] - centerIndices[a]) * axisOffsets[a];
|
|
64
62
|
const translation = this.axes[a].direction.multiply(distance);
|
|
65
63
|
matrix = matrix.multiply(Matrix4.fromTranslationVector(translation));
|
|
66
64
|
}
|
|
67
65
|
for (const obj of objects) {
|
|
68
66
|
for (const shape of obj.getShapes()) {
|
|
69
67
|
const transformed = ShapeOps.transform(shape, matrix);
|
|
68
|
+
transformed.setMeshSource(shape, matrix);
|
|
70
69
|
this.addShape(transformed);
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
|
|
2
|
+
import { IDraft } from "../core/interfaces.js";
|
|
3
|
+
export declare class Draft extends SceneObject implements IDraft {
|
|
4
|
+
private angle;
|
|
5
|
+
private _selections;
|
|
6
|
+
constructor(angle: number, selections: SceneObject[]);
|
|
7
|
+
build(context: BuildSceneObjectContext): void;
|
|
8
|
+
compareTo(other: SceneObject): boolean;
|
|
9
|
+
getDependencies(): SceneObject[];
|
|
10
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
11
|
+
getType(): string;
|
|
12
|
+
serialize(): {
|
|
13
|
+
angle: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { SceneObject } from "../common/scene-object.js";
|
|
2
|
+
import { DraftOps } from "../oc/draft-ops.js";
|
|
3
|
+
export class Draft extends SceneObject {
|
|
4
|
+
angle;
|
|
5
|
+
_selections = [];
|
|
6
|
+
constructor(angle, selections) {
|
|
7
|
+
super();
|
|
8
|
+
this.angle = angle;
|
|
9
|
+
this._selections = selections;
|
|
10
|
+
}
|
|
11
|
+
build(context) {
|
|
12
|
+
const shapeObjMap = new Map();
|
|
13
|
+
for (const obj of context.getSceneObjects()) {
|
|
14
|
+
if (obj.id === this.parentId) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const shapes = obj.getShapes({ excludeMeta: false }, 'solid');
|
|
18
|
+
for (const shape of shapes) {
|
|
19
|
+
shapeObjMap.set(shape, obj);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!shapeObjMap.size) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const allFaceShapes = [];
|
|
26
|
+
for (const sel of this._selections) {
|
|
27
|
+
allFaceShapes.push(...sel.getShapes());
|
|
28
|
+
}
|
|
29
|
+
const selectionFaces = allFaceShapes;
|
|
30
|
+
const selectionFaceRaws = selectionFaces.map(f => f.getShape());
|
|
31
|
+
const newShapes = [];
|
|
32
|
+
const allTargetShapes = Array.from(shapeObjMap.keys());
|
|
33
|
+
for (const shape of allTargetShapes) {
|
|
34
|
+
try {
|
|
35
|
+
const result = DraftOps.applyDraft(shape, selectionFaceRaws, this.angle);
|
|
36
|
+
if (!result) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
newShapes.push(result);
|
|
40
|
+
const originalObj = shapeObjMap.get(shape);
|
|
41
|
+
originalObj.removeShape(shape, this);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
newShapes.push(shape);
|
|
45
|
+
console.warn("Draft: Failed to apply draft angle.", e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const sel of this._selections) {
|
|
49
|
+
sel.removeShapes(this);
|
|
50
|
+
}
|
|
51
|
+
this.addShapes(newShapes);
|
|
52
|
+
}
|
|
53
|
+
compareTo(other) {
|
|
54
|
+
if (!(other instanceof Draft)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (!super.compareTo(other)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (this.angle !== other.angle) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (this._selections.length !== other._selections.length) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
for (let i = 0; i < this._selections.length; i++) {
|
|
67
|
+
if (!this._selections[i].compareTo(other._selections[i])) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
getDependencies() {
|
|
74
|
+
return [...this._selections];
|
|
75
|
+
}
|
|
76
|
+
createCopy(remap) {
|
|
77
|
+
const selections = this._selections.map(sel => remap.get(sel) || sel);
|
|
78
|
+
return new Draft(this.angle, selections);
|
|
79
|
+
}
|
|
80
|
+
getType() {
|
|
81
|
+
return 'draft';
|
|
82
|
+
}
|
|
83
|
+
serialize() {
|
|
84
|
+
return {
|
|
85
|
+
angle: this.angle
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -4,6 +4,8 @@ export declare class Remove extends SceneObject {
|
|
|
4
4
|
constructor(objects: SceneObject[]);
|
|
5
5
|
build(): void;
|
|
6
6
|
compareTo(other: Remove): boolean;
|
|
7
|
+
getDependencies(): SceneObject[];
|
|
8
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
7
9
|
getType(): string;
|
|
8
10
|
serialize(): {};
|
|
9
11
|
}
|
|
@@ -24,6 +24,13 @@ export class Remove extends SceneObject {
|
|
|
24
24
|
}
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
|
+
getDependencies() {
|
|
28
|
+
return this.objects;
|
|
29
|
+
}
|
|
30
|
+
createCopy(remap) {
|
|
31
|
+
const remappedObjects = this.objects.map(obj => remap.get(obj) || obj);
|
|
32
|
+
return new Remove(remappedObjects);
|
|
33
|
+
}
|
|
27
34
|
getType() {
|
|
28
35
|
return 'remove';
|
|
29
36
|
}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { SceneObject } from "../common/scene-object.js";
|
|
2
2
|
import { GeometrySceneObject } from "./2d/geometry.js";
|
|
3
3
|
import { LazyVertex } from "./lazy-vertex.js";
|
|
4
|
+
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
4
5
|
export declare class Trim2D extends GeometrySceneObject {
|
|
5
|
-
private
|
|
6
|
+
private _filters;
|
|
7
|
+
private _picking;
|
|
8
|
+
private _pickPoints;
|
|
6
9
|
constructor();
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
setFilters(...fs: EdgeFilterBuilder[]): this;
|
|
11
|
+
pick(...ps: LazyVertex[]): this;
|
|
12
|
+
isPicking(): boolean;
|
|
13
|
+
getPickPoints(): LazyVertex[];
|
|
14
|
+
get filters(): EdgeFilterBuilder[];
|
|
9
15
|
build(): void;
|
|
16
|
+
private buildWithFilters;
|
|
17
|
+
private buildWithPicking;
|
|
10
18
|
getDependencies(): SceneObject[];
|
|
11
19
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
12
20
|
compareTo(other: Trim2D): boolean;
|
|
13
21
|
getType(): string;
|
|
14
|
-
serialize(): {
|
|
22
|
+
serialize(): {
|
|
23
|
+
trigger: "trim-picking";
|
|
24
|
+
picking: true;
|
|
25
|
+
pickPoints: number[][];
|
|
26
|
+
};
|
|
15
27
|
}
|