1use rayon::prelude::*;
4use serde::Serialize;
5use serde_pickle as pkl;
6use std::{error::Error, ffi::CString, fmt, fs::File, path::Path};
7
8#[repr(C)]
11#[derive(Debug, Copy, Clone)]
12pub struct triangulateio {
13 pub pointlist: *mut f64,
14 pub pointattributelist: *mut f64,
15 pub pointmarkerlist: *mut ::std::os::raw::c_int,
16 pub numberofpoints: ::std::os::raw::c_int,
17 pub numberofpointattributes: ::std::os::raw::c_int,
18 pub trianglelist: *mut ::std::os::raw::c_int,
19 pub triangleattributelist: *mut f64,
20 pub trianglearealist: *mut f64,
21 pub neighborlist: *mut ::std::os::raw::c_int,
22 pub numberoftriangles: ::std::os::raw::c_int,
23 pub numberofcorners: ::std::os::raw::c_int,
24 pub numberoftriangleattributes: ::std::os::raw::c_int,
25 pub segmentlist: *mut ::std::os::raw::c_int,
26 pub segmentmarkerlist: *mut ::std::os::raw::c_int,
27 pub numberofsegments: ::std::os::raw::c_int,
28 pub holelist: *mut f64,
29 pub numberofholes: ::std::os::raw::c_int,
30 pub regionlist: *mut f64,
31 pub numberofregions: ::std::os::raw::c_int,
32 pub edgelist: *mut ::std::os::raw::c_int,
33 pub edgemarkerlist: *mut ::std::os::raw::c_int,
34 pub normlist: *mut f64,
35 pub numberofedges: ::std::os::raw::c_int,
36}
37impl Default for triangulateio {
38 fn default() -> Self {
39 triangulateio {
40 pointlist: std::ptr::null_mut::<f64>(),
41 pointattributelist: std::ptr::null_mut::<f64>(),
42 pointmarkerlist: std::ptr::null_mut::<i32>(),
43 numberofpoints: 0i32,
44 numberofpointattributes: 0i32,
45 trianglelist: std::ptr::null_mut::<i32>(),
46 triangleattributelist: std::ptr::null_mut::<f64>(),
47 trianglearealist: std::ptr::null_mut::<f64>(),
48 neighborlist: std::ptr::null_mut::<i32>(),
49 numberoftriangles: 0i32,
50 numberofcorners: 0i32,
51 numberoftriangleattributes: 0i32,
52 segmentlist: std::ptr::null_mut::<i32>(),
53 segmentmarkerlist: std::ptr::null_mut::<i32>(),
54 numberofsegments: 0i32,
55 holelist: std::ptr::null_mut::<f64>(),
56 numberofholes: 0i32,
57 regionlist: std::ptr::null_mut::<f64>(),
58 numberofregions: 0i32,
59 edgelist: std::ptr::null_mut::<i32>(),
60 edgemarkerlist: std::ptr::null_mut::<i32>(),
61 normlist: std::ptr::null_mut::<f64>(),
62 numberofedges: 0i32,
63 }
64 }
65}
66
67extern "C" {
68 pub fn triangulate(
69 arg1: *mut ::std::os::raw::c_char,
70 arg2: *mut triangulateio,
71 arg3: *mut triangulateio,
72 arg4: *mut triangulateio,
73 );
74}
75extern "C" {
76 pub fn trifree(memptr: *mut ::std::os::raw::c_int);
77}
78
79#[derive(Default, Debug, Serialize)]
81pub struct Delaunay {
82 pub points: Vec<f64>,
84 pub point_markers: Vec<i32>,
86 pub triangles: Vec<usize>,
87 pub neighbors: Option<Vec<i32>>,
89 pub edges: Option<Vec<usize>>,
91}
92
93impl Delaunay {
94 pub fn new() -> Self {
96 Self {
97 points: vec![],
98 point_markers: vec![],
99 triangles: vec![],
100 neighbors: None,
101 edges: None,
102 }
103 }
104 pub fn builder() -> Builder {
106 Builder::new()
107 }
108 pub fn n_vertices(&self) -> usize {
110 self.points.len() / 2
111 }
112 pub fn n_triangles(&self) -> usize {
114 self.triangles.len() / 3
115 }
116 pub fn vertex_iter(&self) -> std::slice::Chunks<'_, f64> {
118 self.points.chunks(2)
119 }
120 pub fn vertex_iter_mut(&mut self) -> std::slice::ChunksMut<'_, f64> {
122 self.points.chunks_mut(2)
123 }
124 pub fn vertex_par_iter(&self) -> rayon::slice::Chunks<'_, f64> {
126 self.points.par_chunks(2)
127 }
128 pub fn triangle_iter(&self) -> std::slice::Chunks<'_, usize> {
130 self.triangles.chunks(3)
131 }
132 pub fn triangle_iter_mut(&mut self) -> std::slice::ChunksMut<'_, usize> {
134 self.triangles.chunks_mut(3)
135 }
136 pub fn triangle_par_iter(&self) -> rayon::slice::Chunks<'_, usize> {
138 self.triangles.par_chunks(3)
139 }
140 pub fn x(&self) -> Vec<f64> {
142 self.vertex_iter().map(|xy| xy[0]).collect()
143 }
144 pub fn y(&self) -> Vec<f64> {
146 self.vertex_iter().map(|xy| xy[1]).collect()
147 }
148 pub fn triangle_vertex_iter(&self) -> impl Iterator<Item = Vec<(f64, f64)>> + '_ {
150 let x = self.x();
151 let y = self.y();
152 self.triangle_iter()
153 .map(move |t| t.iter().map(|&i| (x[i], y[i])).collect::<Vec<(f64, f64)>>())
154 }
155 pub fn filter_within_circle(&mut self, radius: f64, origin: Option<(f64, f64)>) -> &mut Self {
157 let x = self.x();
158 let y = self.y();
159 let (x0, y0) = origin.unwrap_or_default();
160 self.triangles = self
161 .triangles
162 .chunks(3)
163 .filter(move |t| t.iter().all(|&i| (x[i] - x0).hypot(y[i] - y0) > radius))
164 .flatten()
165 .cloned()
166 .collect();
167 self
168 }
169 pub fn triangle_areas(&self) -> Vec<f64> {
171 let vertices: Vec<Vec<f64>> = self.vertex_iter().map(|x| x.to_vec()).collect();
172 self.triangle_iter()
173 .map(|t| {
174 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
175 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs()
176 })
177 .collect()
178 }
179 pub fn area(&self) -> f64 {
181 let vertices: Vec<Vec<f64>> = self.vertex_iter().map(|x| x.to_vec()).collect();
182 self.triangle_iter().fold(0., |s, t| {
183 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
184 s + 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs()
185 })
186 }
187 pub fn mesh_area(&self) -> f64 {
189 let vertices: Vec<Vec<f64>> = self.vertex_iter().map(|x| x.to_vec()).collect();
190 self.triangle_iter().fold(0., |s, t| {
191 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
192 s + 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs()
193 })
194 }
195 pub fn average(&self, vertices: &[[f64; 3]], data: &[f64]) -> f64 {
196 self.triangle_iter().fold(0., |s, t| {
197 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
198 let ta = 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs();
199 let sa = t.iter().fold(0., |m, i| m + data[*i]) / 3 as f64;
200 s + ta * sa
201 })
202 }
203 pub fn average_with<F: Fn(f64) -> f64>(
204 &self,
205 vertices: &[[f64; 3]],
206 data: &[f64],
207 f: F,
208 ) -> f64 {
209 self.triangle_iter().fold(0., |s, t| {
210 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
211 let ta = 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs();
212 let sa = t.iter().fold(0., |m, i| m + f(data[*i])) / 3 as f64;
213 s + ta * sa
214 })
215 }
216 pub fn dot(&self, v_a: &[f64], v_b: &[f64]) -> f64 {
218 assert_eq!(v_a.len(), self.n_vertices());
219 assert_eq!(v_b.len(), self.n_vertices());
220 let vertices: Vec<Vec<f64>> = self.vertex_iter().map(|x| x.to_vec()).collect();
221 self.triangle_iter().fold(0., |s, t| {
222 let (a, b, c) = (&vertices[t[0]], &vertices[t[1]], &vertices[t[2]]);
223 let ta = 0.5 * ((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])).abs();
224 let sa = t.iter().fold(0., |m, i| m + v_a[*i]) / 3 as f64;
225 let sb = t.iter().fold(0., |m, i| m + v_b[*i]) / 3 as f64;
226 s + ta * sa * sb
227 }) / self.area()
228 }
229 pub fn is_point_inside(&self, point: &[f64], triangle_id: usize) -> bool {
231 let triangle = self.triangle_iter().nth(triangle_id).unwrap();
232 let points: Vec<&[f64]> = self.vertex_iter().collect();
233 for i in 0..3 {
234 let j = (i + 1) % 3;
235 let vi = triangle[i];
236 let vj = triangle[j];
237 let d = (points[vj][0] - points[vi][0]) * (point[1] - points[vi][1])
238 - (points[vj][1] - points[vi][1]) * (point[0] - points[vi][0]);
239 if d < 0. && d.abs() > 1e-9 {
240 return false;
241 }
242 }
243 true
244 }
245 pub fn which_contains_point(&self, point: &[f64]) -> Option<usize> {
247 for k in 0..self.n_triangles() {
248 if self.is_point_inside(point, k) {
249 return Some(k);
250 }
251 }
252 None
253 }
254 pub fn point_into_barycentric(&self, point: &[f64], triangle_ids: &[usize]) -> [f64; 3] {
258 let points: Vec<&[f64]> = self.vertex_iter().collect();
259 let v: Vec<&[f64]> = triangle_ids.iter().map(|&i| points[i]).collect();
260 let area =
261 (v[1][1] - v[2][1]) * (v[0][0] - v[2][0]) + (v[2][0] - v[1][0]) * (v[0][1] - v[2][1]);
262 let w0 = ((v[1][1] - v[2][1]) * (point[0] - v[2][0])
263 + (v[2][0] - v[1][0]) * (point[1] - v[2][1]))
264 / area;
265 let w1 = ((v[2][1] - v[0][1]) * (point[0] - v[2][0])
266 + (v[0][0] - v[2][0]) * (point[1] - v[2][1]))
267 / area;
268 [w0, w1, 1. - w0 - w1]
269 }
270 pub fn barycentric_interpolation(&self, point: &[f64], val_at_vertices: &[f64]) -> f64 {
274 match self.which_contains_point(point) {
275 Some(tid) => {
276 let triangle_ids = self.triangle_iter().nth(tid).unwrap();
277 let values: Vec<f64> = triangle_ids.iter().map(|&i| val_at_vertices[i]).collect();
278 self.point_into_barycentric(point, triangle_ids)
279 .iter()
280 .zip(values.iter())
281 .fold(0., |a, (w, v)| a + w * v)
282 }
283 None => std::f64::NAN,
284 }
285 }
286 pub fn dump<T: AsRef<Path>>(&self, filename: T) -> Result<(), Box<dyn Error>> {
316 pkl::to_writer(&mut File::create(filename)?, self, true)?;
317 Ok(())
318 }
319}
320
321impl fmt::Display for Delaunay {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 let areas = self.triangle_areas();
324 let areas_min = areas.iter().cloned().fold(f64::INFINITY, f64::min);
325 let areas_max = areas.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
326 let areas_sum = areas.iter().sum::<f64>();
327 let areas_mean = areas_sum / areas.len() as f64;
328 write!(
329 f,
330 r#"Delaunay triangulation:
331 - vertices: {}
332 - triangles: {}
333 - triangles area:
334 - min : {:.6}
335 - max : {:.6}
336 - mean: {:.6}
337 - sum : {:.6}"#,
338 self.n_vertices(),
339 self.n_triangles(),
340 areas_min,
341 areas_max,
342 areas_mean,
343 areas_sum
344 )
345 }
346}
347
348pub enum TriangulateIO {
349 Points(Vec<f64>),
350}
351
352#[derive(Debug)]
354pub struct Builder {
355 points: Vec<f64>,
357 segments: Option<Vec<i32>>,
358 n_segments: i32,
359 holes: Option<Vec<f64>>,
360 n_holes: i32,
361 switches: String,
362 boundary_marker: i32,
363 point_markers: Option<Vec<i32>>,
364 segment_markers: Option<Vec<i32>>,
365 tri_io: triangulateio,
366}
367impl Builder {
368 pub fn new() -> Self {
370 Self {
371 switches: "z".to_owned(),
373 points: vec![],
374 segments: None,
375 n_segments: 032,
376 holes: None,
377 n_holes: 0i32,
378 boundary_marker: 1i32,
379 point_markers: None,
380 segment_markers: None,
381 tri_io: triangulateio::default(),
382 }
383 }
384 pub fn add_nodes(&mut self, nodes: &[f64]) -> &mut Self {
386 self.points.extend(nodes);
387 self
388 }
389 pub fn set_segments(self, x: Vec<i32>, y: Vec<i32>) -> Self {
390 assert!(x.len() == y.len(), "x and y are not the same length.");
391 let n = x.len() as i32;
393 let xy = x
394 .into_iter()
395 .zip(y.into_iter())
396 .flat_map(|(x, y)| vec![x, y])
397 .collect();
398 Self {
400 segments: Some(xy),
401 n_segments: n,
402 ..self
403 }
404 }
405 pub fn add_holes(&mut self, x: f64, y: f64) -> &mut Self {
406 match self.holes {
407 Some(ref mut h) => {
408 h.extend(vec![x, y]);
409 }
410 None => {
411 self.holes = Some(vec![x, y]);
412 }
413 }
414 self
415 }
416 pub fn set_tri_points(self, points: Vec<f64>) -> Self {
418 Self { points, ..self }
421 }
422 pub fn add_polygon(&mut self, vertices: &[f64]) -> &mut Self {
424 let a = (self.points.len() / 2) as i32;
426 let n_segments = (vertices.len() / 2) as i32;
427 let segments_vertices = (0..n_segments).flat_map(|k| vec![a + k, a + (k + 1) % n_segments]);
444 match self.segments {
445 Some(ref mut s) => {
446 s.extend(segments_vertices);
447 }
448 None => {
449 self.segments = Some(segments_vertices.collect::<Vec<i32>>());
450 }
451 };
452 self.points.extend(vertices);
453 self
454 }
455 pub fn set_switches(&mut self, switches: &str) -> &mut Self {
457 self.switches = format!("z{}", switches);
458 self
459 }
460 pub fn build(&mut self) -> Delaunay {
462 self.tri_io.numberofpoints = (self.points.len() / 2) as i32;
463 self.tri_io.pointlist = self.points.as_mut_ptr();
464 if let Some(ref mut s) = self.segments {
465 self.tri_io.numberofsegments = (s.len() / 2) as i32;
466 self.tri_io.segmentlist = s.as_mut_ptr();
467 }
468 if let Some(ref mut h) = self.holes {
469 self.tri_io.numberofholes = (h.len() / 2) as i32;
470 self.tri_io.holelist = h.as_mut_ptr();
471 }
472 let mut delaunay: triangulateio = unsafe { std::mem::zeroed() };
474 let switches = CString::new(self.switches.as_str()).unwrap();
476 unsafe {
477 let mut empty_tri: triangulateio = std::mem::zeroed();
478 triangulate(
479 switches.into_raw(),
480 &mut self.tri_io,
481 &mut delaunay,
482 &mut empty_tri,
483 )
484 };
485 let points: Vec<f64> = unsafe {
486 let n = delaunay.numberofpoints as usize * 2;
487 Vec::from_raw_parts(delaunay.pointlist, n, n)
488 };
489 let point_markers: Vec<i32> = unsafe {
490 let n = delaunay.numberofpoints as usize;
491 Vec::from_raw_parts(delaunay.pointmarkerlist, n, n)
492 };
493 let triangles: Vec<usize> = unsafe {
494 let n = delaunay.numberoftriangles as usize * 3;
495 Vec::from_raw_parts(delaunay.trianglelist, n, n)
496 }
497 .iter()
498 .map(|x| *x as usize)
499 .collect();
500 let neighbors: Option<Vec<i32>> = if self.switches.contains("n") {
501 let n = delaunay.numberoftriangles as usize * 3;
502 Some(unsafe { Vec::from_raw_parts(delaunay.neighborlist, n, n) })
503 } else {
504 None
505 };
506 let edges: Option<Vec<usize>> = if self.switches.contains("e") {
507 let n = delaunay.numberofedges as usize * 2;
508 Some(
509 unsafe { Vec::from_raw_parts(delaunay.edgelist, n, n) }
510 .iter()
511 .map(|x| *x as usize)
512 .collect(),
513 )
514 } else {
515 None
516 };
517 Delaunay {
518 points,
519 point_markers,
520 triangles,
521 neighbors,
522 edges,
523 }
524 }
525}
526impl From<Vec<f64>> for Builder {
527 fn from(points: Vec<f64>) -> Self {
528 Self {
529 points,
531 switches: "z".to_owned(),
532 segments: None,
533 n_segments: 032,
534 holes: None,
535 n_holes: 0i32,
536 boundary_marker: 1i32,
537 point_markers: None,
538 segment_markers: None,
539 tri_io: triangulateio::default(),
540 }
541 }
542}
543impl From<&[f64]> for Builder {
544 fn from(points: &[f64]) -> Self {
545 Self {
546 points: points.to_owned(),
548 switches: "z".to_owned(),
549 segments: None,
550 n_segments: 032,
551 holes: None,
552 n_holes: 0i32,
553 boundary_marker: 1i32,
554 point_markers: None,
555 segment_markers: None,
556 tri_io: triangulateio::default(),
557 }
558 }
559}
560
561impl fmt::Display for Builder {
562 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563 let nodes_1st_line = format!("{} 2 0 0", self.points.len() / 2);
564 let nodes = self
565 .points
566 .chunks(2)
567 .enumerate()
568 .map(|(k, xy)| format!("{} {} {}", k, xy[0], xy[1]))
569 .collect::<Vec<String>>()
570 .join("\n");
571 let segs = match &self.segments {
572 Some(s) => {
573 let segs_1st_line = format!("{} 0", s.len() / 2);
574 let segs = s
575 .chunks(2)
576 .enumerate()
577 .map(|(k, xy)| format!("{} {} {}", k, xy[0], xy[1]))
578 .collect::<Vec<String>>()
579 .join("\n");
580 let holes_1st_line = format!("{}", self.n_holes);
581 let holes = match &self.holes {
582 Some(h) => h
583 .chunks(2)
584 .enumerate()
585 .map(|(k, xy)| format!("{} {} {}", k, xy[0], xy[1]))
586 .collect::<Vec<String>>()
587 .join("\n"),
588 None => "".to_owned(),
589 };
590 [segs_1st_line, segs, holes_1st_line, holes].join("\n")
591 }
592 None => "".to_owned(),
593 };
594 write!(f, "{}", [nodes_1st_line, nodes, segs].join("\n"))
595 }
596}
597
598#[cfg(test)]
643mod tests {
644 use super::*;
645 #[test]
646 fn area() {
647 let tri = Builder::new()
648 .add_nodes(&[0., 0.])
649 .add_polygon(&[1., 0., 0., 1., -1., 0., 0., -1.])
650 .build();
651 assert_eq!(tri.area(), 2.)
652 }
653}