@metamesh/modeling 0.10.0 → 0.11.0
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/CHANGELOG.md +40 -0
- package/README.md +56 -17
- package/dist/api/engine.d.ts +105 -8
- package/dist/api/index.d.ts +754 -771
- package/dist/engine/construction-operations.js +3 -1
- package/dist/engine/deferred-context.js +182 -0
- package/dist/engine/execution.js +38 -4
- package/dist/engine/finalization.js +65 -133
- package/dist/engine/geometry-recipes.js +12 -0
- package/dist/engine/immutable-generation-data.js +186 -0
- package/dist/engine/index.js +57 -35
- package/dist/engine/kernel/manifold.wasm +0 -0
- package/dist/engine/model-dependencies.js +46 -0
- package/dist/engine/model-summary.js +43 -0
- package/dist/engine/native/ownership.js +11 -5
- package/dist/engine/placement.js +24 -0
- package/dist/engine/recipe-realization.js +119 -0
- package/dist/engine/session-graph.js +442 -0
- package/dist/engine/session-native.js +90 -0
- package/dist/engine/session-snapshot.js +153 -0
- package/dist/engine/session.js +184 -0
- package/dist/engine/shape-anchors.js +5 -0
- package/dist/geometry/mesh-centroid.js +7 -6
- package/dist/geometry/mesh-planar.js +11 -10
- package/dist/geometry/mesh-quality.js +15 -14
- package/dist/geometry/mesh-signature.js +8 -7
- package/dist/geometry/mesh-solid.js +10 -4
- package/dist/geometry/mesh.js +36 -31
- package/dist/index.js +2 -0
- package/dist/model/execution-binding.js +5 -5
- package/dist/model/generation.js +27 -0
- package/dist/model/immutable-buffer.js +111 -0
- package/dist/model/modeling-context.js +1 -0
- package/dist/model/operations.js +521 -160
- package/dist/model/shape-handles.js +44 -0
- package/package.json +4 -4
package/dist/model/operations.js
CHANGED
|
@@ -1,161 +1,522 @@
|
|
|
1
|
+
import { GeometryError } from "../geometry/errors.js";
|
|
1
2
|
import { currentModelContext } from "./execution-binding.js";
|
|
2
|
-
function
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
export
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
export
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
export
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
export
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
export
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
export
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
3
|
+
function invoke(name, args) {
|
|
4
|
+
const context = currentModelContext(name);
|
|
5
|
+
return Reflect.apply(context[name], undefined, args);
|
|
6
|
+
}
|
|
7
|
+
function requireProfile(shape, operation) {
|
|
8
|
+
if (typeof shape !== "object" || shape === null || !("components" in shape)) {
|
|
9
|
+
throw new GeometryError("INVALID_ARGUMENT", `${operation}() requires a CorrespondingProfile. Use section.${operation}() for a Section.`, { operation });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Axis-aligned box with uniform or disjoint named edge/group treatments.
|
|
14
|
+
*
|
|
15
|
+
* @example Rounded box
|
|
16
|
+
* ```ts
|
|
17
|
+
* box({ size: [30, 20, 8], treatment: { kind: "round", radius: 2 } });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function box(options) {
|
|
21
|
+
return invoke("box", [options]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* +Z cylinder or frustum. Face sizing compensates polygon faceting.
|
|
25
|
+
*
|
|
26
|
+
* @example Cylinder
|
|
27
|
+
* ```ts
|
|
28
|
+
* cylinder({ height: 20, radius: 10 });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Cone
|
|
32
|
+
* ```ts
|
|
33
|
+
* cylinder({ height: 20, radius: [10, 0] });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function cylinder(options) {
|
|
37
|
+
return invoke("cylinder", [options]);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hollow +Z tube.
|
|
41
|
+
*
|
|
42
|
+
* @example Tube
|
|
43
|
+
* ```ts
|
|
44
|
+
* tube({ height: 20, outerRadius: 10, innerRadius: 6 });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function tube(options) {
|
|
48
|
+
return invoke("tube", [options]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Origin-centered sphere.
|
|
52
|
+
*
|
|
53
|
+
* @example Sphere
|
|
54
|
+
* ```ts
|
|
55
|
+
* sphere({ diameter: 20 });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function sphere(options) {
|
|
59
|
+
return invoke("sphere", [options]);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Torus around +Z.
|
|
63
|
+
*
|
|
64
|
+
* @example Torus
|
|
65
|
+
* ```ts
|
|
66
|
+
* torus({ majorRadius: 12, minorRadius: 3 });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function torus(options) {
|
|
70
|
+
return invoke("torus", [options]);
|
|
71
|
+
}
|
|
72
|
+
export function union(solids) {
|
|
73
|
+
return invoke("union", [solids]);
|
|
74
|
+
}
|
|
75
|
+
export function intersect(solids) {
|
|
76
|
+
return invoke("intersect", [solids]);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Splits a solid into [inside the cutter, outside the cutter].
|
|
80
|
+
*
|
|
81
|
+
* The first result is the intersection; the second is the subtraction. The cutter itself
|
|
82
|
+
* is not returned. Either result may be empty or contain multiple disconnected components.
|
|
83
|
+
*
|
|
84
|
+
* @example Keep the plug and the surrounding body
|
|
85
|
+
* ```ts
|
|
86
|
+
* const body = box({ size: [30, 20, 8] });
|
|
87
|
+
* const cutter = cylinder({ height: 10, diameter: 8 });
|
|
88
|
+
* const [plug, remainder] = split(body, cutter);
|
|
89
|
+
* // Move the plug beside the body to show both results.
|
|
90
|
+
* union([plug.translate([0, -20, 0]), remainder]);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export function split(solid, cutter) {
|
|
94
|
+
return invoke("split", [solid, cutter]);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Splits a solid into [positive-normal side, negative side] at a world-space plane.
|
|
98
|
+
*
|
|
99
|
+
* The nonzero normal is normalized; offset is the signed distance from the origin in
|
|
100
|
+
* millimetres, defaulting to zero. Both cut faces are closed. Select either tuple result
|
|
101
|
+
* to retain one side; either result may be empty.
|
|
102
|
+
*
|
|
103
|
+
* @example Separate two halves at X = 0
|
|
104
|
+
* ```ts
|
|
105
|
+
* const body = box({ size: [30, 20, 8] });
|
|
106
|
+
* const [right, left] = splitByPlane(body, { normal: [1, 0, 0] });
|
|
107
|
+
* // The gap is only for display; the cut itself removes no material.
|
|
108
|
+
* union([right.translate([4, 0, 0]), left.translate([-4, 0, 0])]);
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example Keep only the positive half
|
|
112
|
+
* ```ts
|
|
113
|
+
* const [right] = splitByPlane(box({ size: [30, 20, 8] }), { normal: [1, 0, 0] });
|
|
114
|
+
* right;
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function splitByPlane(solid, plane) {
|
|
118
|
+
return invoke("splitByPlane", [solid, plane]);
|
|
119
|
+
}
|
|
120
|
+
/** Combines disjoint components without resolving overlap. */
|
|
121
|
+
export function compose(solids) {
|
|
122
|
+
return invoke("compose", [solids]);
|
|
123
|
+
}
|
|
124
|
+
export function hull(options) {
|
|
125
|
+
return invoke("hull", [options]);
|
|
126
|
+
}
|
|
127
|
+
/** Three-dimensional Minkowski sum. */
|
|
128
|
+
export function minkowskiSum(a, b) {
|
|
129
|
+
return invoke("minkowskiSum", [a, b]);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Imports a bounded closed indexed mesh.
|
|
133
|
+
*
|
|
134
|
+
* @example Mesh import
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { solidFromMesh } from "@metamesh/modeling";
|
|
137
|
+
* import { extrudeMesh, rectangleLoop } from "@metamesh/modeling/geometry";
|
|
138
|
+
*
|
|
139
|
+
* const mesh = extrudeMesh(rectangleLoop({ size: [20, 12] }), { height: 6 });
|
|
140
|
+
* solidFromMesh(mesh);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function solidFromMesh(mesh) {
|
|
144
|
+
return invoke("solidFromMesh", [mesh]);
|
|
145
|
+
}
|
|
146
|
+
/** Copies a handle to manufacturing mesh buffers. */
|
|
147
|
+
export function toTriangleMesh(solid) {
|
|
148
|
+
return invoke("toTriangleMesh", [solid]);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Reads a copied, immutable named anchor from this exact active generation.
|
|
152
|
+
*
|
|
153
|
+
* @example Read the top datum
|
|
154
|
+
* ```ts
|
|
155
|
+
* const plate = box({ size: [30, 20, 6] });
|
|
156
|
+
* anchor(plate, "top"); // position [0, 0, 3], direction [0, 0, 1]
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function anchor(shape, name) {
|
|
160
|
+
return invoke("anchor", [shape, name]);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Creates a native filled section without an intermediate Region2 conversion.
|
|
164
|
+
*/
|
|
165
|
+
export function sectionFromContours(contours, options) {
|
|
166
|
+
return invoke("sectionFromContours", [contours, options]);
|
|
167
|
+
}
|
|
168
|
+
/** Imports explicit region topology once. */
|
|
169
|
+
export function fromRegion(region) {
|
|
170
|
+
return invoke("fromRegion", [region]);
|
|
171
|
+
}
|
|
172
|
+
/** Explicit copied topology inspection; not needed for section operations. */
|
|
173
|
+
export function toRegion(section) {
|
|
174
|
+
return invoke("toRegion", [section]);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Filled rectangular section with uniform or independently treated corners.
|
|
178
|
+
*
|
|
179
|
+
* @example Round the corners
|
|
180
|
+
* ```ts
|
|
181
|
+
* rectangle({ size: [30, 20], treatment: { kind: "round", radius: 3 } });
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export function rectangle(options) {
|
|
185
|
+
return invoke("rectangle", [options]);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Centered circular section.
|
|
189
|
+
*
|
|
190
|
+
* @example Size to polygon faces
|
|
191
|
+
* ```ts
|
|
192
|
+
* circle({ diameter: 12, sizing: "face" });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export function circle(options) {
|
|
196
|
+
return invoke("circle", [options]);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Centered elliptical section.
|
|
200
|
+
*
|
|
201
|
+
* @example Ellipse by full dimensions
|
|
202
|
+
* ```ts
|
|
203
|
+
* ellipse({ size: [30, 20] });
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
export function ellipse(options) {
|
|
207
|
+
return invoke("ellipse", [options]);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Regular polygon with a vertex on +X.
|
|
211
|
+
*
|
|
212
|
+
* @example Hexagonal section
|
|
213
|
+
* ```ts
|
|
214
|
+
* regularPolygon({ radius: 10, sides: 6, sizing: "face" });
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export function regularPolygon(options) {
|
|
218
|
+
return invoke("regularPolygon", [options]);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Alternating outer/inner star vertices.
|
|
222
|
+
*
|
|
223
|
+
* @example Five-point section
|
|
224
|
+
* ```ts
|
|
225
|
+
* star({ outerRadius: 12, innerRadius: 6, points: 5 });
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
export function star(options) {
|
|
229
|
+
return invoke("star", [options]);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Extrudes explicit corresponding contours along +Z with degree twist and top scaling.
|
|
233
|
+
* Filled Sections use `section.extrude(options)`.
|
|
234
|
+
*
|
|
235
|
+
* @example Extrude
|
|
236
|
+
* ```ts
|
|
237
|
+
* import { extrude, matchProfiles } from "@metamesh/modeling";
|
|
238
|
+
* import { rectangleProfile } from "@metamesh/modeling/geometry";
|
|
239
|
+
*
|
|
240
|
+
* const [profile] = matchProfiles([rectangleProfile({ size: [30, 20] })]);
|
|
241
|
+
* extrude(profile!, { height: 8 });
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
export function extrude(shape, options) {
|
|
245
|
+
requireProfile(shape, "extrude");
|
|
246
|
+
return invoke("extrude", [shape, options]);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Revolves explicit corresponding radial/Z contours around +Z, with caps for partial sweeps.
|
|
250
|
+
* Filled Sections use `section.revolve(options)`.
|
|
251
|
+
*
|
|
252
|
+
* @example Revolve
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { matchProfiles, revolve } from "@metamesh/modeling";
|
|
255
|
+
* import { rectangleProfile } from "@metamesh/modeling/geometry";
|
|
256
|
+
*
|
|
257
|
+
* // Contour coordinates are [radius, Z].
|
|
258
|
+
* const [profile] = matchProfiles([rectangleProfile({ size: [4, 16], center: false })]);
|
|
259
|
+
* revolve(profile!, { revolveDegrees: 270 });
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export function revolve(shape, options) {
|
|
263
|
+
requireProfile(shape, "revolve");
|
|
264
|
+
return invoke("revolve", [shape, options]);
|
|
265
|
+
}
|
|
266
|
+
export function loft(stations, options) {
|
|
267
|
+
return invoke("loft", [stations, options]);
|
|
268
|
+
}
|
|
269
|
+
/** Explicit family matching on a shared physical span grid using session or operation resolution. */
|
|
270
|
+
export function matchProfiles(profiles, options) {
|
|
271
|
+
return invoke("matchProfiles", [profiles, options]);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Sweeps explicit corresponding contours using the selected frame policy.
|
|
275
|
+
* Filled Sections use `section.sweep(path, options)`.
|
|
276
|
+
*
|
|
277
|
+
* @example Sweep
|
|
278
|
+
* ```ts
|
|
279
|
+
* import { matchProfiles, sweep } from "@metamesh/modeling";
|
|
280
|
+
* import { rectangleProfile } from "@metamesh/modeling/geometry";
|
|
281
|
+
*
|
|
282
|
+
* const [profile] = matchProfiles([rectangleProfile({ size: [6, 3] })]);
|
|
283
|
+
* sweep(profile!, [[0, 0, 0], [0, 0, 15], [10, 0, 25]], {
|
|
284
|
+
* orientation: { kind: "transport", initialNormal: [0, 1, 0] },
|
|
285
|
+
* });
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export function sweep(shape, path, options) {
|
|
289
|
+
requireProfile(shape, "sweep");
|
|
290
|
+
return invoke("sweep", [shape, path, options]);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Copies lightweight section measurements.
|
|
294
|
+
*
|
|
295
|
+
* @example Check filled area
|
|
296
|
+
* ```ts
|
|
297
|
+
* const facts = inspectSection(rectangle({ size: [20, 10] }));
|
|
298
|
+
* facts.area; // 200 mm²
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
export function inspectSection(section) {
|
|
302
|
+
return invoke("inspectSection", [section]);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Reads immutable native facts once per solid; temporary components are owned by the build.
|
|
306
|
+
*
|
|
307
|
+
* @example Check dimensions and volume
|
|
308
|
+
* ```ts
|
|
309
|
+
* const facts = inspectSolid(box({ size: [30, 20, 6] }));
|
|
310
|
+
* facts.volume; // 3600 mm³
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
export function inspectSolid(solid) {
|
|
314
|
+
return invoke("inspectSolid", [solid]);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Checks whether a same-session solid or filled section contains no geometry.
|
|
318
|
+
*
|
|
319
|
+
* Accepts a 3D Solid or 2D Section handle, not a finalized model or raw triangle mesh.
|
|
320
|
+
* Useful after a Boolean operation whose inputs may not overlap; no mesh is extracted.
|
|
321
|
+
*
|
|
322
|
+
* @example Check a missed intersection
|
|
323
|
+
* ```ts
|
|
324
|
+
* const body = box({ size: 10 });
|
|
325
|
+
* const distant = box({ size: 10 }).translate([30, 0, 0]);
|
|
326
|
+
* const empty = intersect([body, distant]);
|
|
327
|
+
* const missed = isEmpty(empty); // true
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
export function isEmpty(shape) {
|
|
331
|
+
return invoke("isEmpty", [shape]);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Measures the minimum separation between two nonempty solids in millimetres.
|
|
335
|
+
*
|
|
336
|
+
* Uses actual geometry, not bounding-box separation or distance between object origins.
|
|
337
|
+
* Contact or overlapping material returns zero, including one solid inside another.
|
|
338
|
+
* The kernel's search range is managed internally; the result is not capped at a caller limit.
|
|
339
|
+
* Empty, expired or foreign-build handles reject. Results follow native numeric precision.
|
|
340
|
+
*
|
|
341
|
+
* @example Measure the gap between two blocks
|
|
342
|
+
* ```ts
|
|
343
|
+
* const left = box({ size: 10 });
|
|
344
|
+
* const right = box({ size: 10 }).translate([13, 0, 0]);
|
|
345
|
+
* const gap = distance(left, right); // 3 mm
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
export function distance(solid, other) {
|
|
349
|
+
return invoke("distance", [solid, other]);
|
|
350
|
+
}
|
|
351
|
+
/** Copies intersections along the finite directed origin-to-endpoint segment. */
|
|
352
|
+
export function rayCast(solid, origin, endpoint) {
|
|
353
|
+
return invoke("rayCast", [solid, origin, endpoint]);
|
|
354
|
+
}
|
|
355
|
+
export function components(solid) {
|
|
356
|
+
return invoke("components", [solid]);
|
|
357
|
+
}
|
|
358
|
+
/** Bounded positive-inside implicit field constructor. */
|
|
359
|
+
export function implicit(options) {
|
|
360
|
+
return invoke("implicit", [options]);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Closed rectilinear top above a flat base, with uniform or explicit XY coordinates.
|
|
364
|
+
* Samples each grid height once; center-error triangulation also samples each cell centre.
|
|
365
|
+
*
|
|
366
|
+
* @example Heightfield
|
|
367
|
+
* ```ts
|
|
368
|
+
* heightfield({
|
|
369
|
+
* size: [30, 20], segments: [24, 16],
|
|
370
|
+
* height: ([x, y]) => 3 + Math.cos(x / 5) * Math.cos(y / 5),
|
|
371
|
+
* });
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
export function heightfield(options) {
|
|
375
|
+
return invoke("heightfield", [options]);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Repeats one solid along a normalized direction.
|
|
379
|
+
*
|
|
380
|
+
* @example Three copies along X
|
|
381
|
+
* ```ts
|
|
382
|
+
* linearPattern(box({ size: 4 }), { count: 3, spacing: 10 });
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
export function linearPattern(source, options) {
|
|
386
|
+
return invoke("linearPattern", [source, options]);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Repeats one solid about an axis.
|
|
390
|
+
*
|
|
391
|
+
* @example Six copies around Z
|
|
392
|
+
* ```ts
|
|
393
|
+
* const pin = cylinder({ height: 6, radius: 2 }).translate([12, 0, 0]);
|
|
394
|
+
* radialPattern(pin, { count: 6, axis: [0, 0, 1], angleDegrees: 60 });
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
export function radialPattern(source, options) {
|
|
398
|
+
return invoke("radialPattern", [source, options]);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* XY or XYZ copies, optionally centered or placed in an explicit frame.
|
|
402
|
+
*
|
|
403
|
+
* @example Centered XY grid
|
|
404
|
+
* ```ts
|
|
405
|
+
* gridPattern(box({ size: 4 }), { count: [3, 2], spacing: [10, 10], center: true });
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
export function gridPattern(source, options) {
|
|
409
|
+
return invoke("gridPattern", [source, options]);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Physical arc-length copies along a sampled path, with optional tangent orientation.
|
|
413
|
+
*
|
|
414
|
+
* @example Copies along a bent path
|
|
415
|
+
* ```ts
|
|
416
|
+
* pathPattern(box({ size: 4 }), [[0, 0, 0], [20, 0, 0], [20, 20, 0]], { count: 5 });
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
export function pathPattern(source, path, options) {
|
|
420
|
+
return invoke("pathPattern", [source, path, options]);
|
|
421
|
+
}
|
|
422
|
+
/** Helical ridge mesh construction. */
|
|
423
|
+
export function threadRidge(options) {
|
|
424
|
+
return invoke("threadRidge", [options]);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Compensated external threaded rod.
|
|
428
|
+
*
|
|
429
|
+
* @example Threaded rod
|
|
430
|
+
* ```ts
|
|
431
|
+
* import { threadedRod } from "@metamesh/modeling";
|
|
432
|
+
* import { createThreadProfile } from "@metamesh/modeling/geometry";
|
|
433
|
+
*
|
|
434
|
+
* const profile = createThreadProfile({ kind: "triangular", pitch: 1.75 });
|
|
435
|
+
* threadedRod({ nominalMajorDiameter: 12, length: 24, profile, fit: "normal" });
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
export function threadedRod(options) {
|
|
439
|
+
return invoke("threadedRod", [options]);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Compensated internal threaded cutter.
|
|
443
|
+
*
|
|
444
|
+
* @example Internal thread
|
|
445
|
+
* ```ts
|
|
446
|
+
* import { cylinder, internalThreadCutter } from "@metamesh/modeling";
|
|
447
|
+
* import { createThreadProfile } from "@metamesh/modeling/geometry";
|
|
448
|
+
* const profile = createThreadProfile({ kind: "triangular", pitch: 1.75 });
|
|
449
|
+
* const body = cylinder({ diameter: 20, height: 12, center: false });
|
|
450
|
+
* const cutter = internalThreadCutter({ nominalMajorDiameter: 12, length: 12, profile });
|
|
451
|
+
* body.subtract([cutter]);
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
export function internalThreadCutter(options) {
|
|
455
|
+
return invoke("internalThreadCutter", [options]);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Compensated threaded nut.
|
|
459
|
+
*
|
|
460
|
+
* @example Threaded nut
|
|
461
|
+
* ```ts
|
|
462
|
+
* import { threadedNut } from "@metamesh/modeling";
|
|
463
|
+
* import { createThreadProfile } from "@metamesh/modeling/geometry";
|
|
464
|
+
*
|
|
465
|
+
* const profile = createThreadProfile({ kind: "triangular", pitch: 1.75 });
|
|
466
|
+
* threadedNut({ nominalMajorDiameter: 12, thickness: 8, width: 20, shape: "hex", profile, fit: "normal" });
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
export function threadedNut(options) {
|
|
470
|
+
return invoke("threadedNut", [options]);
|
|
471
|
+
}
|
|
472
|
+
/** Allocation-free external-thread facts. */
|
|
473
|
+
export function preflightThreadedRod(options) {
|
|
474
|
+
return invoke("preflightThreadedRod", [options]);
|
|
475
|
+
}
|
|
476
|
+
/** Allocation-free internal-thread facts. */
|
|
477
|
+
export function preflightInternalThreadCutter(options) {
|
|
478
|
+
return invoke("preflightInternalThreadCutter", [options]);
|
|
479
|
+
}
|
|
480
|
+
/** Allocation-free nut/cutter facts. */
|
|
481
|
+
export function preflightThreadedNut(options) {
|
|
482
|
+
return invoke("preflightThreadedNut", [options]);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Consumes a previously prepared layout; never shapes or fetches a font.
|
|
486
|
+
*
|
|
487
|
+
* @example Use a prepared text layout
|
|
488
|
+
* ```ts
|
|
489
|
+
* // Prepare the exact font and layout before entering build.
|
|
490
|
+
* function labelSection(layout: VectorTextLayout) {
|
|
491
|
+
* return textSection(layout);
|
|
492
|
+
* }
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
export function textSection(layout, options) {
|
|
496
|
+
return invoke("textSection", [layout, options]);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Imports bounded painted SVG document geometry.
|
|
500
|
+
*
|
|
501
|
+
* @example Import a physical SVG rectangle
|
|
502
|
+
* ```ts
|
|
503
|
+
* svgSection(
|
|
504
|
+
* '<svg xmlns="http://www.w3.org/2000/svg" width="30mm" height="20mm" viewBox="0 0 30 20"><rect width="30" height="20"/></svg>',
|
|
505
|
+
* { origin: "center" },
|
|
506
|
+
* );
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
export function svgSection(svg, options) {
|
|
510
|
+
return invoke("svgSection", [svg, options]);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Imports bounded filled SVG path data.
|
|
514
|
+
*
|
|
515
|
+
* @example Fill an SVG path in millimetres
|
|
516
|
+
* ```ts
|
|
517
|
+
* svgPathSection("M0 0H20V12H0Z", { origin: "center", scale: 1 });
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
export function svgPathSection(path, options) {
|
|
521
|
+
return invoke("svgPathSection", [path, options]);
|
|
522
|
+
}
|