1use crate::algorithm::{AffineOps, AffineTransform, BoundingRect, Centroid};
2use crate::geometry::*;
3use crate::CoordFloat;
4
5pub trait Rotate<T: CoordFloat> {
16 #[must_use]
44 fn rotate_around_centroid(&self, degrees: T) -> Self;
45
46 fn rotate_around_centroid_mut(&mut self, degrees: T);
48
49 #[must_use]
55 fn rotate_around_center(&self, degrees: T) -> Self;
56
57 fn rotate_around_center_mut(&mut self, degrees: T);
59
60 #[must_use]
88 fn rotate_around_point(&self, degrees: T, point: Point<T>) -> Self;
89
90 fn rotate_around_point_mut(&mut self, degrees: T, point: Point<T>);
92}
93
94#[doc(hidden)]
95#[deprecated(since = "0.23.0", note = "Use `Rotate::rotate_around_point` instead.")]
96pub trait RotatePoint<T: CoordFloat> {
97 fn rotate_around_point(&self, degrees: T, point: Point<T>) -> Self;
98}
99
100#[doc(hidden)]
101#[allow(deprecated)]
102impl<T, G> RotatePoint<T> for G
103where
104 T: CoordFloat,
105 G: Rotate<T>,
106{
107 fn rotate_around_point(&self, degrees: T, point: Point<T>) -> Self {
108 Rotate::rotate_around_point(self, degrees, point)
109 }
110}
111
112impl<G, IP, IR, T> Rotate<T> for G
113where
114 T: CoordFloat,
115 IP: Into<Option<Point<T>>>,
116 IR: Into<Option<Rect<T>>>,
117 G: Clone + Centroid<Output = IP> + BoundingRect<T, Output = IR> + AffineOps<T>,
118{
119 fn rotate_around_centroid(&self, degrees: T) -> Self {
120 let point = match self.centroid().into() {
121 Some(coord) => coord,
122 None => return self.clone(),
124 };
125 Rotate::rotate_around_point(self, degrees, point)
126 }
127
128 fn rotate_around_centroid_mut(&mut self, degrees: T) {
129 let point = match self.centroid().into() {
130 Some(coord) => coord,
131 None => return,
133 };
134 self.rotate_around_point_mut(degrees, point)
135 }
136
137 fn rotate_around_center(&self, degrees: T) -> Self {
138 let point = match self.bounding_rect().into() {
139 Some(rect) => Point(rect.center()),
140 None => return self.clone(),
142 };
143 Rotate::rotate_around_point(self, degrees, point)
144 }
145
146 fn rotate_around_center_mut(&mut self, degrees: T) {
147 let point = match self.bounding_rect().into() {
148 Some(rect) => Point(rect.center()),
149 None => return,
151 };
152 self.rotate_around_point_mut(degrees, point)
153 }
154
155 fn rotate_around_point(&self, degrees: T, point: Point<T>) -> Self {
156 let transform = AffineTransform::rotate(degrees, point);
157 self.affine_transform(&transform)
158 }
159
160 fn rotate_around_point_mut(&mut self, degrees: T, point: Point<T>) {
161 let transform = AffineTransform::rotate(degrees, point);
162 self.affine_transform_mut(&transform)
163 }
164}
165
166#[cfg(test)]
167mod test {
168 use crate::algorithm::Rotate;
169 use crate::geometry::*;
170 use crate::{line_string, point, polygon};
171 use approx::assert_relative_eq;
172
173 #[test]
174 fn test_rotate_around_point() {
175 let p = point!(x: 1.0, y: 5.0);
176 let rotated = p.rotate_around_centroid(30.0);
177 assert_eq!(rotated, Point::new(1.0, 5.0));
179 }
180
181 #[test]
182 fn test_rotate_points() {
183 let point = point!(x: 1.0, y: 5.0);
184 let rotated_center = point.rotate_around_center(30.);
185 let rotated_centroid = point.rotate_around_centroid(30.);
186
187 assert_eq!(point, rotated_center);
190 assert_eq!(point, rotated_centroid);
191 }
192
193 #[test]
194 fn test_rotate_multipoints() {
195 let multi_points = MultiPoint::new(vec![
196 point!(x: 0., y: 0.),
197 point!(x: 1., y: 1.),
198 point!(x: 2., y: 1.),
199 ]);
200
201 let expected_for_centroid = MultiPoint::new(vec![
203 point!(x: 0.7642977396044841, y: -0.5118446353109125),
204 point!(x: 0.7642977396044842, y: 0.9023689270621824),
205 point!(x: 1.471404520791032, y: 1.60947570824873),
206 ]);
207 assert_relative_eq!(
208 multi_points.rotate_around_centroid(45.),
209 expected_for_centroid
210 );
211
212 let expected_for_center = MultiPoint::new(vec![
214 point!(x: 0.6464466094067262, y: -0.5606601717798212),
215 point!(x: 0.6464466094067263, y: 0.8535533905932737),
216 point!(x: 1.353553390593274, y: 1.560660171779821),
217 ]);
218 assert_relative_eq!(multi_points.rotate_around_center(45.), expected_for_center);
219 }
220
221 #[test]
222 fn test_rotate_linestring() {
223 let linestring = line_string![
224 (x: 0.0, y: 0.0),
225 (x: 5.0, y: 5.0),
226 (x: 5.0, y: 10.0)
227 ];
228
229 let rotated_around_centroid = linestring.rotate_around_centroid(-45.0);
231 assert_relative_eq!(
232 rotated_around_centroid,
233 line_string![
234 (x: -2.196699141100894, y: 3.838834764831844),
235 (x: 4.874368670764582, y: 3.838834764831844),
236 (x: 8.40990257669732, y: 7.374368670764582)
237 ]
238 );
239
240 let rotated_around_center = linestring.rotate_around_center(-45.0);
242 assert_relative_eq!(
243 rotated_around_center,
244 line_string![
245 (x: -2.803300858899106, y: 3.232233047033631),
246 (x: 4.267766952966369, y: 3.232233047033632),
247 (x: 7.803300858899107, y: 6.767766952966369)
248 ],
249 epsilon = 1e-12
250 );
251 }
252 #[test]
253 fn test_rotate_polygon() {
254 let poly1 = polygon![
255 (x: 5., y: 1.),
256 (x: 4., y: 2.),
257 (x: 4., y: 3.),
258 (x: 5., y: 4.),
259 (x: 6., y: 4.),
260 (x: 7., y: 3.),
261 (x: 7., y: 2.),
262 (x: 6., y: 1.),
263 (x: 5., y: 1.)
264 ];
265 let rotated = poly1.rotate_around_centroid(-15.0);
266 let correct = polygon![
267 (x: 4.6288085192016855, y: 1.1805207831176578),
268 (x: 3.921701738015137, y: 2.405265654509247),
269 (x: 4.180520783117659, y: 3.3711914807983154),
270 (x: 5.405265654509247, y: 4.0782982619848624),
271 (x: 6.371191480798316, y: 3.819479216882342),
272 (x: 7.0782982619848624, y: 2.594734345490753),
273 (x: 6.819479216882343, y: 1.6288085192016848),
274 (x: 5.594734345490753, y: 0.9217017380151372),
275 (x: 4.6288085192016855, y: 1.1805207831176578)
276 ];
277 assert_eq!(rotated, correct);
279 }
280 #[test]
281 fn test_rotate_polygon_holes() {
282 let poly1 = polygon![
283 exterior: [
284 (x: 5.0, y: 1.0),
285 (x: 4.0, y: 2.0),
286 (x: 4.0, y: 3.0),
287 (x: 5.0, y: 4.0),
288 (x: 6.0, y: 4.0),
289 (x: 7.0, y: 3.0),
290 (x: 7.0, y: 2.0),
291 (x: 6.0, y: 1.0),
292 (x: 5.0, y: 1.0)
293 ],
294 interiors: [
295 [
296 (x: 5.0, y: 1.3),
297 (x: 5.5, y: 2.0),
298 (x: 6.0, y: 1.3),
299 (x: 5.0, y: 1.3),
300 ],
301 [
302 (x: 5., y: 2.3),
303 (x: 5.5, y: 3.0),
304 (x: 6., y: 2.3),
305 (x: 5., y: 2.3),
306 ],
307 ],
308 ];
309
310 let center_expected = polygon![
312 exterior: [
313 (x: 4.628808519201685, y: 1.180520783117658),
314 (x: 3.921701738015137, y: 2.405265654509247),
315 (x: 4.180520783117659, y: 3.371191480798315),
316 (x: 5.405265654509247, y: 4.078298261984862),
317 (x: 6.371191480798316, y: 3.819479216882342),
318 (x: 7.078298261984862, y: 2.594734345490753),
319 (x: 6.819479216882343, y: 1.628808519201685),
320 (x: 5.594734345490753, y: 0.9217017380151372),
321 (x: 4.628808519201685, y: 1.180520783117658),
322 ],
323 interiors: [
324 [
325 (x: 4.706454232732442, y: 1.470298531004379),
326 (x: 5.37059047744874, y: 2.017037086855466),
327 (x: 5.67238005902151, y: 1.211479485901858),
328 (x: 4.706454232732442, y: 1.470298531004379),
329 ],
330 [
331 (x: 4.965273277834962, y: 2.436224357293447),
332 (x: 5.62940952255126, y: 2.982962913144534),
333 (x: 5.931199104124032, y: 2.177405312190926),
334 (x: 4.965273277834962, y: 2.436224357293447),
335 ],
336 ],
337 ];
338
339 let rotated_around_center = poly1.rotate_around_center(-15.);
340
341 assert_relative_eq!(rotated_around_center, center_expected, epsilon = 1e-12);
342
343 let centroid_expected = polygon![
345 exterior: [
346 (x: 4.615388272418591, y: 1.182287592124891),
347 (x: 3.908281491232044, y: 2.40703246351648),
348 (x: 4.167100536334565, y: 3.372958289805549),
349 (x: 5.391845407726153, y: 4.080065070992097),
350 (x: 6.357771234015222, y: 3.821246025889576),
351 (x: 7.064878015201769, y: 2.596501154497987),
352 (x: 6.806058970099248, y: 1.630575328208918),
353 (x: 5.58131409870766, y: 0.9234685470223708),
354 (x: 4.615388272418591, y: 1.182287592124891),
355 ],
356 interiors: [
357 [
358 (x: 4.693033985949348, y: 1.472065340011612),
359 (x: 5.357170230665646, y: 2.0188038958627),
360 (x: 5.658959812238415, y: 1.213246294909091),
361 (x: 4.693033985949348, y: 1.472065340011612),
362 ],
363 [
364 (x: 4.951853031051868, y: 2.43799116630068),
365 (x: 5.615989275768166, y: 2.984729722151768),
366 (x: 5.917778857340937, y: 2.179172121198159),
367 (x: 4.951853031051868, y: 2.43799116630068),
368 ],
369 ],
370 ];
371 let rotated_around_centroid = poly1.rotate_around_centroid(-15.);
372 assert_relative_eq!(rotated_around_centroid, centroid_expected, epsilon = 1e-12);
373 }
374 #[test]
375 fn test_rotate_around_point_arbitrary() {
376 let p = Point::new(5.0, 10.0);
377 let rotated = p.rotate_around_point(-45., Point::new(10., 34.));
378 assert_eq!(rotated, Point::new(-10.506096654409877, 20.564971157455595));
379 }
380 #[test]
381 fn test_rotate_line() {
382 let line0 = Line::from([(0., 0.), (0., 2.)]);
383 let line1 = Line::from([(1., 1.), (-1., 1.)]);
384 assert_relative_eq!(line0.rotate_around_centroid(90.0), line1);
385 assert_relative_eq!(line0.rotate_around_center(90.0), line1);
386 }
387
388 #[test]
389 fn test_rotate_multi_line_string() {
390 let ls1 = line_string![
391 (x: 0., y: 0.),
392 (x: 1., y: 1.),
393 (x: 4., y: 1.),
394 ];
395 let ls2 = line_string![
396 (x: 10., y: 10.),
397 (x: 20., y: 20.),
398 (x: 40., y: 20.)
399 ];
400 let multi_line_string: MultiLineString = MultiLineString::new(vec![ls1, ls2]);
401
402 let expected_around_centroid = MultiLineString::new(vec![
404 line_string![
405 (x: -5.062519283392216, y: 19.72288595632566),
406 (x: -3.648305721019121, y: 19.72288595632566),
407 (x: -1.526985377459479, y: 17.60156561276602)
408 ],
409 line_string![
410 (x: 9.079616340338735, y: 19.72288595632566),
411 (x: 23.22175196406969, y: 19.72288595632566),
412 (x: 37.36388758780063, y: 5.580750332594715)
413 ],
414 ]);
415 assert_relative_eq!(
416 multi_line_string.rotate_around_centroid(-45.),
417 expected_around_centroid,
418 epsilon = 1e-12
419 );
420
421 let expected_around_center: MultiLineString = MultiLineString::new(vec![
423 line_string![
424 (x: -1.213203435596426, y: 17.07106781186548),
425 (x: 0.2010101267766693, y: 17.07106781186548),
426 (x: 2.322330470336312, y: 14.94974746830583),
427 ],
428 line_string![
429 (x: 12.92893218813452, y: 17.07106781186548),
430 (x: 27.07106781186548, y: 17.07106781186548),
431 (x: 41.21320343559643, y: 2.928932188134528),
432
433 ],
434 ]);
435 assert_relative_eq!(
436 multi_line_string.rotate_around_center(-45.),
437 expected_around_center,
438 epsilon = 1e-12
439 );
440 }
441
442 #[test]
443 fn test_rotate_line_around_point() {
444 let line0 = Line::new(Point::new(0., 0.), Point::new(0., 2.));
445 let line1 = Line::new(Point::new(0., 0.), Point::new(-2., 0.));
446 assert_relative_eq!(line0.rotate_around_point(90., Point::new(0., 0.)), line1);
447 }
448
449 #[test]
450 fn test_rotate_multipolygon_around_centroid() {
451 let multipolygon: MultiPolygon = vec![
452 polygon![
453 (x: 0., y: 0.),
454 (x: 10., y: 0.),
455 (x: 10., y: 10.),
456 (x: 0., y: 10.),
457 (x: 0., y: 0.),
458 ],
459 polygon![
460 (x: 0., y: 0.),
461 (x: -10., y: 0.),
462 (x: -10., y: -10.),
463 (x: 0., y: -10.),
464 (x: 0., y: 0.),
465 ],
466 ]
467 .into();
468
469 let expected_centroid: MultiPolygon = vec![
470 polygon![
471 (x: 0., y: 0.),
472 (x: 7.0710678118654755, y: 7.071067811865475),
473 (x: 0., y: 14.142135623730951),
474 (x: -7.071067811865475, y: 7.0710678118654755),
475 (x: 0., y: 0.),
476 ],
477 polygon![
478 (x: 0., y: 0.),
479 (x: -7.0710678118654755, y: -7.071067811865475),
480 (x: 0., y: -14.142135623730951),
481 (x: 7.071067811865475, y: -7.0710678118654755),
482 (x: 0., y: 0.),
483 ],
484 ]
485 .into();
486
487 assert_relative_eq!(
490 multipolygon.rotate_around_centroid(45.),
491 expected_centroid,
492 epsilon = 1e-12
493 );
494 }
495
496 #[test]
497 fn test_rotate_multipolygons() {
498 let multipolygon: MultiPolygon = vec![
499 polygon![
500 (x: 1., y: 1. ),
501 (x: 2., y: 1. ),
502 (x: 2., y: 10.),
503 (x: 1., y: 10.),
504 (x: 1., y: 1. ),
505 ],
506 polygon![
507 (x: 10., y: 1.),
508 (x: 12., y: 1.),
509 (x: 12., y: 12.),
510 (x: 10., y: 12.),
511 (x: 10., y: 1.),
512 ],
513 ]
514 .into();
515
516 let expected_center: MultiPolygon = vec![
517 polygon![
518 (x: -0.2360967926537398, y: 2.610912703473988),
519 (x: 0.7298290336353284, y: 2.352093658371467),
520 (x: 3.059200439558015, y: 11.04542609497308),
521 (x: 2.093274613268947, y: 11.3042451400756),
522 (x: -0.2360967926537398, y: 2.610912703473988),
523 ],
524 polygon![
525 (x: 8.457235643947875, y: 0.2815412975513012),
526 (x: 10.38908729652601, y: -0.2360967926537403),
527 (x: 13.23609679265374, y: 10.38908729652601),
528 (x: 11.3042451400756, y: 10.90672538673105),
529 (x: 8.457235643947875, y: 0.2815412975513012),
530 ],
531 ]
532 .into();
533
534 let expected_centroid: MultiPolygon = vec![
535 polygon![
536 (x: -0.1016007672888048, y: 3.05186627999456),
537 (x: 0.8643250590002634, y: 2.793047234892039),
538 (x: 3.19369646492295, y: 11.48637967149365),
539 (x: 2.227770638633882, y: 11.74519871659617),
540 (x: -0.1016007672888048, y: 3.05186627999456),
541 ],
542 polygon![
543 (x: 8.59173166931281, y: 0.7224948740718733),
544 (x: 10.52358332189095, y: 0.2048567838668318),
545 (x: 13.37059281801868, y: 10.83004087304658),
546 (x: 11.43874116544054, y: 11.34767896325162),
547 (x: 8.59173166931281, y: 0.7224948740718733),
548 ],
549 ]
550 .into();
551
552 assert_relative_eq!(
554 multipolygon.rotate_around_center(-15.),
555 expected_center,
556 epsilon = 1e-12
557 );
558 assert_relative_eq!(
559 multipolygon.rotate_around_centroid(-15.),
560 expected_centroid,
561 epsilon = 1e-12
562 );
563 }
564
565 #[test]
566 fn test_rotate_empty_geometries_error_gracefully() {
567 let empty_linestring: LineString = line_string![];
569 let rotated_empty_linestring = empty_linestring.rotate_around_centroid(90.);
570 assert_eq!(empty_linestring, rotated_empty_linestring);
571
572 let empty_multilinestring: MultiLineString = MultiLineString::new(vec![]);
574 let rotated_empty_multilinestring = empty_multilinestring.rotate_around_centroid(90.);
575 assert_eq!(empty_multilinestring, rotated_empty_multilinestring);
576
577 let empty_polygon: Polygon<f64> = polygon![];
579 let rotated_empty_polygon = empty_polygon.rotate_around_centroid(90.);
580 assert_eq!(empty_polygon, rotated_empty_polygon);
581
582 let empty_multipolygon: MultiPolygon = Vec::<Polygon<f64>>::new().into();
584 let rotated_empty_multipolygon = empty_multipolygon.rotate_around_centroid(90.);
585 assert_eq!(empty_multipolygon, rotated_empty_multipolygon);
586 }
587}