fluidcad 0.0.20 → 0.0.22
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/features/2d/rect.d.ts +1 -0
- package/lib/dist/features/2d/rect.js +13 -9
- package/lib/dist/features/2d/sketch.d.ts +1 -0
- package/lib/dist/features/2d/sketch.js +21 -4
- 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/filters/edge/edge-filter.d.ts +0 -14
- package/lib/dist/filters/edge/edge-filter.js +2 -0
- package/lib/dist/filters/face/face-filter.d.ts +0 -14
- package/lib/dist/filters/face/face-filter.js +2 -0
- package/lib/dist/oc/draft-ops.d.ts +5 -0
- package/lib/dist/oc/draft-ops.js +51 -0
- package/lib/dist/oc/mesh.d.ts +2 -0
- package/lib/dist/oc/mesh.js +14 -6
- package/lib/dist/oc/wire-ops.js +4 -1
- 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 +25 -4
- 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/part.test.js +69 -114
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- 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-Bz0YoaQD.js → index-C0JwQ8Bk.js} +15 -11
- 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;
|
|
@@ -13,6 +13,7 @@ export declare class Rect extends ExtrudableGeometryBase implements IRect {
|
|
|
13
13
|
height: number;
|
|
14
14
|
private _radius?;
|
|
15
15
|
private _center;
|
|
16
|
+
private static normalizeRadius;
|
|
16
17
|
constructor(width: number, height: number, targetPlane?: PlaneObjectBase);
|
|
17
18
|
build(): void;
|
|
18
19
|
buildSimpleRect(start: Point2D, plane: Plane): Edge[];
|
|
@@ -8,6 +8,13 @@ export class Rect extends ExtrudableGeometryBase {
|
|
|
8
8
|
height;
|
|
9
9
|
_radius;
|
|
10
10
|
_center = false;
|
|
11
|
+
static normalizeRadius(r) {
|
|
12
|
+
if (Array.isArray(r)) {
|
|
13
|
+
return [r[0] || 0, r[1] || 0, r[2] || 0, r[3] || 0];
|
|
14
|
+
}
|
|
15
|
+
const v = r || 0;
|
|
16
|
+
return [v, v, v, v];
|
|
17
|
+
}
|
|
11
18
|
constructor(width, height, targetPlane = null) {
|
|
12
19
|
super(targetPlane);
|
|
13
20
|
this.width = width;
|
|
@@ -171,14 +178,8 @@ export class Rect extends ExtrudableGeometryBase {
|
|
|
171
178
|
if (this.width !== other.width || this.height !== other.height || this._center !== other._center) {
|
|
172
179
|
return false;
|
|
173
180
|
}
|
|
174
|
-
const thisRadius =
|
|
175
|
-
|
|
176
|
-
this._radius || 0,
|
|
177
|
-
this._radius || 0];
|
|
178
|
-
const otherRadius = Array.isArray(other._radius) ? other._radius : [other._radius || 0,
|
|
179
|
-
other._radius || 0,
|
|
180
|
-
other._radius || 0,
|
|
181
|
-
other._radius || 0];
|
|
181
|
+
const thisRadius = Rect.normalizeRadius(this._radius);
|
|
182
|
+
const otherRadius = Rect.normalizeRadius(other._radius);
|
|
182
183
|
return thisRadius.every((r, i) => r === otherRadius[i]);
|
|
183
184
|
}
|
|
184
185
|
topEdge() {
|
|
@@ -242,7 +243,10 @@ export class Rect extends ExtrudableGeometryBase {
|
|
|
242
243
|
});
|
|
243
244
|
}
|
|
244
245
|
radius(...r) {
|
|
245
|
-
if (r.length ===
|
|
246
|
+
if (r.length === 0) {
|
|
247
|
+
this._radius = 0;
|
|
248
|
+
}
|
|
249
|
+
else if (r.length === 1) {
|
|
246
250
|
this._radius = r[0];
|
|
247
251
|
}
|
|
248
252
|
else {
|
|
@@ -18,6 +18,7 @@ export declare class Sketch extends SceneObject implements Extrudable {
|
|
|
18
18
|
build(context?: BuildSceneObjectContext): void;
|
|
19
19
|
getEdges(): Edge[];
|
|
20
20
|
getEdgesWithOwner(): Map<Edge, GeometrySceneObject>;
|
|
21
|
+
getAllEdges(): Edge[];
|
|
21
22
|
getGeometriesWithOwner(): Map<Edge, GeometrySceneObject>;
|
|
22
23
|
getGeometries(): Edge[];
|
|
23
24
|
getDependencies(): SceneObject[];
|
|
@@ -83,7 +83,7 @@ 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 originalEdges = source.
|
|
86
|
+
const originalEdges = source.getAllEdges();
|
|
87
87
|
const transformedEdges = originalEdges.map(edge => ShapeOps.transform(edge, transform));
|
|
88
88
|
this.setState('cloned-edges', transformedEdges);
|
|
89
89
|
}
|
|
@@ -98,7 +98,24 @@ export class Sketch extends SceneObject {
|
|
|
98
98
|
getEdgesWithOwner() {
|
|
99
99
|
const children = this.getChildren();
|
|
100
100
|
const result = new Map();
|
|
101
|
-
|
|
101
|
+
for (const child of children) {
|
|
102
|
+
const shapes = child.getShapes();
|
|
103
|
+
for (const shape of shapes) {
|
|
104
|
+
if (shape instanceof Edge) {
|
|
105
|
+
result.set(shape, child);
|
|
106
|
+
}
|
|
107
|
+
else if (shape instanceof Wire) {
|
|
108
|
+
for (const edge of shape.getEdges()) {
|
|
109
|
+
result.set(edge, child);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
getAllEdges() {
|
|
117
|
+
const children = this.getChildren();
|
|
118
|
+
const result = [];
|
|
102
119
|
for (const child of children) {
|
|
103
120
|
const shapes = child.getAddedShapes();
|
|
104
121
|
const removedShapes = child.getRemovedShapes();
|
|
@@ -111,11 +128,11 @@ export class Sketch extends SceneObject {
|
|
|
111
128
|
continue;
|
|
112
129
|
}
|
|
113
130
|
if (shape instanceof Edge) {
|
|
114
|
-
result.
|
|
131
|
+
result.push(shape);
|
|
115
132
|
}
|
|
116
133
|
else if (shape instanceof Wire) {
|
|
117
134
|
for (const edge of shape.getEdges()) {
|
|
118
|
-
result.
|
|
135
|
+
result.push(edge);
|
|
119
136
|
}
|
|
120
137
|
}
|
|
121
138
|
}
|
|
@@ -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
|
+
}
|
|
@@ -5,20 +5,6 @@ import { PlaneObjectBase } from "../../features/plane-renderable-base.js";
|
|
|
5
5
|
import { ISceneObject } from "../../core/interfaces.js";
|
|
6
6
|
export declare class EdgeFilterBuilder extends FilterBuilderBase<Edge> {
|
|
7
7
|
constructor();
|
|
8
|
-
/**
|
|
9
|
-
* Selects the edge at the given index.
|
|
10
|
-
* @param index - Zero-based edge index.
|
|
11
|
-
* @param shapes - The edge array to index into.
|
|
12
|
-
* @param originalShapes - Optional original edge array before filtering.
|
|
13
|
-
*/
|
|
14
|
-
atIndex(index: number, shapes: Edge[], originalShapes?: Edge[]): this;
|
|
15
|
-
/**
|
|
16
|
-
* Excludes the edge at the given index.
|
|
17
|
-
* @param index - Zero-based edge index to exclude.
|
|
18
|
-
* @param shapes - The edge array to index into.
|
|
19
|
-
* @param originalShapes - Optional original edge array before filtering.
|
|
20
|
-
*/
|
|
21
|
-
notAtIndex(index: number, shapes: Edge[], originalShapes?: Edge[]): this;
|
|
22
8
|
/**
|
|
23
9
|
* Selects edges that lie on the given plane.
|
|
24
10
|
* @param plane - The reference plane.
|
|
@@ -21,6 +21,7 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
21
21
|
* @param index - Zero-based edge index.
|
|
22
22
|
* @param shapes - The edge array to index into.
|
|
23
23
|
* @param originalShapes - Optional original edge array before filtering.
|
|
24
|
+
* @internal
|
|
24
25
|
*/
|
|
25
26
|
atIndex(index, shapes, originalShapes) {
|
|
26
27
|
const filter = new AtIndexFilter(index, shapes, originalShapes);
|
|
@@ -32,6 +33,7 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
32
33
|
* @param index - Zero-based edge index to exclude.
|
|
33
34
|
* @param shapes - The edge array to index into.
|
|
34
35
|
* @param originalShapes - Optional original edge array before filtering.
|
|
36
|
+
* @internal
|
|
35
37
|
*/
|
|
36
38
|
notAtIndex(index, shapes, originalShapes) {
|
|
37
39
|
const filter = new NotAtIndexFilter(index, shapes, originalShapes);
|
|
@@ -6,20 +6,6 @@ import { EdgeFilterBuilder } from "../edge/edge-filter.js";
|
|
|
6
6
|
import { ISceneObject } from "../../core/interfaces.js";
|
|
7
7
|
export declare class FaceFilterBuilder extends FilterBuilderBase<Face> {
|
|
8
8
|
constructor();
|
|
9
|
-
/**
|
|
10
|
-
* Selects the face at the given index.
|
|
11
|
-
* @param index - Zero-based face index.
|
|
12
|
-
* @param shapes - The face array to index into.
|
|
13
|
-
* @param originalShapes - Optional original face array before filtering.
|
|
14
|
-
*/
|
|
15
|
-
atIndex(index: number, shapes: Face[], originalShapes?: Face[]): this;
|
|
16
|
-
/**
|
|
17
|
-
* Excludes the face at the given index.
|
|
18
|
-
* @param index - Zero-based face index to exclude.
|
|
19
|
-
* @param shapes - The face array to index into.
|
|
20
|
-
* @param originalShapes - Optional original face array before filtering.
|
|
21
|
-
*/
|
|
22
|
-
notAtIndex(index: number, shapes: Face[], originalShapes?: Face[]): this;
|
|
23
9
|
/**
|
|
24
10
|
* Selects faces that lie on the given plane.
|
|
25
11
|
* @param plane - The reference plane.
|
|
@@ -23,6 +23,7 @@ export class FaceFilterBuilder extends FilterBuilderBase {
|
|
|
23
23
|
* @param index - Zero-based face index.
|
|
24
24
|
* @param shapes - The face array to index into.
|
|
25
25
|
* @param originalShapes - Optional original face array before filtering.
|
|
26
|
+
* @internal
|
|
26
27
|
*/
|
|
27
28
|
atIndex(index, shapes, originalShapes) {
|
|
28
29
|
const filter = new AtIndexFilter(index, shapes, originalShapes);
|
|
@@ -34,6 +35,7 @@ export class FaceFilterBuilder extends FilterBuilderBase {
|
|
|
34
35
|
* @param index - Zero-based face index to exclude.
|
|
35
36
|
* @param shapes - The face array to index into.
|
|
36
37
|
* @param originalShapes - Optional original face array before filtering.
|
|
38
|
+
* @internal
|
|
37
39
|
*/
|
|
38
40
|
notAtIndex(index, shapes, originalShapes) {
|
|
39
41
|
const filter = new NotAtIndexFilter(index, shapes, originalShapes);
|