1use super::*;
2
3pub struct Chain {
4 pub transform: mat3<f32>,
5 pub vertices: Vec<ColoredVertex>,
6}
7
8impl Chain {
9 pub fn new(
10 chain: batbox_lapp::Chain<f32>,
11 width: f32,
12 color: Rgba<f32>,
13 round_resolution: usize,
14 ) -> Self {
15 Self::new_gradient(
16 chain
17 .vertices
18 .into_iter()
19 .map(|pos| ColoredVertex {
20 a_pos: pos,
21 a_color: color,
22 })
23 .collect(),
24 width,
25 round_resolution,
26 )
27 }
28
29 pub fn new_gradient(vertices: Vec<ColoredVertex>, width: f32, round_resolution: usize) -> Self {
30 let len = vertices.len();
31 if len < 2 {
32 return Self {
33 transform: mat3::identity(),
34 vertices: vec![],
35 };
36 }
37
38 let polygon_vertices = (len - 1) * 6;
39 let mut polygon = Vec::with_capacity(polygon_vertices);
40
41 {
43 let dir = (vertices[1].a_pos - vertices[0].a_pos)
44 .normalize_or_zero()
45 .rotate_90()
46 * width
47 / 2.0;
48 polygon.push(ColoredVertex {
49 a_pos: vertices[0].a_pos + dir,
50 ..vertices[0]
51 });
52 let right = ColoredVertex {
53 a_pos: vertices[0].a_pos - dir,
54 ..vertices[0]
55 };
56 polygon.push(right);
57 polygon.push(right); polygon.push(right);
59 }
60
61 let mut vertex_iter = vertices.iter().copied();
63 let (mut prev, mut current) = (vertex_iter.next().unwrap(), vertex_iter.next().unwrap());
64 {
65 for next in vertex_iter {
66 let backward = (prev.a_pos - current.a_pos).normalize_or_zero();
68 let forward = (next.a_pos - current.a_pos).normalize_or_zero();
69 if backward == vec2::ZERO || forward == vec2::ZERO {
70 current = next;
72 continue;
73 }
74
75 let cos = -vec2::dot(forward, backward);
76 let cos_half = ((cos + 1.0) / 2.0).max(0.0).sqrt();
77
78 if cos_half.approx_eq(&1.0) {
79 let dir =
81 (current.a_pos - prev.a_pos).normalize_or_zero().rotate_90() * width / 2.0;
82 let left = ColoredVertex {
83 a_pos: current.a_pos + dir,
84 ..current
85 };
86 let right = ColoredVertex {
87 a_pos: current.a_pos - dir,
88 ..current
89 };
90 let temp = polygon.len() - 2;
92 polygon[temp] = left;
93 polygon.push(left);
94 polygon.push(right);
95 polygon.push(left);
97 polygon.push(right);
98 polygon.push(right); polygon.push(right);
100
101 prev = current;
102 current = next;
103 continue;
104 }
105
106 let d = width / cos_half.max(0.1) / 2.0;
109
110 let inside_dir = (backward + forward).normalize_or_zero();
111 let inner = current.a_pos + inside_dir * d;
112
113 let side = vec2::dot(
116 (next.a_pos - prev.a_pos).normalize_or_zero().rotate_90(),
117 inside_dir,
118 )
119 .signum();
120
121 let inner_vertex = ColoredVertex {
122 a_pos: inner,
123 ..current
124 };
125
126 let backward_norm = backward.rotate_90() * side;
127 let back_vertex = ColoredVertex {
128 a_pos: inner + backward_norm * width,
129 ..current
130 };
131
132 let forward_norm = -forward.rotate_90() * side;
133 let forward_vertex = ColoredVertex {
134 a_pos: inner + forward_norm * width,
135 ..current
136 };
137
138 {
140 let (left, right) = if side.is_sign_positive() {
141 (inner_vertex, back_vertex) } else {
143 (back_vertex, inner_vertex) };
145 let temp = polygon.len() - 2;
146 polygon[temp] = left;
147 polygon.push(left);
148 polygon.push(right);
149 }
150
151 {
153 let angle =
154 Angle::acos(vec2::dot(forward_norm, backward_norm).clamp(-1.0, 1.0));
155 let (start, end, shift) = if side.is_sign_positive() {
156 (back_vertex, forward_vertex, backward_norm * width)
157 } else {
158 (forward_vertex, back_vertex, forward_norm * width)
159 };
160 let mut round = Vec::with_capacity(round_resolution + 2);
161 round.push(start);
162 for i in 1..=round_resolution {
163 round.push(ColoredVertex {
164 a_pos: inner
165 + shift.rotate(angle * i as f32 / (round_resolution + 1) as f32),
166 ..current
167 });
168 }
169 round.push(end);
170
171 for i in 0..=round_resolution {
173 polygon.push(inner_vertex);
174 polygon.push(round[i]);
175 polygon.push(round[i + 1]);
176 }
177 }
178
179 {
181 let (left, right) = if side.is_sign_positive() {
182 (inner_vertex, forward_vertex) } else {
184 (forward_vertex, inner_vertex) };
186 polygon.push(left);
187 polygon.push(right);
188 polygon.push(right); polygon.push(right);
190 }
191
192 prev = current;
193 current = next;
194 }
195 }
196
197 {
199 let dir = (current.a_pos - prev.a_pos).normalize_or_zero().rotate_90() * width / 2.0;
200 let left = ColoredVertex {
201 a_pos: vertices[len - 1].a_pos + dir,
202 ..vertices[len - 1]
203 };
204 let temp = polygon.len() - 2;
205 polygon[temp] = left; polygon.push(left);
207 polygon.push(ColoredVertex {
208 a_pos: vertices[len - 1].a_pos - dir,
209 ..vertices[len - 1]
210 });
211 }
212
213 let (transform, vertices) = Polygon::normalize(polygon);
214 Self {
215 transform,
216 vertices,
217 }
218 }
219}
220
221impl Transform2d<f32> for Chain {
222 fn bounding_quad(&self) -> batbox_lapp::Quad<f32> {
223 batbox_lapp::Quad {
224 transform: self.transform,
225 }
226 }
227
228 fn apply_transform(&mut self, transform: mat3<f32>) {
229 self.transform = transform * self.transform;
230 }
231}
232
233impl Draw2d for Chain {
234 fn draw2d_transformed(
235 &self,
236 helper: &Helper,
237 framebuffer: &mut ugli::Framebuffer,
238 camera: &dyn AbstractCamera2d,
239 transform: mat3<f32>,
240 ) {
241 let framebuffer_size = framebuffer.size();
242 ugli::draw(
243 framebuffer,
244 &helper.color_program,
245 ugli::DrawMode::Triangles,
246 &ugli::VertexBuffer::new_dynamic(helper.ugli(), self.vertices.clone()),
247 (
248 ugli::uniforms! {
249 u_color: Rgba::WHITE,
250 u_framebuffer_size: framebuffer_size,
251 u_model_matrix: transform * self.transform,
252 },
253 camera.uniforms(framebuffer_size.map(|x| x as f32)),
254 ),
255 ugli::DrawParameters {
256 blend_mode: Some(ugli::BlendMode::straight_alpha()),
257 ..Default::default()
258 },
259 );
260 }
261}