polygon_offsetting/
polygon_offsetting.rs

1use std::collections::HashMap;
2
3#[derive(Default, Debug, Clone)]
4pub struct Offset {
5    pub contour: Vec<(f64, f64)>,
6    pub area: f64,
7    pub perimeter: f64,
8}
9
10#[derive(Default, Debug, Clone)]
11pub struct Polygon {
12    edges: Vec<Edge>,
13    vertices: HashMap<usize, Vertex>,
14    offset_margin: f64,
15}
16
17#[derive(Default, Debug, Copy, Clone)]
18struct Vertex {
19    x: f64,
20    y: f64,
21    is_intersect: bool
22}
23
24#[derive(Default, Debug, Copy, Clone)]
25struct Edge {
26    p1: usize,
27    p2: usize,
28    index: usize,
29    outward_normal: Vertex,
30}
31
32#[derive(Default, Clone, Debug)]
33struct Segment {
34    p1: (f64, f64),
35    p2: (f64, f64),
36}
37
38#[inline]
39fn compute_area(contours: &Vec<(f64, f64)>) -> f64 {
40    let mut a = 0.0;
41    if contours.len() == 0 { return 0.0 }
42    for i in 0..contours.len() - 1 {
43        a = a + (contours[i].0 * contours[i + 1].1) - (contours[i + 1].0 * contours[i].1);
44    }
45    (a * -0.5).abs()
46}
47
48fn compute_perimeter(contour2d: &Vec<(f64, f64)>) -> f64 {
49    let mut perimeter2d = 0.;
50    for i in 0..(contour2d.len() - 1) {
51        let p1 = &contour2d[i];
52        let p2 = &contour2d[i + 1];
53        perimeter2d += ((p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2)).sqrt();
54    }
55    perimeter2d
56}
57
58#[inline]
59fn get_dist(p1: (f64, f64), p2: (f64, f64)) -> f64 {
60    ((p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2)).sqrt()
61}
62
63#[inline]
64fn vector_sub(v1: (f64, f64), v2: (f64, f64)) -> (f64, f64) {
65    (v1.0 - v2.0, v1.1 - v2.1)
66}
67
68#[inline]
69fn vector_add(v1: (f64, f64), v2: (f64, f64)) -> (f64, f64) {
70    (v1.0 + v2.0, v1.1 + v2.1)
71}
72
73#[inline]
74fn reverse_segments(sgmts: &Vec<Segment>) -> Vec<Segment> {
75    let mut segments: Vec<Segment> = Vec::new();
76
77    for s in sgmts.iter().rev() {
78        segments.push( Segment {
79            p1: s.p2,
80            p2: s.p1,
81        });
82    }
83    segments
84}
85
86// ================================================================================= 
87
88impl Polygon {
89    fn append_arc(&self,  center: &Vertex, radius: f64, start_vertex: &Vertex,  end_vertex: &Vertex, tolerance: f64) -> Vec<Vertex> {        
90        // we compute start and end angles
91        let mut start_angle = (start_vertex.y - center.y).atan2(start_vertex.x - center.x);
92        let mut end_angle   = (end_vertex.y   - center.y).atan2(end_vertex.x   - center.x);
93        if start_angle < 0.  { start_angle = 2. * std::f64::consts::PI + start_angle } 
94        if end_angle   <= 0. { end_angle   = 2. * std::f64::consts::PI + end_angle } 
95
96        // we compute oriented angle
97        let mut angle : f64 = end_angle - start_angle;
98        if angle.abs() > std::f64::consts::PI { 
99            angle = -( angle / angle.abs()) *  (2. * std::f64::consts::PI - angle.abs());
100        }
101
102        // we compute number of segments to fit the tolerance
103        let n_angle = (1. - (4. * radius * tolerance - 2. * tolerance.powi(2) ) / (radius.powi(2)) ).acos();
104        if n_angle == 0.0 {
105            let vect = vector_add((start_vertex.x, start_vertex.y), vector_sub((end_vertex.x, end_vertex.y), (start_vertex.x, start_vertex.y)));
106            return vec![Vertex { x: vect.0, y: vect.1, is_intersect: false }]
107        }
108        let nseg: i64 = ((angle.abs() / n_angle.abs()).round() + 1.) as i64;
109
110        // we define the angular step
111        let angular_step = angle / nseg as f64;
112        
113        // we create the segments
114        let mut dots = Vec::new();
115        for i in 0..(nseg + 1) {
116            let theta = start_angle + angular_step * i as f64;
117            let x = radius * (theta).cos() + center.x;
118            let y = radius * (theta).sin() + center.y;
119            dots.push(Vertex {x: x, y: y, is_intersect: false});
120        }
121        dots
122    }
123
124    fn edges_intersection(&self, e1: &(Vertex, Vertex), e2: &(Vertex, Vertex), is_inters: bool) -> Option<Vertex> {
125        let den = (e2.1.y - e2.0.y) * (e1.1.x - e1.0.x) - (e2.1.x - e2.0.x) * (e1.1.y - e1.0.y);
126        if den > -0.0001 && den < 0.0001 {
127            return None;  // lines are parallel or conincident
128        }
129
130        let ua = ((e2.1.x - e2.0.x) * (e1.0.y - e2.0.y) - (e2.1.y - e2.0.y) * (e1.0.x - e2.0.x)) / den;
131        let ub = ((e1.1.x - e1.0.x) * (e1.0.y - e2.0.y) - (e1.1.y - e1.0.y) * (e1.0.x - e2.0.x)) / den;
132
133        if ua < 0.0000001 || ub < 0.0000001 || ua > 0.9999999 || ub > 0.9999999 {
134            return None;
135        }
136
137        let v_cross = Some(Vertex {
138            x: e1.0.x + ua * (e1.1.x - e1.0.x),
139            y: e1.0.y + ua * (e1.1.y - e1.0.y),
140            is_intersect: is_inters
141        });
142        v_cross
143    }
144
145    fn create_offset_edge(&self, p1: &Vertex, p2: &Vertex, dx: f64, dy: f64) -> (Vertex, Vertex) {
146        let mut v1: Vertex = Vertex::default();
147        let mut v2: Vertex = Vertex::default();
148        v1.x = p1.x + dx;
149        v1.y = p1.y + dy;
150        v2.x = p2.x + dx;
151        v2.y = p2.y + dy;
152        (v1, v2)
153    }
154
155    fn create_margin_polygon(&mut self, tolerance: f64) -> Polygon {
156        let mut offset_edges: Vec<(Vertex, Vertex)> = Vec::new();
157        let mut vertices: HashMap<usize, Vertex> = HashMap::new();
158        let mut index: usize = 0;
159
160        // compute and store offsets points
161        self.edges.iter().for_each(|edge| {
162            let p1 = self.vertices.get(&edge.p1).unwrap();
163            let p2 = self.vertices.get(&edge.p2).unwrap();
164            let dx = edge.outward_normal.x * self.offset_margin;
165            let dy = edge.outward_normal.y * self.offset_margin;
166            offset_edges.push(self.create_offset_edge(p1, p2, dx, dy));
167        });
168
169        
170        for i in 0..offset_edges.len() {
171            let this_edge = &offset_edges[i];
172            let prev_edge = &offset_edges[(i + offset_edges.len() - 1) % offset_edges.len()];
173            let vertex = self.edges_intersection(prev_edge, this_edge, false);
174
175            // Check if offsets segments intersect or not
176            if vertex.is_none() {
177                let arc_center = &self.vertices.get(&i).unwrap();
178                let arc_vertices = &self.append_arc(arc_center, self.offset_margin, &prev_edge.1, &this_edge.0, tolerance);
179
180                // if not, adding arc segments & boundaries matching our bounded edges
181
182                // adding index and vertex to our vertices container
183                for av in arc_vertices.iter() {
184                    vertices.insert(index, *av);
185                    index += 1;
186                }
187            } else {
188                vertices.insert(index, vertex.unwrap());
189                index += 1;
190            }
191        }
192
193        // Finally, recreate a polygon from vertices and boundaries
194        let margin_polygon = self.create_polygon(vertices, self.offset_margin, false);
195        margin_polygon
196    }
197
198    fn sort_by_squared_dist(&self, p1: &Vertex, cross: &Vec<usize>, poly: &Polygon) -> Vec<usize> {
199        let mut cross_coord: Vec<(usize, Vertex)> = Vec::new();
200        let mut vp: Vec<(f64, usize)> = Vec::new();
201        let mut crossp = Vec::new();
202
203        for c in cross {
204            cross_coord.push((*c, *poly.vertices.get(&c).unwrap()));
205        }
206
207        for i in 0..cross_coord.len() {
208            let dist = (p1.x - cross_coord[i].1.x).powi(2) + (p1.y - cross_coord[i].1.y).powi(2);
209            vp.push(( dist, cross_coord[i].0));
210        }
211
212        vp.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
213        vp.iter().for_each(|v| {
214            crossp.push(v.1);
215        });
216        crossp
217    }
218
219    fn detect_all_intersect(&mut self, margin_polygon: &mut Polygon) {
220        let mut poly: Polygon = Polygon::default();
221        let mut indices: HashMap<usize, Vec<usize>> = HashMap::new();
222        let mut vertices = margin_polygon.vertices.clone();
223        
224        let mut iteration = 0;
225        margin_polygon.edges.iter_mut().for_each(|edge| {
226            indices.insert(edge.index, Vec::new());
227            edge.index = iteration;
228            iteration += 1;
229        });
230
231        let mut max_vertices = 0;
232        margin_polygon.vertices.iter().for_each(|(id, _)| {
233            if id > &max_vertices { max_vertices = *id;}
234        });
235
236        // Store intersection points with index 
237        margin_polygon.edges.iter().for_each(|edge1| {
238            margin_polygon.edges.iter().for_each(|edge2| {
239                if edge1.index > edge2.index {
240                    let p1: Vertex = *margin_polygon.vertices.get(&edge1.p1).unwrap();
241                    let p2: Vertex = *margin_polygon.vertices.get(&edge1.p2).unwrap();
242                    let p3: Vertex = *margin_polygon.vertices.get(&edge2.p1).unwrap();
243                    let p4: Vertex = *margin_polygon.vertices.get(&edge2.p2).unwrap();
244                    let e1: (Vertex, Vertex) = (p1, p2);
245                    let e2: (Vertex, Vertex) = (p3, p4);
246
247                    let inters = self.edges_intersection(&e1, &e2, true);
248                    if !inters.is_none() {
249                        vertices.insert(max_vertices + 1, inters.unwrap());
250                        max_vertices += 1;
251                        
252                        match indices.get_mut(&edge1.index) {
253                            Some(v) => {
254                                if !v.contains(&max_vertices) {
255                                    v.push(max_vertices);
256                                }
257                            },
258                            _ => {}
259                        };
260                        match indices.get_mut(&edge2.index) {
261                            Some(v) => {
262                                if !v.contains(&max_vertices) {
263                                    v.push(max_vertices);
264                                }
265                            },
266                            _ => {}
267                        };
268                    }
269                }
270            });
271        });
272
273        // Split segments into 2 new segments from intersection point
274        margin_polygon.vertices = vertices.clone();
275        let mut new_poly: Vec<Edge> = Vec::new();
276        margin_polygon.edges.iter().for_each(|edge| {
277            let idx = edge.index;
278            let p1 = margin_polygon.vertices.get(&edge.p1).unwrap();
279
280            match indices.get(&idx) {
281                Some(cross) => {
282                    if cross.len() > 0 {
283                        let sorted_cross: Vec<usize> = self.sort_by_squared_dist(p1, &cross.to_vec(), &margin_polygon);
284                        let new_edge: Edge = Edge {
285                            p1: edge.p1,
286                            p2: sorted_cross[0],
287                            index: 0,
288                            outward_normal: edge.outward_normal
289                        };
290                        new_poly.push(new_edge);
291
292                        for i in 0..sorted_cross.len() - 1 {
293                            let new_edge: Edge = Edge {
294                                p1: sorted_cross[i],
295                                p2: sorted_cross[i + 1],
296                                index: 0,
297                                outward_normal: edge.outward_normal
298                            };
299                            new_poly.push(new_edge);
300                        }
301                        let new_edge: Edge = Edge {
302                            p1: sorted_cross[sorted_cross.len() - 1],
303                            p2: edge.p2,
304                            index: 0,
305                            outward_normal: edge.outward_normal
306                        };
307                        new_poly.push(new_edge);
308                    } else {
309                        new_poly.push(*edge);
310                    }
311                },
312                _ => {}
313            }
314        });
315        let mut iteration = 0;
316        new_poly.iter_mut().for_each(|edge| {
317            edge.index = iteration;
318            iteration += 1;
319        });
320        poly.edges = new_poly;
321        poly.vertices = vertices;
322        *margin_polygon = poly;
323    }
324
325    fn detect_regions(&self, polygon: &Polygon) -> Vec<Polygon> {
326        // each region is described by a polygon
327        let mut regions : Vec<Polygon> = Vec::new();
328
329        // remaining is a vect of the indices of the vertices
330        let mut remaining: Vec<usize> = Vec::new();
331
332        polygon.vertices.iter().for_each(|(id, _)| remaining.push(*id));
333
334        // we create a map: keys are the vertice index, values are the list of edges containing this vertice
335        let mut map : HashMap<usize, Vec<usize>> = HashMap::new();
336        polygon.vertices.iter().for_each(|(id, _)| { map.insert(*id, Vec::new()); });
337        polygon.edges.iter().for_each(|edge| {
338            match map.get_mut(&edge.p1) { Some(v) => {if !v.contains(&edge.index) {v.push(edge.index)}}, _ => {} };
339        });
340
341        // we follow the contour
342        while remaining.len() > 0 {
343            
344            let mut current_region: Polygon = Polygon {
345                edges: Vec::new(),
346                vertices: HashMap::new(),
347                offset_margin: 0.0,
348            };
349            
350            let mut idx = remaining[0];
351            let start_idx = idx;
352            let mut prev_edge_indice: usize = 0;
353
354            while idx != start_idx || current_region.vertices.len() == 0 {
355                match polygon.vertices.get(&idx) {
356                    Some(v) => {
357                        // we add the current point to the vertices of the current region
358                        current_region.vertices.insert(idx, *v);
359                        remaining.retain(|r| {
360                            *r != idx
361                        });
362
363                        match v.is_intersect {
364                            true => {
365                                let mut edges: Vec<usize> = map.get(&idx).unwrap().clone();
366                                edges.retain(|e| {
367                                    *e != prev_edge_indice + 1
368                                });
369                                let edge = edges[0];
370                                let new_edge = Edge { 
371                                    p1: idx, 
372                                    p2: polygon.edges[edge].p2,
373                                    outward_normal: polygon.edges[edge].outward_normal,
374                                    index: 0
375                                };
376                                current_region.edges.push(new_edge);
377                                idx = polygon.edges[edge].p2;
378                                prev_edge_indice = edge;
379                            },
380                            false => {
381                                let edge = map.get(&idx).unwrap()[0];
382                                let new_edge = Edge { 
383                                    p1: idx, 
384                                    p2: polygon.edges[edge].p2,
385                                    outward_normal: polygon.edges[edge].outward_normal,
386                                    index: 0
387                                };
388                                current_region.edges.push(new_edge);
389                                idx = polygon.edges[edge].p2;
390                                prev_edge_indice = edge;
391                            }
392                        }
393                    },
394                    _ => panic!("Regions, vertice not found")
395                }
396            }
397            let mut iteration = 0;
398            current_region.edges.iter_mut().for_each(|e| {
399                e.index = iteration;
400                iteration += 1;
401            });
402            regions.push(current_region)
403        }
404        regions
405    }
406
407    fn outward_edge_normal(&self, v1: &Vertex, v2: &Vertex) -> Vertex {
408        let dx = v2.x - v1.x;
409        let dy = v2.y - v1.y;
410        let edge_length = (dx * dx + dy * dy).sqrt();
411        Vertex {
412            is_intersect: false,
413            x:  dy / edge_length, 
414            y: -dx / edge_length
415        }
416    }
417
418    fn contour_to_vertices(contour: Vec<(f64, f64)>) -> HashMap<usize, Vertex> {
419         let mut vtxs: HashMap<usize, Vertex> = HashMap::new();
420
421         for i in 0..contour.len() - 1 {
422             let mut vertex: Vertex = Vertex::default();
423             vertex.is_intersect = false;
424             vertex.x = contour[i].0;
425             vertex.y = contour[i].1;
426             vtxs.insert(i, vertex);
427         }
428         vtxs
429    }
430
431    fn get_polygon_area<'a>(&self, poly: &'a mut Polygon) -> (f64, &'a Polygon) {
432        let mut contours: Vec<(f64, f64)> = Vec::new();
433
434        let edges = &mut poly.edges;
435        edges.sort_by_key(|k| k.index);
436
437        for i in 0..edges.len() {
438            let v = poly.vertices.get(&edges[i].p1).unwrap();
439            contours.push((v.x, v.y));
440        }
441
442        let mut a = 0.0;
443        for i in 0..contours.len() - 1 {
444            a = a + (contours[i].0 * contours[i + 1].1) - (contours[i + 1].0 * contours[i].1);
445        }
446        a = a + (contours[contours.len() - 1].0 * contours[0].1) - (contours[0].0 * contours[contours.len() - 1].1);
447        ((a * -0.5).abs(), poly)
448    }
449
450    // convert tuples to a Vec of Segment
451    fn tuples_to_segments(contour1: &Vec<(f64, f64)>) -> Vec<Segment> {
452        let mut segments: Vec<Segment> = Vec::new();
453
454        for i in 1..contour1.len() {
455            let mut sgmt: Segment = Segment::default();
456            sgmt.p1.0 = contour1[i - 1].0;
457            sgmt.p1.1 = contour1[i - 1].1;
458            if i == contour1.len() { 
459                sgmt.p2.0 = contour1[0].0;
460                sgmt.p2.1 = contour1[0].1;
461                segments.push(sgmt);
462                break ;
463            }
464            sgmt.p2.0 = contour1[i].0;
465            sgmt.p2.1 = contour1[i].1;
466            segments.push(sgmt);
467        }
468        segments
469    }
470
471    fn create_polygon(
472        &mut self,
473        vertices: HashMap<usize, Vertex>,
474        offset_size: f64,
475        is_initial_polygon: bool
476    ) -> Polygon {
477        let mut polygon = Polygon::default();
478        let mut edges: Vec<Edge> = Vec::new();
479        polygon.vertices = vertices;
480        polygon.offset_margin = offset_size;
481
482        // Create edges segments with their index and normal
483        for i in 0..polygon.vertices.len() {
484            let mut edge = Edge {
485                p1: i, 
486                p2: (i + 1) % polygon.vertices.len(),
487                index: i,
488                outward_normal: Vertex::default(),
489            };
490            if edge.p1 == edge.p2 {
491                continue ;
492            }
493            if is_initial_polygon {
494                edge.outward_normal = self.outward_edge_normal(
495                    polygon.vertices.get(&edge.p1).unwrap(),
496                    polygon.vertices.get(&edge.p2).unwrap()
497                );
498            }
499            edges.push(edge);
500        }
501
502        polygon.edges = edges;
503        polygon
504    }
505
506    pub fn new(
507        initial_contour: &Vec<(f64, f64)>,
508        offset_size: f64,
509    ) -> Result <Polygon, Box<dyn std::error::Error>> {
510
511        let mut new_segments: Vec<Segment> = Polygon::tuples_to_segments(&initial_contour);
512        // remove contiguous points with same coords
513        new_segments.retain(|s| { get_dist(s.p1, s.p2) != 0. });
514
515        // check if our polygon is closed
516        if initial_contour[0] != initial_contour[initial_contour.len() - 1] {
517            return Err(format!("Unclosed polygon").into())
518        }
519
520        // check the direction of our polygon
521        let value = new_segments.iter().fold(
522            0.,
523            |acc, seg|
524            acc + (seg.p2.0 - seg.p1.0) * (seg.p2.1 + seg.p1.1)
525        );
526
527        // reverse it if clockwise
528        if value > 0. { new_segments = reverse_segments(&new_segments); }
529
530
531        let mut points: Vec<(f64, f64)> = Vec::new();
532        for s in new_segments.iter() {
533            points.push(s.p1);
534        }
535        points.push(points[0]);
536
537        let mut initial_polygon: Polygon = Polygon::default();
538        let vertices: HashMap<usize, Vertex> = Polygon::contour_to_vertices(points.to_vec());
539        Ok(initial_polygon.create_polygon(vertices, offset_size, true))
540    }
541
542    pub fn offsetting(&mut self, tolerance: f64) -> Result <Offset, Box<dyn std::error::Error>> {
543        let mut offsets: Offset = Offset::default();
544        let mut areas: Vec<(f64, &Polygon)> = Vec::new();
545
546        if tolerance <= 0.0 {
547            return Err("The tolerance can't be below or egal to 0".into())
548        }
549
550        // Offset 0.0, only return an Offset struct without computing its margin
551        if self.offset_margin == 0.0 {
552            let mut points: Vec<(f64, f64)> = Vec::new();
553            self.edges.iter().for_each(|e| {
554                let p1 = self.vertices.get(&e.p1).unwrap();
555                points.push((p1.x, p1.y));
556            });
557            points.push(points[0]);
558
559            let offset = Offset {
560                area: compute_area(&points),
561                perimeter: compute_perimeter(&points),
562                contour: points,
563            };
564            return Ok(offset)
565        }
566
567        // Now we can compute our margin polygon
568        let mut margin_polygon: Polygon = self.create_margin_polygon(tolerance);
569        
570        // Check for all intersections
571        self.detect_all_intersect(&mut margin_polygon);
572
573        // Intersections for new regions
574        let mut regions = self.detect_regions(&margin_polygon);
575        regions.iter_mut().for_each(|r| {
576            areas.push(self.get_polygon_area(&mut *r));
577        });
578        areas.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
579        
580        // And keep only the biggest
581        let offset_polygon: &Polygon = areas[areas.len() - 1].1;
582        offsets.area = areas[areas.len() - 1].0;
583              
584        offset_polygon.edges.iter().for_each(|edge| {
585            offsets.contour.push((
586                offset_polygon.vertices.get(&edge.p1).unwrap().x,
587                offset_polygon.vertices.get(&edge.p1).unwrap().y
588            ));
589        });
590        offsets.contour.push(offsets.contour[0]); 
591        offsets.perimeter = compute_perimeter(&offsets.contour);
592
593        Ok(offsets)
594    }
595}
596
597
598
599
600
601