Skip to main content

brep_kernel/abi/
modeling_b.rs

1use super::*;
2
3#[derive(Deserialize)]
4pub(crate) struct TangentLoftRequest {
5    pub(crate) sections: Vec<Vec<NurbsCurve>>,
6    pub(crate) start_direction: Vec3,
7    pub(crate) end_direction: Vec3,
8}
9
10#[wasm_bindgen]
11pub fn loft_profile_brep_closed_json(request_json: &str) -> Result<String, JsValue> {
12    let request: LoftBrepRequest = json_in(request_json)?;
13    let solid = loft_profile_brep_closed(&request.sections).map_err(javascript_refusal)?;
14    json_out(&solid)
15}
16
17#[wasm_bindgen]
18pub fn loft_profile_brep_closed_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
19    let request: LoftBrepRequest = json_in(request_json)?;
20    let solid = loft_profile_brep_closed(&request.sections).map_err(javascript_refusal)?;
21    solid_buffer(&solid, "{}".into())
22}
23
24#[wasm_bindgen]
25pub fn loft_profile_brep_tangent_json(request_json: &str) -> Result<String, JsValue> {
26    let request: TangentLoftRequest = json_in(request_json)?;
27    let solid = loft_profile_brep_tangent(
28        &request.sections,
29        request.start_direction,
30        request.end_direction,
31    )
32    .map_err(javascript_refusal)?;
33    json_out(&solid)
34}
35
36#[wasm_bindgen]
37pub fn loft_profile_brep_tangent_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
38    let request: TangentLoftRequest = json_in(request_json)?;
39    let solid = loft_profile_brep_tangent(
40        &request.sections,
41        request.start_direction,
42        request.end_direction,
43    )
44    .map_err(javascript_refusal)?;
45    solid_buffer(&solid, "{}".into())
46}
47
48#[wasm_bindgen]
49pub fn loft_profile_brep_guided_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
50    let request: GuidedLoftRequest = json_in(request_json)?;
51    let solid = guided_loft_dispatch(&request).map_err(javascript_refusal)?;
52    solid_buffer(&solid, "{}".into())
53}
54
55#[wasm_bindgen]
56pub fn sweep_profile_along_path_json(request_json: &str) -> Result<String, JsValue> {
57    let request: SweepRequest = json_in(request_json)?;
58    let solid = sweep_profile_along_path(&request.profile, &request.path, request.name.as_deref())
59        .map_err(javascript_refusal)?;
60    json_out(&solid)
61}
62
63#[wasm_bindgen]
64pub fn sweep_profile_along_path_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
65    let request: SweepRequest = json_in(request_json)?;
66    let solid = sweep_profile_along_path(&request.profile, &request.path, request.name.as_deref())
67        .map_err(javascript_refusal)?;
68    solid_buffer(&solid, "{}".into())
69}
70
71#[derive(Deserialize)]
72pub(crate) struct TwistedSweepRequest {
73    pub(crate) profile: Vec<NurbsCurve>,
74    pub(crate) path: NurbsCurve,
75    /// Total twist about the path tangent, radians, linear in arc length.
76    pub(crate) twist_angle: f64,
77    #[serde(default)]
78    pub(crate) name: Option<String>,
79}
80
81#[wasm_bindgen]
82pub fn sweep_profile_twisted_json(request_json: &str) -> Result<String, JsValue> {
83    let request: TwistedSweepRequest = json_in(request_json)?;
84    let solid = sweep_profile_twisted(
85        &request.profile,
86        &request.path,
87        request.twist_angle,
88        request.name.as_deref(),
89    )
90    .map_err(javascript_refusal)?;
91    json_out(&solid)
92}
93
94#[wasm_bindgen]
95pub fn sweep_profile_twisted_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
96    let request: TwistedSweepRequest = json_in(request_json)?;
97    let solid = sweep_profile_twisted(
98        &request.profile,
99        &request.path,
100        request.twist_angle,
101        request.name.as_deref(),
102    )
103    .map_err(javascript_refusal)?;
104    solid_buffer(&solid, "{}".into())
105}
106
107#[wasm_bindgen]
108pub fn sweep_profile_helix_json(request_json: &str) -> Result<String, JsValue> {
109    let request: HelixSweepRequest = json_in(request_json)?;
110    let solid = sweep_profile_helix(
111        &request.profile,
112        request.axis_origin,
113        request.axis_direction,
114        request.helix_radius,
115        request.pitch,
116        request.turns,
117        request.name.as_deref(),
118    )
119    .map_err(javascript_refusal)?;
120    json_out(&solid)
121}
122
123#[wasm_bindgen]
124pub fn sweep_profile_helix_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
125    let request: HelixSweepRequest = json_in(request_json)?;
126    let solid = sweep_profile_helix(
127        &request.profile,
128        request.axis_origin,
129        request.axis_direction,
130        request.helix_radius,
131        request.pitch,
132        request.turns,
133        request.name.as_deref(),
134    )
135    .map_err(javascript_refusal)?;
136    solid_buffer(&solid, "{}".into())
137}
138
139#[wasm_bindgen]
140pub fn transform_brep_json(request_json: &str) -> Result<String, JsValue> {
141    let request: TransformBrepRequest = json_in(request_json)?;
142    let transform = AffineTransform::new(request.matrix).map_err(javascript_error)?;
143    let solid = transform_brep(&request.solid, transform, request.reverse_orientation)
144        .map_err(javascript_error)?;
145    json_out(&solid)
146}
147
148#[wasm_bindgen]
149pub fn mirror_brep_json(request_json: &str) -> Result<String, JsValue> {
150    let request: MirrorBrepRequest = json_in(request_json)?;
151    let solid = mirror_brep(&request.solid, request.plane_point, request.plane_normal)
152        .map_err(javascript_error)?;
153    json_out(&solid)
154}
155
156#[wasm_bindgen]
157pub fn mirror_brep_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
158    let request: MirrorBrepRequest = json_in(request_json)?;
159    let solid = mirror_brep(&request.solid, request.plane_point, request.plane_normal)
160        .map_err(javascript_error)?;
161    solid_buffer(&solid, "{}".into())
162}
163
164/// Cut a solid by a plane (Golovanov §6.4). Returns `{ "below": <solid>,
165/// "above": <solid> }` — the −n and +n pieces of the split. Errors with
166/// "plane does not intersect the solid" when the plane misses the body.
167#[wasm_bindgen]
168pub fn split_solid_by_plane_json(request_json: &str) -> Result<String, JsValue> {
169    let request: SplitByPlaneRequest = json_in(request_json)?;
170    let (below, above) =
171        split_solid_by_plane(&request.solid, request.plane_point, request.plane_normal)
172            .map_err(javascript_error)?;
173    let result = serde_json::json!({ "below": below, "above": above });
174    json_out(&result)
175}
176
177/// Cut a solid by an analytic tool surface (Golovanov §6.4). The request is
178/// `{ "solid": <solid>, "tool": { "type": "cylinder"|"sphere"|"cone"|"torus"|
179/// "plane", ... } }`. Returns `{ "pieces": [<inside>, <outside>] }` (for a
180/// plane, `[below, above]`). Errors with "tool surface does not divide the
181/// solid" when the tool does not cleanly cut the body.
182#[wasm_bindgen]
183pub fn split_solid_by_surface_json(request_json: &str) -> Result<String, JsValue> {
184    let request: SplitBySurfaceRequest = json_in(request_json)?;
185    let pieces = split_solid_by_surface(&request.solid, &request.tool).map_err(javascript_error)?;
186    let result = serde_json::json!({ "pieces": pieces });
187    json_out(&result)
188}
189
190/// Cut a solid by the analytic carrier of a selected face's `surface` (Golovanov
191/// §6.4). The face may be a plane, cylinder, cone, or sphere; the kernel
192/// recognizes its analytic type and extends it to span the body. Returns
193/// `{ "pieces": [<below/inside>, <above/outside>] }`. Errors on non-analytic
194/// faces or when the carrier does not cleanly divide the body.
195#[wasm_bindgen]
196pub fn split_solid_by_face_surface_json(request_json: &str) -> Result<String, JsValue> {
197    let request: SplitByFaceSurfaceRequest = json_in(request_json)?;
198    let pieces =
199        split_solid_by_face_surface(&request.solid, &request.surface).map_err(javascript_error)?;
200    let result = serde_json::json!({ "pieces": pieces });
201    json_out(&result)
202}
203
204#[wasm_bindgen]
205pub fn solid_mass_properties_json(solid_json: &str) -> Result<String, JsValue> {
206    let solid: BrepSolid = json_in(solid_json)?;
207    let properties = solid_mass_properties(&solid).map_err(javascript_error)?;
208    json_out(&properties)
209}
210
211#[wasm_bindgen]
212pub fn solid_mass_properties_full_json(solid_json: &str) -> Result<String, JsValue> {
213    let solid: BrepSolid = json_in(solid_json)?;
214    let properties = solid_mass_properties_full(&solid).map_err(javascript_error)?;
215    json_out(&properties)
216}
217
218#[wasm_bindgen]
219pub fn face_metrics_json(face_json: &str) -> Result<String, JsValue> {
220    let face: FaceRecord = json_in(face_json)?;
221    let metrics = FaceMetrics {
222        parameter_space_area: parameter_space_area(&face).map_err(javascript_error)?,
223        area: face_area(&face).map_err(javascript_error)?,
224        volume_contribution: face_volume_contribution(&face).map_err(javascript_error)?,
225        trim_polygons: trim_polygons(&face).map_err(javascript_error)?,
226    };
227    json_out(&metrics)
228}
229
230#[wasm_bindgen]
231pub fn tessellate_face_json(request_json: &str) -> Result<String, JsValue> {
232    let request: TessellateFaceRequest = json_in(request_json)?;
233    let mesh = tessellate_face(
234        &request.face,
235        TessellationOptions {
236            slabs_per_span_u: request.slabs_per_span_u,
237            steps_per_span_v: request.steps_per_span_v,
238        },
239        request.face.id as u32,
240    )
241    .map_err(javascript_error)?;
242    json_out(&mesh)
243}
244
245#[wasm_bindgen]
246pub fn tessellate_brep_json(request_json: &str) -> Result<String, JsValue> {
247    let request: TessellateBrepRequest = json_in(request_json)?;
248    let mesh = tessellate_brep(
249        &request.solid,
250        TessellationOptions {
251            slabs_per_span_u: request.slabs_per_span_u,
252            steps_per_span_v: request.steps_per_span_v,
253        },
254    )
255    .map_err(javascript_error)?;
256    json_out(&mesh)
257}
258
259#[wasm_bindgen]
260pub fn classify_points_json(request_json: &str) -> Result<String, JsValue> {
261    let request: ClassifyPointsRequest = json_in(request_json)?;
262    let classifications = request
263        .points
264        .into_iter()
265        .map(|point| classify_point(point, &request.solid, request.tolerance))
266        .collect::<Result<Vec<_>, _>>()
267        .map_err(javascript_error)?;
268    json_out(&classifications)
269}
270
271#[wasm_bindgen]
272pub fn arrange_segments_json(request_json: &str) -> Result<String, JsValue> {
273    let request: ArrangeSegmentsRequest = json_in(request_json)?;
274    let regions =
275        arrange_segments(&request.segments, request.tolerance).map_err(javascript_error)?;
276    json_out(&regions)
277}
278
279#[wasm_bindgen]
280pub fn build_pcurve_json(request_json: &str) -> Result<String, JsValue> {
281    let request: BuildPcurveRequest = json_in(request_json)?;
282    let pcurve =
283        build_pcurve_on_surface(&request.surface, &request.curve).map_err(javascript_error)?;
284    json_out(&pcurve)
285}
286
287#[wasm_bindgen]
288pub fn build_imprints_json(request_json: &str) -> Result<String, JsValue> {
289    let request: BuildImprintsRequest = json_in(request_json)?;
290    let result = build_imprints(&request.solid_a, &request.solid_b, &request.options)
291        .map_err(javascript_refusal)?;
292    json_out(&result)
293}
294
295#[wasm_bindgen]
296pub fn apply_edge_splits_json(request_json: &str) -> Result<String, JsValue> {
297    let request: ApplyEdgeSplitsRequest = json_in(request_json)?;
298    let result = apply_edge_splits(&request.solid, request.operand, &request.imprint)
299        .map_err(javascript_refusal)?;
300    json_out(&result)
301}
302
303#[wasm_bindgen]
304pub fn fragment_faces_json(request_json: &str) -> Result<String, JsValue> {
305    let request: FragmentFacesRequest = json_in(request_json)?;
306    let result = fragment_solid(&request.solid, request.operand, &request.imprint)
307        .map_err(javascript_refusal)?;
308    json_out(&result)
309}
310
311#[wasm_bindgen]
312pub fn boolean_brep_json(request_json: &str) -> Result<String, JsValue> {
313    let request: BooleanRequest = json_in(request_json)?;
314    let result = boolean_operation(
315        &request.first,
316        &request.second,
317        request.operation,
318        &request.options,
319    )
320    .map_err(javascript_refusal)?;
321    json_out(&result)
322}
323
324#[wasm_bindgen]
325pub fn boolean_brep_diagnostics_json(request_json: &str) -> Result<String, JsValue> {
326    let request: BooleanRequest = json_in(request_json)?;
327    let result = boolean_operation_with_diagnostics(
328        &request.first,
329        &request.second,
330        request.operation,
331        &request.options,
332    )
333    .map_err(javascript_refusal)?;
334    json_out(&result)
335}
336
337#[wasm_bindgen]
338pub fn offset_surface_json(request_json: &str) -> Result<String, JsValue> {
339    let request: OffsetSurfaceRequest = json_in(request_json)?;
340    let surface = offset_surface(&request.face, request.distance, request.planar_extension)
341        .map_err(javascript_refusal)?;
342    json_out(&surface)
343}
344
345#[derive(Deserialize)]
346pub(crate) struct SolveAssemblyRequest {
347    pub(crate) bodies: Vec<AssemblyBody>,
348    pub(crate) mates: Vec<AssemblyMate>,
349    #[serde(default)]
350    pub(crate) options: AssemblySolveOptions,
351}
352
353#[wasm_bindgen]
354pub fn solve_assembly_json(request_json: &str) -> Result<String, JsValue> {
355    let request: SolveAssemblyRequest = json_in(request_json)?;
356    let solution = solve_assembly(&request.bodies, &request.mates, &request.options)
357        .map_err(javascript_error)?;
358    json_out(&solution)
359}
360
361#[derive(Deserialize)]
362pub(crate) struct SegmentMeshRequest {
363    /// Flat xyz triples.
364    pub(crate) positions: Vec<f64>,
365    /// Triangle indices; empty = raw soup (STL-style, 3 positions/triangle).
366    #[serde(default)]
367    pub(crate) indices: Vec<u32>,
368    #[serde(default)]
369    pub(crate) options: SegmentOptions,
370}
371
372#[wasm_bindgen]
373pub fn segment_mesh_faces_json(request_json: &str) -> Result<String, JsValue> {
374    let request: SegmentMeshRequest = json_in(request_json)?;
375    let segmentation = segment_mesh_faces(&request.positions, &request.indices, &request.options)
376        .map_err(javascript_error)?;
377    json_out(&segmentation)
378}
379
380#[wasm_bindgen]
381pub fn mesh_regions_to_brep_json(request_json: &str) -> Result<String, JsValue> {
382    let request: SegmentMeshRequest = json_in(request_json)?;
383    let solid = mesh_regions_to_brep(&request.positions, &request.indices, &request.options)
384        .map_err(javascript_error)?;
385    json_out(&solid)
386}
387
388#[wasm_bindgen]
389pub fn mesh_regions_to_brep_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
390    let request: SegmentMeshRequest = json_in(request_json)?;
391    let solid = mesh_regions_to_brep(&request.positions, &request.indices, &request.options)
392        .map_err(javascript_error)?;
393    solid_buffer(&solid, "{}".into())
394}
395
396#[derive(Deserialize)]
397pub(crate) struct ThickenSheetRequest {
398    /// The sheet as an untrimmed surface patch over its full domain.
399    pub(crate) surface: NurbsSurface,
400    /// Signed thickness (positive = along the sheet normal Su×Sv).
401    pub(crate) thickness: f64,
402    /// Split the thickness half per side (sheet becomes the mid-surface).
403    #[serde(default)]
404    pub(crate) symmetric: bool,
405}
406
407#[wasm_bindgen]
408pub fn thicken_face_sheet_json(request_json: &str) -> Result<String, JsValue> {
409    let request: ThickenSheetRequest = json_in(request_json)?;
410    let bodies = thicken_face_sheet(&request.surface, request.thickness, request.symmetric)
411        .map_err(javascript_refusal)?;
412    json_out(&bodies)
413}
414
415#[wasm_bindgen]
416pub fn thicken_face_sheet_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
417    let request: ThickenSheetRequest = json_in(request_json)?;
418    let bodies = thicken_face_sheet(&request.surface, request.thickness, request.symmetric)
419        .map_err(javascript_refusal)?;
420    solid_buffer(single_body(&bodies, "thicken_face_sheet")?, "{}".into())
421}
422
423#[derive(Deserialize)]
424pub(crate) struct ThickenTrimmedSheetRequest {
425    pub(crate) surface: NurbsSurface,
426    /// Pcurve trim loops on the sheet: outer loop first (counter-clockwise),
427    /// optional holes (clockwise).
428    pub(crate) loops: Vec<Vec<NurbsCurve>>,
429    pub(crate) thickness: f64,
430    #[serde(default)]
431    pub(crate) symmetric: bool,
432}
433
434#[wasm_bindgen]
435pub fn thicken_trimmed_sheet_json(request_json: &str) -> Result<String, JsValue> {
436    let request: ThickenTrimmedSheetRequest = json_in(request_json)?;
437    let bodies = thicken_trimmed_sheet(
438        &request.surface,
439        &request.loops,
440        request.thickness,
441        request.symmetric,
442    )
443    .map_err(javascript_refusal)?;
444    json_out(&bodies)
445}
446
447#[wasm_bindgen]
448pub fn thicken_trimmed_sheet_buffer(request_json: &str) -> Result<WasmSolidBuffer, JsValue> {
449    let request: ThickenTrimmedSheetRequest = json_in(request_json)?;
450    let bodies = thicken_trimmed_sheet(
451        &request.surface,
452        &request.loops,
453        request.thickness,
454        request.symmetric,
455    )
456    .map_err(javascript_refusal)?;
457    solid_buffer(single_body(&bodies, "thicken_trimmed_sheet")?, "{}".into())
458}
459
460/// The one body a single-solid BUFFER export can carry.
461///
462/// A thicken whose trim a fold BAND crosses produces one body per regular
463/// piece, and this buffer shape holds exactly one solid. Returning the first
464/// and dropping the rest would hand a caller half a result it could not know
465/// was half, so the count is named and the `_json` export — which carries the
466/// whole list — is pointed at instead.
467fn single_body<'a>(bodies: &'a [BrepSolid], operation: &str) -> Result<&'a BrepSolid, JsValue> {
468    match bodies {
469        [body] => Ok(body),
470        _ => Err(javascript_error(format!(
471            "{operation}: the selected trim thickens into {} bodies and this buffer carries one \
472             — call {operation}_json, which returns them all",
473            bodies.len()
474        ))),
475    }
476}
477
478#[wasm_bindgen]
479pub fn offset_face_carrier_json(request_json: &str) -> Result<String, JsValue> {
480    let request: OffsetFaceCarrierRequest = json_in(request_json)?;
481    let carrier = offset_face_carrier(
482        &request.solid,
483        request.face_id,
484        request.distance,
485        request.planar_extension,
486    )
487    .map_err(javascript_refusal)?;
488    json_out(&carrier)
489}
490
491#[wasm_bindgen]
492pub fn offset_shell_json(request_json: &str) -> Result<String, JsValue> {
493    let request: OffsetShellRequest = json_in(request_json)?;
494    let result = offset_shell(&request.solid, &request.opening_face_ids, request.distance)
495        .map_err(javascript_refusal)?;
496    json_out(&result)
497}
498
499#[wasm_bindgen]
500pub fn offset_shell_diagnostics_json(request_json: &str) -> Result<String, JsValue> {
501    let request: OffsetShellDiagnosticsRequest = json_in(request_json)?;
502    let result = offset_shell_with_diagnostics(
503        &request.solid,
504        &request.opening_face_ids,
505        request.distance,
506        request.tolerances,
507    )
508    .map_err(javascript_refusal)?;
509    json_out(&result)
510}
511
512#[wasm_bindgen]
513pub fn export_step_json(request_json: &str) -> Result<String, JsValue> {
514    let request: ExportStepRequest = json_in(request_json)?;
515    export_step(
516        &request.solids,
517        &request.name,
518        &request.unit,
519        &request.timestamp,
520    )
521    .map_err(javascript_error)
522}
523
524#[wasm_bindgen]
525pub fn merge_curve_continuations_json(request_json: &str) -> Result<String, JsValue> {
526    let request: CoalesceRequest = json_in(request_json)?;
527    let solid = merge_curve_continuation_edges(&request.solid, request.tolerance)
528        .map_err(|refusal| javascript_error(refusal.message))?;
529    json_out(&solid)
530}
531
532#[wasm_bindgen]
533pub fn solve_sketch_json(request_json: &str) -> Result<String, JsValue> {
534    solve_sketch_from_json(request_json).map_err(javascript_error)
535}