1use super::*;
2use geo_clipper::Clipper;
3use geo_types::CoordFloat;
4
5#[derive(Debug, Copy, Clone, PartialEq)]
7pub enum OffsetError {
8 EdgeError(EdgeError),
10}
11
12pub const DEFAULT_ARC_SEGMENTS: u32 = 5;
14
15pub trait Offset<F: CoordFloat> {
16 fn offset(&self, distance: F) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
17 self.offset_with_arc_segments(distance, DEFAULT_ARC_SEGMENTS)
18 }
19
20 fn offset_with_arc_segments(
21 &self,
22 distance: F,
23 arc_segments: u32,
24 ) -> Result<geo_types::MultiPolygon<F>, OffsetError>;
25}
26
27impl<F: CoordFloat> Offset<F> for geo_types::GeometryCollection<F> {
28 fn offset_with_arc_segments(
29 &self,
30 distance: F,
31 arc_segments: u32,
32 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
33 let mut geometry_collection_with_offset = geo_types::MultiPolygon::<F>(Vec::new());
34 for geometry in self.0.iter() {
35 let geometry_with_offset = geometry.offset_with_arc_segments(distance, arc_segments)?;
36 geometry_collection_with_offset = geometry_collection_with_offset
37 .union(&geometry_with_offset, F::from(1000.0).unwrap());
38 }
39 Ok(geometry_collection_with_offset)
40 }
41}
42
43impl<F: CoordFloat> Offset<F> for geo_types::Geometry<F> {
44 fn offset_with_arc_segments(
45 &self,
46 distance: F,
47 arc_segments: u32,
48 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
49 match self {
50 geo_types::Geometry::Point(point) => {
51 point.offset_with_arc_segments(distance, arc_segments)
52 }
53 geo_types::Geometry::Line(line) => {
54 line.offset_with_arc_segments(distance, arc_segments)
55 }
56 geo_types::Geometry::LineString(line_tring) => {
57 line_tring.offset_with_arc_segments(distance, arc_segments)
58 }
59 geo_types::Geometry::Triangle(triangle) => triangle
60 .to_polygon()
61 .offset_with_arc_segments(distance, arc_segments),
62 geo_types::Geometry::Rect(rect) => rect
63 .to_polygon()
64 .offset_with_arc_segments(distance, arc_segments),
65 geo_types::Geometry::Polygon(polygon) => {
66 polygon.offset_with_arc_segments(distance, arc_segments)
67 }
68 geo_types::Geometry::MultiPoint(multi_point) => {
69 multi_point.offset_with_arc_segments(distance, arc_segments)
70 }
71 geo_types::Geometry::MultiLineString(multi_line_string) => {
72 multi_line_string.offset_with_arc_segments(distance, arc_segments)
73 }
74 geo_types::Geometry::MultiPolygon(multi_polygon) => {
75 multi_polygon.offset_with_arc_segments(distance, arc_segments)
76 }
77 geo_types::Geometry::GeometryCollection(geometry_collection) => {
78 geometry_collection.offset_with_arc_segments(distance, arc_segments)
79 }
80 }
81 }
82}
83
84impl<F: CoordFloat> Offset<F> for geo_types::MultiPolygon<F> {
85 fn offset_with_arc_segments(
86 &self,
87 distance: F,
88 arc_segments: u32,
89 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
90 let mut polygons = geo_types::MultiPolygon::<F>(Vec::new());
91 for polygon in self.0.iter() {
92 let polygon_with_offset = polygon.offset_with_arc_segments(distance, arc_segments)?;
93 polygons = polygons.union(&polygon_with_offset, F::from(1000.0).unwrap());
94 }
95 Ok(polygons)
96 }
97}
98
99impl<F: CoordFloat> Offset<F> for geo_types::Polygon<F> {
100 fn offset_with_arc_segments(
101 &self,
102 distance: F,
103 arc_segments: u32,
104 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
105 let exterior_with_offset = self
106 .exterior()
107 .offset_with_arc_segments(distance.abs(), arc_segments)?;
108 let interiors_with_offset = geo_types::MultiLineString::<F>(self.interiors().to_vec())
109 .offset_with_arc_segments(distance.abs(), arc_segments)?;
110
111 Ok(if distance.is_sign_positive() {
112 self.union(&exterior_with_offset, F::from(1000.0).unwrap())
113 .union(&interiors_with_offset, F::from(1000.0).unwrap())
114 } else {
115 self.difference(&exterior_with_offset, F::from(1000.0).unwrap())
116 .difference(&interiors_with_offset, F::from(1000.0).unwrap())
117 })
118 }
119}
120
121impl<F: CoordFloat> Offset<F> for geo_types::MultiLineString<F> {
122 fn offset_with_arc_segments(
123 &self,
124 distance: F,
125 arc_segments: u32,
126 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
127 if distance < F::zero() {
128 return Ok(geo_types::MultiPolygon(Vec::new()));
129 }
130
131 let mut multi_line_string_with_offset = geo_types::MultiPolygon::<F>(Vec::new());
132 for line_string in self.0.iter() {
133 let line_string_with_offset =
134 line_string.offset_with_arc_segments(distance, arc_segments)?;
135 multi_line_string_with_offset = multi_line_string_with_offset
136 .union(&line_string_with_offset, F::from(1000.0).unwrap());
137 }
138 Ok(multi_line_string_with_offset)
139 }
140}
141
142impl<F: CoordFloat> Offset<F> for geo_types::LineString<F> {
143 fn offset_with_arc_segments(
144 &self,
145 distance: F,
146 arc_segments: u32,
147 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
148 if distance < F::zero() {
149 return Ok(geo_types::MultiPolygon(Vec::new()));
150 }
151
152 let mut line_string_with_offset = geo_types::MultiPolygon::<F>(Vec::new());
153 for line in self.lines() {
154 let line_with_offset = line.offset_with_arc_segments(distance, arc_segments)?;
155 line_string_with_offset =
156 line_string_with_offset.union(&line_with_offset, F::from(1000.0).unwrap());
157 }
158
159 let line_string_with_offset = line_string_with_offset.0.iter().skip(1).fold(
160 geo_types::MultiPolygon::<F>(
161 line_string_with_offset
162 .0
163 .get(0)
164 .map(|polygon| vec![polygon.clone()])
165 .unwrap_or_default(),
166 ),
167 |result, hole| result.difference(hole, F::from(1000.0).unwrap()),
168 );
169
170 Ok(line_string_with_offset)
171 }
172}
173
174impl<F: CoordFloat> Offset<F> for geo_types::Line<F> {
175 fn offset_with_arc_segments(
176 &self,
177 distance: F,
178 arc_segments: u32,
179 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
180 if distance < F::zero() {
181 return Ok(geo_types::MultiPolygon(Vec::new()));
182 }
183
184 let v1 = &self.start;
185 let v2 = &self.end;
186 let e1 = Edge::new(v1, v2);
187
188 if let (Ok(in_normal), Ok(out_normal)) = (e1.inwards_normal(), e1.outwards_normal()) {
189 let offsets = [
190 e1.with_offset(in_normal.x * distance, in_normal.y * distance),
191 e1.inverse_with_offset(out_normal.x * distance, out_normal.y * distance),
192 ];
193
194 let len = 2;
195 let mut vertices = Vec::new();
196
197 for i in 0..len {
198 let current_edge = offsets.get(i).unwrap();
199 let prev_edge = offsets.get((i + len + 1) % len).unwrap();
200 create_arc(
201 &mut vertices,
202 if i == 0 { v1 } else { v2 },
203 distance,
204 &prev_edge.next,
205 ¤t_edge.current,
206 arc_segments,
207 true,
208 );
209 }
210
211 Ok(geo_types::MultiPolygon(vec![geo_types::Polygon::new(
212 geo_types::LineString(vertices),
213 vec![],
214 )]))
215 } else {
216 geo_types::Point::from(self.start).offset_with_arc_segments(distance, arc_segments)
217 }
218 }
219}
220
221impl<F: CoordFloat> Offset<F> for geo_types::MultiPoint<F> {
222 fn offset_with_arc_segments(
223 &self,
224 distance: F,
225 arc_segments: u32,
226 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
227 if distance < F::zero() {
228 return Ok(geo_types::MultiPolygon(Vec::new()));
229 }
230
231 let mut multi_point_with_offset = geo_types::MultiPolygon::<F>(Vec::new());
232 for point in self.0.iter() {
233 let point_with_offset = point.offset_with_arc_segments(distance, arc_segments)?;
234 multi_point_with_offset =
235 multi_point_with_offset.union(&point_with_offset, F::from(1000.0).unwrap());
236 }
237 Ok(multi_point_with_offset)
238 }
239}
240
241impl<F: CoordFloat> Offset<F> for geo_types::Point<F> {
242 fn offset_with_arc_segments(
243 &self,
244 distance: F,
245 arc_segments: u32,
246 ) -> Result<geo_types::MultiPolygon<F>, OffsetError> {
247 if distance < F::zero() {
248 return Ok(geo_types::MultiPolygon(Vec::new()));
249 }
250
251 let mut angle = F::zero();
252
253 let vertice_count = match arc_segments * 2 {
254 count if count % 2 == 0 => count + 1,
255 count => count,
256 };
257
258 let contour = (0..vertice_count)
259 .map(|_| {
260 angle =
261 angle + F::from(2.0 * std::f64::consts::PI / f64::from(vertice_count)).unwrap(); geo_types::Coord::from((
264 self.x() + (distance * angle.cos()),
265 self.y() + (distance * angle.sin()),
266 ))
267 })
268 .collect();
269
270 Ok(geo_types::MultiPolygon(vec![geo_types::Polygon::new(
271 contour,
272 Vec::new(),
273 )]))
274 }
275}
276
277fn create_arc<F: CoordFloat>(
278 vertices: &mut Vec<geo_types::Coord<F>>,
279 center: &geo_types::Coord<F>,
280 radius: F,
281 start_vertex: &geo_types::Coord<F>,
282 end_vertex: &geo_types::Coord<F>,
283 segment_count: u32,
284 outwards: bool,
285) {
286 let pi2 = F::from(std::f64::consts::PI * 2.0).unwrap();
287
288 let start_angle = (start_vertex.y - center.y).atan2(start_vertex.x - center.x);
289 let start_angle = if start_angle.is_sign_negative() {
290 start_angle + pi2
291 } else {
292 start_angle
293 };
294
295 let end_angle = (end_vertex.y - center.y).atan2(end_vertex.x - center.x);
296 let end_angle = if end_angle.is_sign_negative() {
297 end_angle + pi2
298 } else {
299 end_angle
300 };
301
302 let segment_count = if segment_count % 2 == 0 {
303 segment_count - 1
304 } else {
305 segment_count
306 };
307
308 let angle = if start_angle > end_angle {
309 start_angle - end_angle
310 } else {
311 start_angle + pi2 - end_angle
312 };
313
314 let segment_angle =
315 if outwards { -angle } else { pi2 - angle } / F::from(segment_count).unwrap();
316
317 vertices.push(*start_vertex);
318 for i in 1..segment_count {
319 let angle = start_angle + segment_angle * F::from(i).unwrap();
320 vertices.push(geo_types::Coord::from((
321 center.x + angle.cos() * radius,
322 center.y + angle.sin() * radius,
323 )));
324 }
325 vertices.push(*end_vertex);
326}