Skip to main content

neco_edge_routing_wasm/
lib.rs

1//! WebAssembly bindings for `neco-edge-routing` via `wasm-bindgen`.
2
3use js_sys::{Array, Object, Reflect};
4use neco_edge_routing::{route, PathKind, RouteRequest, RouteStyle};
5use wasm_bindgen::prelude::*;
6
7/// Route an edge between two points using the given style name.
8#[wasm_bindgen]
9pub fn route_edge(
10    style: &str,
11    from_x: f64,
12    from_y: f64,
13    to_x: f64,
14    to_y: f64,
15) -> Result<JsValue, JsValue> {
16    let request = RouteRequest {
17        from: (from_x, from_y),
18        to: (to_x, to_y),
19        from_tangent: (1.0, 0.0),
20        to_tangent: (-1.0, 0.0),
21        style: parse_style(style)?,
22    };
23
24    let path = route(&request).map_err(|error| JsValue::from_str(&error.to_string()))?;
25    path_to_js(style, path)
26}
27
28fn parse_style(style: &str) -> Result<RouteStyle, JsValue> {
29    match style {
30        "bezier" => Ok(RouteStyle::Bezier { curvature: 0.25 }),
31        "orthogonal" => Ok(RouteStyle::Orthogonal {
32            corner_radius: 16.0,
33        }),
34        "spline" => Ok(RouteStyle::Spline),
35        "nurbs" => Ok(RouteStyle::Nurbs { degree: 3 }),
36        other => Err(JsValue::from_str(&format!(
37            "unsupported route style: {other}"
38        ))),
39    }
40}
41
42fn path_to_js(style: &str, path: neco_edge_routing::PathData) -> Result<JsValue, JsValue> {
43    let object = Object::new();
44    Reflect::set(
45        &object,
46        &JsValue::from_str("style"),
47        &JsValue::from_str(style),
48    )?;
49    Reflect::set(
50        &object,
51        &JsValue::from_str("kind"),
52        &JsValue::from_str(kind_name(&path.kind)),
53    )?;
54
55    let points = Array::new();
56    for (x, y) in path.points {
57        let point = Object::new();
58        Reflect::set(&point, &JsValue::from_str("x"), &JsValue::from_f64(x))?;
59        Reflect::set(&point, &JsValue::from_str("y"), &JsValue::from_f64(y))?;
60        points.push(&point);
61    }
62    Reflect::set(&object, &JsValue::from_str("points"), &points)?;
63
64    if let PathKind::Nurbs { knots, weights } = path.kind {
65        Reflect::set(&object, &JsValue::from_str("knots"), &vec_to_js(knots))?;
66        Reflect::set(&object, &JsValue::from_str("weights"), &vec_to_js(weights))?;
67    }
68
69    Ok(JsValue::from(object))
70}
71
72fn vec_to_js(values: Vec<f64>) -> JsValue {
73    let array = Array::new();
74    for value in values {
75        array.push(&JsValue::from_f64(value));
76    }
77    JsValue::from(array)
78}
79
80fn kind_name(kind: &PathKind) -> &'static str {
81    match kind {
82        PathKind::Polyline => "polyline",
83        PathKind::Cubic => "cubic",
84        PathKind::Quadratic => "quadratic",
85        PathKind::Nurbs { .. } => "nurbs",
86    }
87}