1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use std::fmt;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::{Angle, Bounds, Distance, Polygon, Pt2D, Ring};
const TRIANGLES_PER_CIRCLE: usize = 60;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Circle {
pub center: Pt2D,
pub radius: Distance,
}
impl Circle {
pub fn new(center: Pt2D, radius: Distance) -> Circle {
Circle { center, radius }
}
pub fn contains_pt(&self, pt: Pt2D) -> bool {
(pt.x() - self.center.x()).powi(2) + (pt.y() - self.center.y()).powi(2)
< self.radius.inner_meters().powi(2)
}
pub fn get_bounds(&self) -> Bounds {
Bounds {
min_x: self.center.x() - self.radius.inner_meters(),
max_x: self.center.x() + self.radius.inner_meters(),
min_y: self.center.y() - self.radius.inner_meters(),
max_y: self.center.y() + self.radius.inner_meters(),
}
}
pub fn to_polygon(&self) -> Polygon {
self.to_ring().into_polygon()
}
pub fn to_partial_polygon(&self, percent_full: f64) -> Polygon {
#![allow(clippy::float_cmp)]
assert!((0. ..=1.).contains(&percent_full));
let mut pts = vec![self.center];
let mut indices = Vec::new();
for i in 0..TRIANGLES_PER_CIRCLE {
pts.push(self.center.project_away(
self.radius,
Angle::degrees((i as f64) / (TRIANGLES_PER_CIRCLE as f64) * percent_full * 360.0),
));
indices.push(0);
indices.push(i + 1);
if i != TRIANGLES_PER_CIRCLE - 1 {
indices.push(i + 2);
} else if percent_full == 1.0 {
indices.push(1);
} else {
indices.pop();
indices.pop();
}
}
Polygon::precomputed(pts, indices)
}
fn to_ring(&self) -> Ring {
let mut pts: Vec<Pt2D> = (0..=TRIANGLES_PER_CIRCLE)
.map(|i| {
self.center.project_away(
self.radius,
Angle::degrees((i as f64) / (TRIANGLES_PER_CIRCLE as f64) * 360.0),
)
})
.collect();
pts.dedup();
Ring::must_new(pts)
}
pub fn to_outline(&self, thickness: Distance) -> Result<Polygon> {
if self.radius <= thickness {
bail!(
"Can't make Circle outline with radius {} and thickness {}",
self.radius,
thickness
);
}
let bigger = self.to_ring();
let smaller = Circle::new(self.center, self.radius - thickness).to_ring();
Ok(Polygon::with_holes(bigger, vec![smaller]))
}
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle({}, {})", self.center, self.radius)
}
}