Skip to main content

dot_canvas/
arc.rs

1use crate::Shape;
2
3pub struct Arc {
4    pub x1: f32,
5    pub y1: f32,
6    pub x2: f32,
7    pub y2: f32,
8    pub radius: f32,
9    pub sweep_flag: bool,
10}
11
12impl Arc {
13    pub fn new(
14        x1: f32,
15        y1: f32,
16        x2: f32,
17        y2: f32,
18        radius: f32,
19        sweep_flag: bool,
20    ) -> Self {
21        Arc {
22            x1,
23            y1,
24            x2,
25            y2,
26            radius,
27            sweep_flag,
28        }
29    }
30
31    /// calculate the center of this arc given start point, end point, radius and sweep direction
32    pub fn center(&self) -> (f32, f32) {
33        let q = ((self.x2 - self.x1).powf(2.0) + (self.y2 - self.y1).powf(2.0))
34            .sqrt();
35        let y3 = (self.y1 + self.y2) / 2.0;
36        let x3 = (self.x1 + self.x2) / 2.0;
37
38        let rr_q22 = (self.radius.powf(2.0) - (q / 2.0).powf(2.0)).sqrt();
39
40        let base_x = rr_q22 * (self.y1 - self.y2) / q;
41        let base_y = rr_q22 * (self.x2 - self.x1) / q;
42
43        if self.sweep_flag {
44            let cx = x3 + base_x;
45            let cy = y3 + base_y;
46            (cx, cy)
47        } else {
48            let cx = x3 - base_x;
49            let cy = y3 - base_y;
50            (cx, cy)
51        }
52    }
53
54    /// which octant range the arc lies
55    fn octant(&self) -> (u8, u8) {
56        let (cx, cy) = self.center();
57        let o1 = Self::line_octant(cx, cy, self.x1, self.y1);
58        let o2 = Self::line_octant(cx, cy, self.x2, self.y2);
59        (o1 + 1, o2)
60    }
61
62    /// calculate the octant of a line
63    fn line_octant(x1: f32, y1: f32, x2: f32, y2: f32) -> u8 {
64        let mut dx = x2 - x1;
65        let mut dy = -(y2 * 2.0 - y1 * 2.0);
66
67        let mut octant = 0;
68
69        if dy < 0.0 {
70            dx = -dx;
71            dy = -dy;
72            octant += 4;
73        }
74
75        if dx < 0.0 {
76            let tmp = dx;
77            dx = dy;
78            dy = -tmp;
79            octant += 2
80        }
81
82        if dx < dy {
83            octant += 1
84        }
85        octant
86    }
87}
88
89impl<'a> Shape<'a> for Arc {
90    fn points(&'a self) -> Box<dyn Iterator<Item = (f32, f32)> + 'a> {
91        let mut x = self.radius;
92        let mut y = 0.0;
93        let mut err = 0.0;
94
95        let inc = 0.25;
96
97        let mut points = vec![];
98
99        let (cx, cy) = self.center();
100        let (o1, o2) = self.octant();
101
102        while x >= y {
103            if (o1..=o2).contains(&7) {
104                points.push((cx + x, cy + y));
105            }
106            if (o1..=o2).contains(&6) {
107                points.push((cx + y, cy + x));
108            }
109            if (o1..=o2).contains(&5) {
110                points.push((cx - y, cy + x));
111            }
112            if (o1..=o2).contains(&4) {
113                points.push((cx - x, cy + y));
114            }
115            if (o1..=o2).contains(&3) {
116                points.push((cx - x, cy - y));
117            }
118            if (o1..=o2).contains(&2) {
119                points.push((cx - y, cy - x));
120            }
121            if (o1..=o2).contains(&1) {
122                points.push((cx + y, cy - x));
123            }
124            if (o1..=o2).contains(&0) {
125                points.push((cx + x, cy - y));
126            }
127
128            if err <= 0.0 {
129                y += inc;
130                err += 2.0 * y + inc;
131            }
132
133            if err > 0.0 {
134                x -= inc;
135                err -= 2.0 * x + inc;
136            }
137        }
138        Box::new(points.into_iter())
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::*;
146
147    #[test]
148    fn arc_center() {
149        let arc = Arc {
150            x1: 0.0,
151            y1: 0.0,
152            x2: 10.0,
153            y2: 10.0,
154            radius: 10.0,
155            sweep_flag: false,
156        };
157
158        let center = arc.center();
159        assert_eq!(center, (10.0, 0.0));
160    }
161
162    #[test]
163    fn arc_center2() {
164        let arc = Arc {
165            x1: 0.0,
166            y1: 0.0,
167            x2: 10.0,
168            y2: 10.0,
169            radius: 10.0,
170            sweep_flag: true,
171        };
172
173        let center = arc.center();
174        assert_eq!(center, (0.0, 10.0));
175    }
176
177    #[test]
178    fn draw_arc() {
179        let width = 11.0;
180        let height = 11.0;
181        let mut context = Context::new(width, height);
182        let arc = Arc {
183            x1: 0.0,
184            y1: 0.0,
185            x2: 10.0,
186            y2: 10.0,
187            radius: 10.0,
188            sweep_flag: false,
189        };
190
191        context.draw(&arc);
192
193        let result = context.to_string();
194        println!("{}", result);
195        let expected = [
196            "⢱                     ",
197            "⢸                     ",
198            " ⡇                    ",
199            " ⠸⡀                   ",
200            "  ⠱⡀                  ",
201            "   ⠑⡄                 ",
202            "    ⠈⠢⡀               ",
203            "      ⠈⠢⡀             ",
204            "        ⠈⠑⠤⣀          ",
205            "            ⠉⠒⠢⠤⢄⣀⣀⣀  ",
206            "                    ⠁ ",
207        ];
208        let expected = expected.join("\n");
209        assert_eq!(result, expected);
210    }
211
212    #[test]
213    fn draw_arc2() {
214        let width = 22.0;
215        let height = 22.0;
216        let mut context = Context::new(width, height);
217        let arc = Arc {
218            x1: 10.0,
219            y1: 0.0,
220            x2: 10.0,
221            y2: 20.0,
222            radius: 10.0,
223            sweep_flag: false,
224        };
225
226        context.draw(&arc);
227
228        let result = context.to_string();
229        println!("{}", result);
230        let expected = [
231            "             ⣀⡠⠤⠔⠒⠒⠒⠁                       ",
232            "         ⢀⠤⠒⠉                               ",
233            "       ⡠⠊⠁                                  ",
234            "     ⡠⠊                                     ",
235            "   ⢀⠎                                       ",
236            "  ⢠⠃                                        ",
237            " ⢠⠃                                         ",
238            " ⡎                                          ",
239            "⢰⠁                                          ",
240            "⢸                                           ",
241            "⢱                                           ",
242            "⢸                                           ",
243            " ⡇                                          ",
244            " ⠸⡀                                         ",
245            "  ⠱⡀                                        ",
246            "   ⠑⡄                                       ",
247            "    ⠈⠢⡀                                     ",
248            "      ⠈⠢⡀                                   ",
249            "        ⠈⠑⠤⣀                                ",
250            "            ⠉⠒⠢⠤⢄⣀⣀⣀                        ",
251            "                    ⠁                       ",
252            "                                            ",
253        ];
254        let expected = expected.join("\n");
255        assert_eq!(result, expected);
256    }
257}