triangle_rs/
lib.rs

1//! A Rust library wrapped around the 2D mesh generator and Delaunay triangulator [Triangle](https://www.cs.cmu.edu/~quake/triangle.html)
2
3use rayon::prelude::*;
4use serde::Serialize;
5use serde_pickle as pkl;
6use std::{error::Error, ffi::CString, fmt, fs::File, path::Path};
7
8//include!("bindings.rs");
9
10#[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/// Delaunay triangulation
80#[derive(Default, Debug, Serialize)]
81pub struct Delaunay {
82    /// Triangulation vertices as [x0,y0,x1,y1,...]
83    pub points: Vec<f64>,
84    /// Indices in `points.chunks(2)`, the first 3 indices correspond to the vertices of the 1st triangle, the next 3 to the 2nd triangle, etc...
85    pub point_markers: Vec<i32>,
86    pub triangles: Vec<usize>,
87    /// List of triangles neighbors: indices in triangles.chunks(3) (3 integers per triangle)
88    pub neighbors: Option<Vec<i32>>,
89    /// Edges endpoints: indices in points.chunks(2) (2 integers per edge)
90    pub edges: Option<Vec<usize>>,
91}
92
93impl Delaunay {
94    /// Creates a new empty Delaunay triangulation
95    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    /// Returns the `Delaunay` builder
105    pub fn builder() -> Builder {
106        Builder::new()
107    }
108    /// Returns the number of vertices
109    pub fn n_vertices(&self) -> usize {
110        self.points.len() / 2
111    }
112    /// Returns the number of Delaunay triangles
113    pub fn n_triangles(&self) -> usize {
114        self.triangles.len() / 3
115    }
116    /// Returns an iterator over the vertices, each item is a vertex (x,y) coordinates
117    pub fn vertex_iter(&self) -> std::slice::Chunks<'_, f64> {
118        self.points.chunks(2)
119    }
120    /// Returns an iterator over mutable vertices, each item is a vertex (x,y) coordinates
121    pub fn vertex_iter_mut(&mut self) -> std::slice::ChunksMut<'_, f64> {
122        self.points.chunks_mut(2)
123    }
124    /// Returns a parallel iterator over the vertices, each item is a vertex (x,y) coordinates
125    pub fn vertex_par_iter(&self) -> rayon::slice::Chunks<'_, f64> {
126        self.points.par_chunks(2)
127    }
128    /// Returns an iterator over the triangles, each item is the indices of the vertices in `vertex_iter`
129    pub fn triangle_iter(&self) -> std::slice::Chunks<'_, usize> {
130        self.triangles.chunks(3)
131    }
132    /// Returns an iterator over mutable triangles, each item is the indices of the vertices in `vertex_iter`
133    pub fn triangle_iter_mut(&mut self) -> std::slice::ChunksMut<'_, usize> {
134        self.triangles.chunks_mut(3)
135    }
136    /// Returns a parallel iterator over the triangles, each item is the indices of the vertices in `vertex_iter`
137    pub fn triangle_par_iter(&self) -> rayon::slice::Chunks<'_, usize> {
138        self.triangles.par_chunks(3)
139    }
140    /// Gets node x coordinates
141    pub fn x(&self) -> Vec<f64> {
142        self.vertex_iter().map(|xy| xy[0]).collect()
143    }
144    /// Gets node y coordinates
145    pub fn y(&self) -> Vec<f64> {
146        self.vertex_iter().map(|xy| xy[1]).collect()
147    }
148    /// Returns an interator over the triangles, each item is a vector of the 3 (x,y) vertices coordinates
149    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    /// Removes triangles inside a circle of given `radius` centered on `Some(origin)`
156    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    /// Returns the triangle areas
170    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    /// Returns the area covered by the mesh as the sum of the triangle area
180    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    /// Returns the area covered by the mesh as the sum of the Delaunay triangles area
188    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    /// Returns the dot product of two vector `a` and `b` sampled on the Delaunay mesh
217    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    /// Returns true if a point `[x,y]` is inside the triangle given by its index (`triangle_id`) in `triangles_iter`, otherwise returns false
230    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    /// Finds the index of the triangle in `triangles_iter` that contains the given point `[x,y]`
246    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    /// Returns the barycentric coordinates of a point `[x,y]` with respect to the triangle that contains it
255    ///
256    /// The triangle that contains the point is specified with the indices `triangle_ids` in `vertex_iter` of the triangle vertices
257    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    /// Linearly interpolates at a given point [x,y], values `val_at_vertices` at the Delaunay mesh vertices
271    ///
272    /// The linear interpolation is based on the barycentric coordinates of the point
273    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    /*
287    pub fn linear_interpolation(&self, point: &[f64], val_at_points: &[f64]) -> f64 {
288        match self.contain_point(point) {
289            Some(tid) => {
290                println!("Triangle #{}", tid);
291                let ipts = self.triangles[tid].clone();
292                let mut wgts = vec![0f64; 3];
293                for i in 0..3 {
294                    //let j = if i + 1 == 3 { 0 } else { i + 1 };
295                    //let k = if i + 2 == 3 { 0 } else { i + 2 };
296                    let j = (i + 1) % 3;
297                    let k = (i + 2) % 3;
298                    wgts[k] = (self.points[ipts[j]][0] - self.points[ipts[i]][0])
299                        * (point[1] - self.points[ipts[i]][1])
300                        - (self.points[ipts[j]][1] - self.points[ipts[i]][1])
301                            * (point[0] - self.points[ipts[i]][0]);
302                }
303                println!("weights: {:?}", wgts);
304                let sum: f64 = wgts.iter().sum();
305                let values: Vec<f64> = ipts.iter().map(|k| val_at_points[*k]).collect();
306                println!("values: {:?}", values);
307                wgts.iter()
308                    .zip(values.iter())
309                    .fold(0f64, |a, (w, v)| a + w * v / sum)
310            }
311            None => std::f64::NAN,
312        }
313    }
314     */
315    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/// Delaunay triangulation builder
353#[derive(Debug)]
354pub struct Builder {
355    //triangulate_io: Vec<TriangulateIO>,
356    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    /// Creates a new Delaunay triangulation builder
369    pub fn new() -> Self {
370        Self {
371            //triangulate_io: vec![],
372            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    /// Sets the Delaunay mesh `x` and `y` vertices coordinates
385    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 mut data = self.triangulate_io;
392        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        //data.push(TriangulateIO::Points(xy));
399        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    /// Sets the Delaunay mesh vertices as [x0,y0,x1,y1,...]
417    pub fn set_tri_points(self, points: Vec<f64>) -> Self {
418        /*let mut data = self.triangulate_io;
419        data.push(TriangulateIO::Points(points));*/
420        Self { points, ..self }
421    }
422    /// Adds a closed polygon given its vertices [x1,y1,x2,y2,...]
423    pub fn add_polygon(&mut self, vertices: &[f64]) -> &mut Self {
424        //let boundary_marker = self.boundary_marker + 1;
425        let a = (self.points.len() / 2) as i32;
426        let n_segments = (vertices.len() / 2) as i32;
427        /*
428        let point_markers = match self.point_markers.clone() {
429            Some(mut p_m) => {
430                p_m.extend(vec![boundary_marker; n_segments as usize]);
431                p_m
432            }
433            None => vec![boundary_marker; n_segments as usize],
434        };
435        let segment_markers = match self.segment_markers.clone() {
436            Some(mut s_m) => {
437                s_m.extend(vec![boundary_marker; n_segments as usize]);
438                s_m
439            }
440            None => vec![boundary_marker; n_segments as usize],
441        };
442        println!("point markers: {:?}", point_markers);*/
443        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    /// Sets triangulation [switches](https://www.cs.cmu.edu/~quake/triangle.switch.html)
456    pub fn set_switches(&mut self, switches: &str) -> &mut Self {
457        self.switches = format!("z{}", switches);
458        self
459    }
460    /// Compute the Delaunay mesh and returns a `Delaunay` structure
461    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        //use TriangulateIO::*;
473        let mut delaunay: triangulateio = unsafe { std::mem::zeroed() };
474        //        println!("Delaunay triangulation with  switches: {}", self.switches);
475        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            //triangulate_io: vec![TriangulateIO::Points(points)],
530            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            //triangulate_io: vec![TriangulateIO::Points(points.to_owned())],
547            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/*
599impl TriPlot for Delaunay {
600    fn mesh<'a, D: DrawingBackend>(
601        &self,
602        x: &[f64],
603        y: &[f64],
604        color: [u8; 3],
605        chart: &mut ChartContext<'a, D, Cartesian2d<RangedCoordf64, RangedCoordf64>>,
606    ) -> &Self {
607        let color = RGBColor(color[0], color[1], color[2]);
608        self.triangle_iter()
609            .map(|t| t.iter().map(|&i| (x[i], y[i])).collect::<Vec<(f64, f64)>>())
610            .for_each(|v| {
611                chart
612                    .draw_series(LineSeries::new(
613                        v.iter().cycle().take(4).map(|(x, y)| (*x, *y)),
614                        &color,
615                    ))
616                    .unwrap();
617            });
618        self
619    }
620    fn map<'a, D: DrawingBackend>(
621        &self,
622        _x: &[f64],
623        _y: &[f64],
624        _z: &[f64],
625        _chart: &mut ChartContext<'a, D, Cartesian2d<RangedCoordf64, RangedCoordf64>>,
626    ) -> &Self {
627        self
628    }
629    fn heatmap(
630        &self,
631        _x: &[f64],
632        _y: &[f64],
633        _z: &[f64],
634        _range: Range<f64>,
635        _config: Option<Config>,
636    ) -> Result<(), Box<dyn Error>> {
637        Ok(())
638    }
639}
640 */
641
642#[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}