microcad_core/geo2d/
collection.rs1use derive_more::{Deref, DerefMut};
7use geo::{CoordsIter, HasDimensions, LineString, Polygon};
8use std::rc::Rc;
9
10use crate::{
11 geo2d::{FetchBounds2D, bounds::Bounds2D},
12 *,
13};
14
15#[derive(Debug, Clone, Default, Deref, DerefMut)]
17pub struct Geometries2D(Vec<Rc<Geometry2D>>);
18
19impl Geometries2D {
20 pub fn new(geometries: Vec<Geometry2D>) -> Self {
22 Self(geometries.into_iter().map(Rc::new).collect())
23 }
24
25 pub fn append(&mut self, mut geometries: Geometries2D) {
27 self.0.append(&mut geometries.0)
28 }
29
30 pub fn boolean_op(&self, resolution: &RenderResolution, op: &BooleanOp) -> geo2d::MultiPolygon {
32 let multi_polygon_list: Vec<_> = self
33 .0
34 .iter()
35 .filter_map(|geo| {
37 let multi_polygon = geo.render_to_multi_polygon(resolution);
38 if multi_polygon.is_empty() {
39 None
40 } else {
41 Some(multi_polygon)
42 }
43 })
44 .collect();
45
46 if multi_polygon_list.is_empty() {
47 return geo2d::MultiPolygon::empty();
48 }
49
50 multi_polygon_list[1..]
51 .iter()
52 .fold(multi_polygon_list[0].clone(), |acc, geo| {
53 use geo::BooleanOps;
54 acc.boolean_op(geo, op.into())
55 })
56 }
57
58 pub fn hull(&self, resolution: &RenderResolution) -> geo2d::Polygon {
60 let mut coords = self.iter().fold(Vec::new(), |mut coords, geo| {
61 match geo.as_ref() {
62 Geometry2D::LineString(line_string) => {
63 coords.append(&mut line_string.coords_iter().collect())
64 }
65 Geometry2D::MultiLineString(multi_line_string) => {
66 coords.append(&mut multi_line_string.coords_iter().collect())
67 }
68 Geometry2D::Polygon(polygon) => {
69 coords.append(&mut polygon.exterior_coords_iter().collect())
70 }
71 Geometry2D::MultiPolygon(multi_polygon) => {
72 coords.append(&mut multi_polygon.exterior_coords_iter().collect())
73 }
74 Geometry2D::Rect(rect) => {
75 let mut rect_corners: Vec<_> = rect.coords_iter().collect();
76 coords.append(&mut rect_corners)
77 }
78 Geometry2D::Circle(circle) => coords.append(
79 &mut circle
80 .clone()
81 .render_to_polygon(resolution)
82 .unwrap_or(Polygon::new(LineString(vec![]), vec![]))
83 .exterior_coords_iter()
84 .collect(),
85 ),
86 Geometry2D::Line(line) => {
87 coords.push(line.0.into());
88 coords.push(line.1.into());
89 }
90 Geometry2D::Collection(collection) => {
91 coords.append(&mut collection.hull(resolution).exterior_coords_iter().collect())
92 }
93 }
94 coords
95 });
96
97 geo2d::Polygon::new(
98 geo::algorithm::convex_hull::qhull::quick_hull(&mut coords),
99 vec![],
100 )
101 }
102}
103
104impl FromIterator<Rc<Geometry2D>> for Geometries2D {
105 fn from_iter<T: IntoIterator<Item = Rc<Geometry2D>>>(iter: T) -> Self {
106 Geometries2D(iter.into_iter().collect())
107 }
108}
109
110impl FetchBounds2D for Geometries2D {
111 fn fetch_bounds_2d(&self) -> Bounds2D {
112 self.0.iter().fold(Bounds2D::default(), |bounds, geometry| {
113 bounds.extend(geometry.fetch_bounds_2d())
114 })
115 }
116}
117
118impl Transformed2D for Geometries2D {
119 fn transformed_2d(&self, render_resolution: &RenderResolution, mat: &Mat3) -> Self {
120 Self(
121 self.iter()
122 .map(|geometry| Rc::new(geometry.transformed_2d(render_resolution, mat)))
123 .collect::<Vec<_>>(),
124 )
125 }
126}
127
128impl RenderToMultiPolygon for Geometries2D {
129 fn render_to_existing_multi_polygon(
130 &self,
131 resolution: &RenderResolution,
132 polygons: &mut geo2d::MultiPolygon,
133 ) {
134 self.iter().for_each(|geometry| {
135 geometry.render_to_existing_multi_polygon(resolution, polygons);
136 });
137 }
138}