brepkit_wasm/bindings/
polygon2d.rs1#![allow(clippy::missing_errors_doc)]
4
5use wasm_bindgen::prelude::*;
6
7use crate::error::{WasmError, validate_positive};
8use crate::helpers::{parse_polygon_2d, polygons_overlap_2d};
9use crate::kernel::BrepKernel;
10use brepkit_math::polygon2d::{
11 chamfer_polygon_2d, fillet_polygon_2d, find_common_segments, sutherland_hodgman_clip,
12};
13
14#[wasm_bindgen]
15impl BrepKernel {
16 #[wasm_bindgen(js_name = "offsetPolygon2d")]
23 #[allow(clippy::needless_pass_by_value, clippy::unused_self)]
24 pub fn offset_polygon_2d(
25 &self,
26 coords: Vec<f64>,
27 distance: f64,
28 tolerance: f64,
29 ) -> Result<Vec<f64>, JsError> {
30 if !coords.len().is_multiple_of(2) {
31 return Err(WasmError::InvalidInput {
32 reason: format!(
33 "2D coordinate array length must be even, got {}",
34 coords.len()
35 ),
36 }
37 .into());
38 }
39 let points: Vec<brepkit_math::vec::Point2> = coords
40 .chunks_exact(2)
41 .map(|c| brepkit_math::vec::Point2::new(c[0], c[1]))
42 .collect();
43 let result = brepkit_math::polygon_offset::offset_polygon_2d(&points, distance, tolerance)?;
44 Ok(result.iter().flat_map(|p| [p.x(), p.y()]).collect())
45 }
46
47 #[wasm_bindgen(js_name = "pointInPolygon2d")]
54 #[allow(clippy::unused_self)]
55 pub fn point_in_polygon_2d(
56 &self,
57 polygon_coords: Vec<f64>,
58 px: f64,
59 py: f64,
60 ) -> Result<bool, JsError> {
61 if !polygon_coords.len().is_multiple_of(2) || polygon_coords.len() < 6 {
62 return Err(WasmError::InvalidInput {
63 reason: "polygon needs at least 3 points (6 coordinates)".into(),
64 }
65 .into());
66 }
67 let polygon: Vec<brepkit_math::vec::Point2> = polygon_coords
68 .chunks_exact(2)
69 .map(|c| brepkit_math::vec::Point2::new(c[0], c[1]))
70 .collect();
71 let point = brepkit_math::vec::Point2::new(px, py);
72 Ok(brepkit_math::predicates::point_in_polygon(point, &polygon))
73 }
74
75 #[wasm_bindgen(js_name = "polygonsIntersect2d")]
81 #[allow(clippy::unused_self)]
82 pub fn polygons_intersect_2d(
83 &self,
84 coords_a: Vec<f64>,
85 coords_b: Vec<f64>,
86 ) -> Result<bool, JsError> {
87 let poly_a = parse_polygon_2d(&coords_a)?;
88 let poly_b = parse_polygon_2d(&coords_b)?;
89 Ok(polygons_overlap_2d(&poly_a, &poly_b))
90 }
91
92 #[wasm_bindgen(js_name = "intersectPolygons2d")]
100 #[allow(clippy::unused_self)]
101 pub fn intersect_polygons_2d(
102 &self,
103 coords_a: Vec<f64>,
104 coords_b: Vec<f64>,
105 ) -> Result<Vec<f64>, JsError> {
106 let subject = parse_polygon_2d(&coords_a)?;
107 let clip = parse_polygon_2d(&coords_b)?;
108 let result = sutherland_hodgman_clip(&subject, &clip);
109 Ok(result.iter().flat_map(|p| [p.x(), p.y()]).collect())
110 }
111
112 #[wasm_bindgen(js_name = "commonSegment2d")]
118 #[allow(clippy::unused_self)]
119 pub fn common_segment_2d(
120 &self,
121 coords_a: Vec<f64>,
122 coords_b: Vec<f64>,
123 ) -> Result<Vec<f64>, JsError> {
124 let poly_a = parse_polygon_2d(&coords_a)?;
125 let poly_b = parse_polygon_2d(&coords_b)?;
126 let tolerance = 1e-7;
127 let result = find_common_segments(&poly_a, &poly_b, tolerance);
128 Ok(result
129 .iter()
130 .flat_map(|(a, b)| [a.x(), a.y(), b.x(), b.y()])
131 .collect())
132 }
133
134 #[wasm_bindgen(js_name = "fillet2d")]
140 #[allow(clippy::unused_self)]
141 pub fn fillet_2d(&self, coords: Vec<f64>, radius: f64) -> Result<Vec<f64>, JsError> {
142 validate_positive(radius, "radius")?;
143 let polygon = parse_polygon_2d(&coords)?;
144 let result = fillet_polygon_2d(&polygon, radius);
145 Ok(result.iter().flat_map(|p| [p.x(), p.y()]).collect())
146 }
147
148 #[wasm_bindgen(js_name = "chamfer2d")]
154 #[allow(clippy::unused_self)]
155 pub fn chamfer_2d(&self, coords: Vec<f64>, distance: f64) -> Result<Vec<f64>, JsError> {
156 validate_positive(distance, "distance")?;
157 let polygon = parse_polygon_2d(&coords)?;
158 let result = chamfer_polygon_2d(&polygon, distance);
159 Ok(result.iter().flat_map(|p| [p.x(), p.y()]).collect())
160 }
161}