microcad_core/geo2d/
collection.rs1use derive_more::{Deref, DerefMut};
7use geo::{CoordsIter, HasDimensions, MultiPolygon};
8use std::rc::Rc;
9
10use crate::{
11 geo2d::{CalcBounds2D, 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, op: &BooleanOp) -> geo2d::MultiPolygon {
32 let multi_polygon_list: Vec<_> = self
33 .0
34 .iter()
35 .filter_map(|geo| {
37 let multi_polygon = geo.to_multi_polygon();
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 to_multi_polygon(&self) -> MultiPolygon {
60 let mut polygons = Vec::new();
61 self.iter().for_each(|geo| {
62 polygons.append(&mut (**geo).clone().to_multi_polygon().0);
63 });
64
65 MultiPolygon::new(polygons)
66 }
67
68 pub fn hull(&self) -> geo2d::Polygon {
70 let mut coords = self.iter().fold(Vec::new(), |mut coords, geo| {
71 match geo.as_ref() {
72 Geometry2D::LineString(line_string) => {
73 coords.append(&mut line_string.coords_iter().collect())
74 }
75 Geometry2D::MultiLineString(multi_line_string) => {
76 coords.append(&mut multi_line_string.coords_iter().collect())
77 }
78 Geometry2D::Polygon(polygon) => {
79 coords.append(&mut polygon.exterior_coords_iter().collect())
80 }
81 Geometry2D::MultiPolygon(multi_polygon) => {
82 coords.append(&mut multi_polygon.exterior_coords_iter().collect())
83 }
84 Geometry2D::Rect(rect) => {
85 let mut rect_corners: Vec<_> = rect.coords_iter().collect();
86 coords.append(&mut rect_corners)
87 }
88 Geometry2D::Line(line) => {
89 coords.push(line.0.into());
90 coords.push(line.1.into());
91 }
92 Geometry2D::Collection(collection) => {
93 coords.append(&mut collection.hull().exterior_coords_iter().collect())
94 }
95 }
96 coords
97 });
98
99 geo2d::Polygon::new(
100 geo::algorithm::convex_hull::qhull::quick_hull(&mut coords),
101 vec![],
102 )
103 }
104}
105
106impl FromIterator<Rc<Geometry2D>> for Geometries2D {
107 fn from_iter<T: IntoIterator<Item = Rc<Geometry2D>>>(iter: T) -> Self {
108 Geometries2D(iter.into_iter().collect())
109 }
110}
111
112impl CalcBounds2D for Geometries2D {
113 fn calc_bounds_2d(&self) -> Bounds2D {
114 self.0.iter().fold(Bounds2D::default(), |bounds, geometry| {
115 bounds.extend(geometry.calc_bounds_2d())
116 })
117 }
118}
119
120impl Transformed2D for Geometries2D {
121 fn transformed_2d(&self, mat: &Mat3) -> Self {
122 Self(
123 self.iter()
124 .map(|geometry| Rc::new(geometry.transformed_2d(mat)))
125 .collect::<Vec<_>>(),
126 )
127 }
128}
129
130impl From<Geometries2D> for MultiPolygon {
131 fn from(geometries: Geometries2D) -> Self {
132 Self(
133 geometries
134 .iter()
135 .flat_map(|geo| {
136 let multi_polygon: MultiPolygon = geo.as_ref().clone().into();
137 multi_polygon.0
138 })
139 .collect(),
140 )
141 }
142}