Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ The workaround is to have an abstraction layer over the geometric predicates. Fo
When activating `wasm` the fallback rust-only predicates will be used. The downside of these is that they do not support lifted orientation test, i.e. we can not compute weighted Delaunay triangulations.
For the majority of the use cases however this is fine, and maybe `robust` can be extended in the future to support the weighted predicates as well.

### Building and publishing the WASM package
The name `rita` is already taken on npm, so **pass the scope at build time** so the generated `package.json` gets a scoped name (e.g. `@lempf/rita`). If you build without `--scope`, the package name stays `rita` and publish will try to use the existing npm package.

Build with your npm scope (use `--` so `--no-default-features` and `--features` go to cargo, not wasm-pack):
```bash
wasm-pack build rita --scope lempf --target web -- --no-default-features --features "std,wasm"
```
Then publish (scoped packages require `--access public`):
```bash
cd rita/pkg && npm publish --access=public
```
Install as `npm install @lempf/rita`.

## Testing
To make sure both predicate libraries produce the same results tests can be run for both features.

Expand Down
10 changes: 7 additions & 3 deletions rita/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ keywords = [
"math",
]
categories = ["algorithms", "graphics"]

repository = "https://github.com/glennDittmann/rita"
authors = ["Glenn Dittmann <glenn.dittmann@posteo.de>"]
license = "MIT"
readme = "../README.md"

[lib]
crate-type = ["rlib", "cdylib"]

[dependencies]
anyhow = { version = "1.0", default-features = false }
geogram_predicates = { version = "0.2.1", optional = true }
Expand All @@ -30,6 +32,8 @@ nalgebra = { version = "0.33", features = [
], default-features = false }
rayon = "1.10"
robust = { version = "1.2", optional = true }
wasm-bindgen = { version = "0.2", optional = true }
js-sys = { version = "0.3", optional = true }
arbitrary = { version = "1.4", optional = true, features = ["derive"] }

[dev-dependencies]
Expand All @@ -39,8 +43,8 @@ rita_test_utils = { path = "../rita_test_utils" }
default = ["std", "geogram"]
std = ["anyhow/std", "nalgebra/std"]
geogram = ["dep:geogram_predicates"]
# wasm: use pure-Rust robust predicates (no weighted Delaunay). For wasm32 build with: --no-default-features --features "std,wasm"
wasm = ["dep:robust"]
# wasm: use pure-Rust robust predicates + JS API. For wasm32: --no-default-features --features "std,wasm"
wasm = ["dep:robust", "dep:wasm-bindgen", "dep:js-sys"]
timing = ["std"]
logging = ["dep:log"]
log_timing = ["logging", "timing"]
3 changes: 3 additions & 0 deletions rita/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ pub mod tetrahedralization;
pub mod triangulation;
mod trids;
mod utils;

#[cfg(feature = "wasm")]
pub mod wasm;
79 changes: 79 additions & 0 deletions rita/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! WASM bindings for 2D Delaunay triangulation.
//!
//! Provides a single function `triangulate` that takes flat vertex coordinates and optional
//! epsilon, and returns triangles and vertices as 2D objects only: `{ x, y }`.

use crate::triangulation::Triangulation;
use wasm_bindgen::prelude::*;

/// 2D Delaunay triangulation.
///
/// # Arguments
/// * `vertices` - Flat array of 2D coordinates: [x1, y1, x2, y2, ...]
/// * `epsilon` - Optional epsilon for regularity (pass `null` or omit for `None`). When provided,
/// a positive value can speed up the triangulation.
///
/// # Returns
/// A JavaScript object with:
/// * `triangles` - Array of `{ id, a: { x, y }, b, c }`
/// * `vertices` - Array of `{ x, y }`
#[wasm_bindgen(js_name = triangulate)]
pub fn triangulate_2d(vertices: &[f64], epsilon: Option<f64>) -> Result<JsValue, JsValue> {
let vertices_2d = parse_vertices_2d(vertices)?;
if vertices_2d.len() < 3 {
return Err(JsValue::from_str(
"At least 3 vertices are required for 2D triangulation",
));
}

let mut t = Triangulation::new(epsilon);
t.insert_vertices(&vertices_2d, None, true)
.map_err(|e| JsValue::from_str(&format!("insert_vertices failed: {}", e)))?;

let tri_list = t.tris();
let vert_list = t.vertices();

let triangles_js = js_sys::Array::new();
for (i, tri) in tri_list.iter().enumerate() {
let obj = triangle_to_js(tri, i)?;
triangles_js.push(&obj);
}

let vertices_js = js_sys::Array::new();
for v in vert_list.iter() {
let obj = vertex2_to_js(v);
vertices_js.push(&obj);
}

let result = js_sys::Object::new();
js_sys::Reflect::set(&result, &"triangles".into(), &triangles_js)?;
js_sys::Reflect::set(&result, &"vertices".into(), &vertices_js)?;
Ok(result.into())
}

fn parse_vertices_2d(flat: &[f64]) -> Result<Vec<[f64; 2]>, JsValue> {
if flat.len() % 2 != 0 {
return Err(JsValue::from_str(
"Vertices must have even length (pairs of x, y)",
));
}
Ok(flat.chunks_exact(2).map(|c| [c[0], c[1]]).collect())
}

/// [x, y] -> { x, y } (2D vertex, same dimension as input)
fn vertex2_to_js(v: &[f64; 2]) -> JsValue {
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"x".into(), &v[0].into()).unwrap();
js_sys::Reflect::set(&obj, &"y".into(), &v[1].into()).unwrap();
obj.into()
}

/// Triangle2 -> { id, a, b, c } with each corner as { x, y }
fn triangle_to_js(tri: &[[f64; 2]; 3], index: usize) -> Result<JsValue, JsValue> {
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"id".into(), &format!("tri_{}", index).into())?;
js_sys::Reflect::set(&obj, &"a".into(), &vertex2_to_js(&tri[0]))?;
js_sys::Reflect::set(&obj, &"b".into(), &vertex2_to_js(&tri[1]))?;
js_sys::Reflect::set(&obj, &"c".into(), &vertex2_to_js(&tri[2]))?;
Ok(obj.into())
}