ori_graphics/
curve.rs

1use std::f32::consts::PI;
2
3use glam::Vec2;
4
5use crate::{Color, Mesh, Vertex};
6
7#[derive(Clone, Debug, Default, PartialEq)]
8pub struct Curve {
9    pub points: Vec<Vec2>,
10}
11
12impl Curve {
13    pub fn new() -> Self {
14        Self { points: vec![] }
15    }
16
17    pub fn arc(center: Vec2, radius: f32, start_angle: f32, end_angle: f32) -> Self {
18        let mut curve = Curve::new();
19
20        let mut angle = start_angle;
21        let step = (end_angle - start_angle) / 32.0;
22
23        while angle < end_angle {
24            let x = center.x + radius * angle.cos();
25            let y = center.y + radius * angle.sin();
26
27            curve.add_point(Vec2::new(x, y));
28
29            angle += step;
30        }
31
32        curve
33    }
34
35    pub fn len(&self) -> usize {
36        self.points.len()
37    }
38
39    pub fn is_empty(&self) -> bool {
40        self.points.is_empty()
41    }
42
43    pub fn add_point(&mut self, point: Vec2) {
44        self.points.push(point);
45    }
46
47    pub fn remove_point(&mut self, index: usize) {
48        self.points.remove(index);
49    }
50
51    pub fn clear(&mut self) {
52        self.points.clear();
53    }
54
55    pub fn rounded_mesh(self, thickness: f32, color: Color) -> Mesh {
56        if self.is_empty() {
57            return Mesh::new();
58        }
59
60        if self.len() == 1 {
61            return Mesh::circle(self.points[0], thickness, color);
62        }
63
64        let mut mesh = Mesh::new();
65
66        // compute first cap
67        let center = self.points[0];
68        let next = self.points[1];
69        let angle = (center - next).normalize();
70        let angle = angle.y.atan2(angle.x);
71
72        let index = mesh.vertices.len() as u32;
73        mesh.vertices.push(Vertex::new_color(center, color));
74        for i in -10..=10 {
75            let angle = angle + i as f32 * PI / 20.0;
76            let point = center + Vec2::new(angle.cos(), angle.sin()) * thickness;
77            mesh.vertices.push(Vertex::new_color(point, color));
78
79            if i > -10 {
80                let i = mesh.vertices.len() as u32;
81                mesh.indices.push(index);
82                mesh.indices.push(i - 2);
83                mesh.indices.push(i - 1);
84            }
85        }
86
87        // compute middle segments
88        for i in 0..self.len() {
89            if i == self.len() - 1 {
90                let prev = self.points[i - 1];
91                let center = self.points[i];
92
93                let prev_center = (center - prev).normalize();
94
95                let hat = Vec2::new(prev_center.y, -prev_center.x);
96
97                let offset = hat * thickness;
98
99                let vertex_a = Vertex::new_color(center + offset, color);
100                let vertex_b = Vertex::new_color(center - offset, color);
101
102                let i = mesh.vertices.len() as u32;
103                mesh.vertices.push(vertex_a);
104                mesh.vertices.push(vertex_b);
105
106                // add indices for prev center
107                mesh.indices.push(i - 2);
108                mesh.indices.push(i - 1);
109                mesh.indices.push(i + 0);
110                mesh.indices.push(i - 1);
111                mesh.indices.push(i + 1);
112                mesh.indices.push(i + 0);
113            } else if i > 0 {
114                let prev = self.points[i - 1];
115                let center = self.points[i];
116                let next = self.points[i + 1];
117
118                let prev_center = (center - prev).normalize();
119                let center_next = (next - center).normalize();
120
121                let hat_a = Vec2::new(prev_center.y, -prev_center.x);
122                let hat_b = Vec2::new(center_next.y, -center_next.x);
123
124                let offset = (hat_a + hat_b).normalize() * thickness;
125
126                let vertex_a = Vertex::new_color(center + offset, color);
127                let vertex_b = Vertex::new_color(center - offset, color);
128
129                let i = mesh.vertices.len() as u32;
130                mesh.vertices.push(vertex_a);
131                mesh.vertices.push(vertex_b);
132
133                // add indices for prev center
134                mesh.indices.push(i - 2);
135                mesh.indices.push(i - 1);
136                mesh.indices.push(i + 0);
137                mesh.indices.push(i - 1);
138                mesh.indices.push(i + 1);
139                mesh.indices.push(i + 0);
140            } else {
141                let center = self.points[i];
142                let next = self.points[i + 1];
143
144                let center_next = (next - center).normalize();
145
146                let hat = Vec2::new(center_next.y, -center_next.x);
147
148                let offset = hat * thickness;
149
150                let vertex_a = Vertex::new_color(center + offset, color);
151                let vertex_b = Vertex::new_color(center - offset, color);
152
153                mesh.vertices.push(vertex_a);
154                mesh.vertices.push(vertex_b);
155            }
156        }
157
158        // compute last cap
159        let center = self.points[self.len() - 1];
160        let prev = self.points[self.len() - 2];
161        let angle = (center - prev).normalize();
162        let angle = angle.y.atan2(angle.x);
163
164        let index = mesh.vertices.len() as u32;
165        mesh.vertices.push(Vertex::new_color(center, color));
166        for i in -10..=10 {
167            let angle = angle + i as f32 * PI / 20.0;
168            let point = center + Vec2::new(angle.cos(), angle.sin()) * thickness;
169            mesh.vertices.push(Vertex::new_color(point, color));
170
171            if i > -10 {
172                let i = mesh.vertices.len() as u32;
173                mesh.indices.push(index);
174                mesh.indices.push(i - 2);
175                mesh.indices.push(i - 1);
176            }
177        }
178
179        mesh
180    }
181}
182
183impl IntoIterator for Curve {
184    type Item = Vec2;
185    type IntoIter = std::vec::IntoIter<Vec2>;
186
187    fn into_iter(self) -> Self::IntoIter {
188        self.points.into_iter()
189    }
190}
191
192impl<'a> IntoIterator for &'a Curve {
193    type Item = &'a Vec2;
194    type IntoIter = std::slice::Iter<'a, Vec2>;
195
196    fn into_iter(self) -> Self::IntoIter {
197        self.points.iter()
198    }
199}
200
201impl<'a> IntoIterator for &'a mut Curve {
202    type Item = &'a mut Vec2;
203    type IntoIter = std::slice::IterMut<'a, Vec2>;
204
205    fn into_iter(self) -> Self::IntoIter {
206        self.points.iter_mut()
207    }
208}