comfy/
trail.rs

1use crate::*;
2
3#[derive(Clone, Debug)]
4pub struct Trail {
5    pub positions: Vec<Vec2>,
6    pub last_vertex_at: Vec2,
7
8    pub z_index: i32,
9
10    pub is_enabled: bool,
11
12    pub trail_length: f32,
13    pub width: f32,
14    pub color_start: Color,
15    pub color_end: Color,
16
17    pub max_vertices: usize,
18    pub fade_start_distance: f32,
19    pub fade_end_distance: f32,
20    pub width_curve: Option<Curve>,
21    pub color_curve: Option<ColorCurve>,
22    pub texture: Option<TextureHandle>,
23    pub blend_mode: BlendMode,
24}
25
26impl Trail {
27    pub fn new(
28        width: f32,
29        trail_length: f32,
30        z_index: i32,
31        color_start: Color,
32        color_end: Color,
33        max_vertices: usize,
34        fade_start_distance: f32,
35        fade_end_distance: f32,
36        // width_curve: Option<Curve>,
37        color_curve: Option<ColorCurve>,
38        texture: Option<TextureHandle>,
39        blend_mode: BlendMode,
40    ) -> Self {
41        Self {
42            positions: vec![],
43            last_vertex_at: Vec2::ZERO,
44
45            z_index,
46
47            is_enabled: true,
48
49            trail_length,
50            width,
51            color_start,
52            color_end,
53
54            max_vertices,
55            fade_start_distance,
56            fade_end_distance,
57            width_curve: None,
58            color_curve,
59            texture,
60            blend_mode,
61        }
62    }
63
64    pub fn simple(
65        width: f32,
66        trail_length: f32,
67        z_index: i32,
68        color_start: Color,
69        color_end: Color,
70    ) -> Self {
71        Self::new(
72            width,
73            trail_length,
74            z_index,
75            color_start,
76            color_end,
77            100,
78            0.0,
79            0.0,
80            None,
81            None,
82            BlendMode::Additive,
83        )
84    }
85
86    pub fn update(&mut self, position: Vec2, _delta: f32) {
87        if self.is_enabled {
88            let distance = (position - self.last_vertex_at).length();
89
90            let min_vertex_distance =
91                self.trail_length / self.max_vertices as f32;
92
93            if self.positions.is_empty() {
94                self.positions.push(position);
95                self.last_vertex_at = position;
96            } else if distance > min_vertex_distance {
97                // The number of interpolation steps is the distance divided by min_vertex_distance, rounded up
98                let num_steps =
99                    (distance / min_vertex_distance).ceil() as usize;
100                for i in 1..=num_steps {
101                    // Compute the interpolated position
102                    let t = i as f32 / num_steps as f32;
103                    let interpolated_position =
104                        self.last_vertex_at * (1.0 - t) + position * t;
105                    self.positions.push(interpolated_position);
106                }
107
108                let mut total_distance = self.total_distance();
109
110                while self.positions.len() > 2 &&
111                    total_distance > self.trail_length
112                {
113                    self.positions.remove(0);
114                    total_distance = self.total_distance();
115                }
116
117                self.last_vertex_at = position;
118
119                // self.positions.push(position);
120                //
121                // let mut total_distance = self.total_distance();
122                //
123                // while self.positions.len() > 2 &&
124                //     total_distance > self.trail_length
125                // {
126                //     self.positions.remove(0);
127                //     total_distance = self.total_distance();
128                // }
129                //
130                // self.last_vertex_at = position;
131            }
132
133            // self.positions.push(position);
134        } else if let Some(first_position) = self.positions.first() {
135            if (*first_position - position).length() > self.trail_length {
136                self.positions.remove(0);
137            }
138        }
139    }
140
141    fn total_distance(&self) -> f32 {
142        self.positions.windows(2).map(|w| (w[0] - w[1]).length()).sum()
143    }
144
145    pub fn draw_mesh(&self) {
146        if self.positions.len() <= 1 {
147            return;
148        }
149
150        let tex = self.texture.unwrap_or(texture_id("1px"));
151        // let tex = texture_id("1px");
152
153        // let mut trail_length = 0.0;
154
155        let mut vertices = vec![];
156
157        for (i, (a, b)) in self.positions.iter().tuple_windows().enumerate() {
158            let n = self.positions.len() as f32;
159            // let step = 1.0 / n;
160            let pct = i as f32 / n;
161
162            let off = 2.0 * (get_unpaused_time() as f32 % 1.0);
163
164            let width_pct_a = self
165                .width_curve
166                .as_ref()
167                .map(|curve| curve.eval(pct))
168                .unwrap_or(pct);
169
170            // let width_pct_b = self
171            //     .width_curve
172            //     .as_ref()
173            //     .map(|curve| curve.eval(pct + step))
174            //     .unwrap_or(pct + step);
175
176            let color = self
177                .color_curve
178                .as_ref()
179                .map(|curve| curve.eval(pct))
180                .unwrap_or(self.color_start.lerp(self.color_end, 1.0 - pct));
181
182            // If the trail is not enabled, fade the color out based on the distance
183            let color = if self.is_enabled {
184                color
185            } else {
186                let distance_pct = (i as f32 / n).powi(2);
187                color.darken(distance_pct)
188            };
189
190            let p1 = Position::world(a.x, a.y);
191            let p2 = Position::world(b.x, b.y);
192
193            let (x1, y1) = p1.to_world().tuple();
194            let (x2, y2) = p2.to_world().tuple();
195
196            let dx = x2 - x1;
197            let dy = y2 - y1;
198
199            let nx = -dy;
200            let ny = dx;
201
202            let tlen = (nx * nx + ny * ny).sqrt();
203            if tlen < std::f32::EPSILON {
204                return;
205            }
206
207            let nxn = nx / tlen;
208            let nyn = ny / tlen;
209
210
211            let start_thickness = self.width * width_pct_a;
212            // let end_thickness = self.width * width_pct_b;
213
214            let tx1 = nxn * start_thickness * 0.5;
215            let ty1 = nyn * start_thickness * 0.5;
216
217            // let tx2 = nxn * end_thickness * 0.5;
218            // let ty2 = nyn * end_thickness * 0.5;
219
220            let z = self.z_index as f32;
221
222            // let wrapped_y_uv_start = uv_offset % 1.0;
223            // let wrapped_y_uv_end = (uv_offset + uv_size) % 1.0;
224
225            let start = off + pct;
226            // let uv_size = step;
227
228            // let start = uv_offset;
229            // let end = start + uv_size;
230
231            let top_left = vec3(x1 + tx1, y1 + ty1, z);
232            let bottom_left = vec3(x1 - tx1, y1 - ty1, z);
233
234            vertices.push(SpriteVertex::new(top_left, vec2(0.0, start), color));
235            vertices.push(SpriteVertex::new(
236                bottom_left,
237                vec2(1.0, start),
238                color,
239            ));
240
241            // draw_line_tex_y_uv_flex(
242            //     // 1.0,
243            //     // 1.0,
244            //     // color * alpha,
245            //     color,
246            //     Some(tex),
247            //     self.z_index,
248            //     TextureParams {
249            //         blend_mode: self.blend_mode,
250            //         ..Default::default()
251            //     },
252            // );
253        }
254
255        let indices = Self::generate_triangle_list_indices(vertices.len());
256
257        draw_mesh_ex(
258            Mesh {
259                // TODO: might want the average instead
260                origin: self
261                    .positions
262                    .last()
263                    .copied()
264                    .unwrap_or_default()
265                    .extend(self.z_index as f32),
266                vertices: vertices.into(),
267                indices: indices.into(),
268                z_index: self.z_index,
269                texture: Some(tex),
270                y_sort_offset: 0.0,
271            },
272            BlendMode::Additive,
273        );
274    }
275
276    fn generate_triangle_list_indices(n: usize) -> Vec<u32> {
277        let mut indices = Vec::with_capacity(2 * (n - 2));
278        let mut is_even = true;
279
280        for i in 1..(n as u32 - 1) {
281            if is_even {
282                indices.push(i - 1);
283                indices.push(i);
284                indices.push(i + 1);
285            } else {
286                indices.push(i + 1);
287                indices.push(i);
288                indices.push(i - 1);
289            }
290
291            is_even = !is_even;
292        }
293
294        indices
295    }
296
297    // pub fn draw(&self) {
298    //     if self.positions.len() <= 1 {
299    //         return;
300    //     }
301    //
302    //     let tex = self.texture.unwrap_or(texture_id("trail"));
303    //     // let tex = texture_id("1px");
304    //
305    //     let mut trail_length = 0.0;
306    //
307    //     // let mesh = vec![];
308    //
309    //     for (i, (a, b)) in self.positions.iter().tuple_windows().enumerate() {
310    //         let n = self.positions.len() as f32;
311    //         let step = 1.0 / n;
312    //         let pct = i as f32 / n;
313    //
314    //         let off = 2.0 * get_unpaused_time() as f32;
315    //
316    //         let width_pct_a = self
317    //             .width_curve
318    //             .as_ref()
319    //             .map(|curve| curve.eval(pct))
320    //             .unwrap_or(pct);
321    //
322    //         let width_pct_b = self
323    //             .width_curve
324    //             .as_ref()
325    //             .map(|curve| curve.eval(pct + step))
326    //             .unwrap_or(pct + step);
327    //
328    //         // let width_pct_a = pct;
329    //         // let width_pct_b = pct + step;
330    //
331    //         let color = self
332    //             .color_curve
333    //             .as_ref()
334    //             .map(|curve| curve.eval(pct))
335    //             .unwrap_or(self.color_start.lerp(self.color_end, 1.0 - pct));
336    //
337    //         trail_length += (*b - *a).length();
338    //
339    //         let fade_start = trail_length - self.fade_start_distance;
340    //         let fade_end = trail_length - self.fade_end_distance;
341    //
342    //         let alpha = if trail_length < self.fade_start_distance {
343    //             1.0
344    //         } else if trail_length > self.fade_end_distance {
345    //             0.0
346    //         } else {
347    //             1.0 - (fade_start / (fade_end - fade_start))
348    //         };
349    //
350    //         draw_line_tex_y_uv_flex(
351    //             Position::world(a.x, a.y),
352    //             Position::world(b.x, b.y),
353    //             self.width * width_pct_a,
354    //             self.width * width_pct_b,
355    //             // 1.0,
356    //             // 1.0,
357    //             // color * alpha,
358    //             color,
359    //             Some(tex),
360    //             off + pct,
361    //             step,
362    //             self.z_index,
363    //             TextureParams {
364    //                 blend_mode: self.blend_mode,
365    //                 ..Default::default()
366    //             },
367    //         );
368    //     }
369    // }
370
371    // pub fn draw(&self) {
372    //     if self.positions.len() <= 1 {
373    //         return;
374    //     }
375    //
376    //     // let tex = self.texture.unwrap_or(texture_id("trail"));
377    //     let tex = texture_id("1px");
378    //
379    //     let mut trail_length = 0.0;
380    //
381    //     for (i, (a, b)) in self.positions.iter().tuple_windows().enumerate() {
382    //         let n = self.positions.len() as f32;
383    //         let step = 1.0 / n;
384    //         let pct = i as f32 / n;
385    //
386    //         let off = 2.0 * get_unpaused_time() as f32;
387    //
388    //         let width_pct = self
389    //             .width_curve
390    //             .as_ref()
391    //             .map(|curve| curve.eval(pct))
392    //             .unwrap_or(pct);
393    //
394    //         let width_pct_a = self
395    //             .width_curve
396    //             .as_ref()
397    //             .map(|curve| curve.eval(pct))
398    //             .unwrap_or(pct);
399    //         let width_pct_b = self
400    //             .width_curve
401    //             .as_ref()
402    //             .map(|curve| curve.eval(pct + step))
403    //             .unwrap_or(pct + step);
404    //
405    //         let color = self
406    //             .color_curve
407    //             .as_ref()
408    //             .map(|curve| curve.eval(pct))
409    //             .unwrap_or(self.color_start.lerp(self.color_end, 1.0 - pct));
410    //
411    //         trail_length += (*b - *a).length();
412    //
413    //         let fade_start = trail_length - self.fade_start_distance;
414    //         let fade_end = trail_length - self.fade_end_distance;
415    //
416    //         let alpha = if trail_length < self.fade_start_distance {
417    //             1.0
418    //         } else if trail_length > self.fade_end_distance {
419    //             0.0
420    //         } else {
421    //             1.0 - (fade_start / (fade_end - fade_start))
422    //         };
423    //
424    //         draw_line_tex_y_uv_flex(
425    //             Position::world(a.x, a.y),
426    //             Position::world(b.x, b.y),
427    //             self.width * width_pct_a,
428    //             self.width * width_pct_b,
429    //             color * alpha,
430    //             Some(tex),
431    //             (off + pct)..(off + pct + step),
432    //             self.z_index,
433    //             TextureParams {
434    //                 blend_mode: self.blend_mode,
435    //                 ..Default::default()
436    //             },
437    //         );
438    //
439    //         // draw_line_tex_y_uv(
440    //         //     Position::world(a.x, a.y),
441    //         //     Position::world(b.x, b.y),
442    //         //     self.width * width_pct,
443    //         //     color * alpha,
444    //         //     Some(tex),
445    //         //     (off + pct)..(off + pct + step),
446    //         //     self.z_index,
447    //         //     TextureParams {
448    //         //         blend_mode: self.blend_mode,
449    //         //         ..Default::default()
450    //         //     },
451    //         // );
452    //     }
453    // }
454}
455
456#[derive(Clone, Debug)]
457pub struct Curve {
458    pub points: Vec<(f32, f32)>,
459    pub wrap: bool,
460}
461
462impl Curve {
463    pub fn eval(&self, t: f32) -> f32 {
464        let len = self.points.len();
465        if len == 0 {
466            return 0.0;
467        }
468        if len == 1 {
469            return self.points[0].1;
470        }
471
472        let x_min = self.points.first().unwrap().0;
473        let x_max = self.points.last().unwrap().0;
474        let t_normalized = t * (x_max - x_min) + x_min;
475
476        if t_normalized <= x_min {
477            if self.wrap {
478                let (x0, y0) = self.points[len - 1];
479                let (x1, y1) = self.points[0];
480                return y0 + (t_normalized - x0) * (y1 - y0) / (x1 - x0);
481            } else {
482                return self.points[0].1;
483            }
484        }
485        if t_normalized >= x_max {
486            if self.wrap {
487                let (x0, y0) = self.points[len - 1];
488                let (x1, y1) = self.points[0];
489                return y0 + (t_normalized - x0) * (y1 - y0) / (x1 - x0);
490            } else {
491                return self.points[len - 1].1;
492            }
493        }
494        for i in 1..len {
495            if t_normalized <= self.points[i].0 {
496                let (x0, y0) = self.points[i - 1];
497                let (x1, y1) = self.points[i];
498                return y0 + (t_normalized - x0) * (y1 - y0) / (x1 - x0);
499            }
500        }
501
502        0.0
503    }
504}
505
506// impl Curve {
507//     pub fn eval(&self, t: f32) -> f32 {
508//         let len = self.points.len();
509//         if len == 0 {
510//             return 0.0;
511//         }
512//         if len == 1 {
513//             return self.points[0].1;
514//         }
515//         if t <= self.points[0].0 {
516//             if self.wrap {
517//                 let (x0, y0) = self.points[len - 1];
518//                 let (x1, y1) = self.points[0];
519//                 return y0 + (t - x0) * (y1 - y0) / (x1 - x0);
520//             } else {
521//                 return self.points[0].1;
522//             }
523//         }
524//         if t >= self.points[len - 1].0 {
525//             if self.wrap {
526//                 let (x0, y0) = self.points[len - 1];
527//                 let (x1, y1) = self.points[0];
528//                 return y0 + (t - x0) * (y1 - y0) / (x1 - x0);
529//             } else {
530//                 return self.points[len - 1].1;
531//             }
532//         }
533//         for i in 1..len {
534//             if t <= self.points[i].0 {
535//                 let (x0, y0) = self.points[i - 1];
536//                 let (x1, y1) = self.points[i];
537//                 return y0 + (t - x0) * (y1 - y0) / (x1 - x0);
538//             }
539//         }
540//         return 0.0;
541//     }
542// }
543
544#[derive(Clone, Debug)]
545pub struct ColorCurve {
546    pub gradient: Vec<(Color, f32)>,
547}
548
549impl ColorCurve {
550    pub fn new(gradient: Vec<(Color, f32)>) -> Self {
551        Self { gradient }
552    }
553
554    pub fn eval(&self, t: f32) -> Color {
555        if t <= 0.0 {
556            return self.gradient[0].0;
557        }
558        if t >= 1.0 {
559            return self.gradient.last().unwrap().0;
560        }
561
562        for i in 1..self.gradient.len() {
563            let (prev_color, prev_pos) = self.gradient[i - 1];
564            let (next_color, next_pos) = self.gradient[i];
565
566            if t <= next_pos {
567                let factor = (t - prev_pos) / (next_pos - prev_pos);
568                return prev_color.lerp(next_color, factor);
569            }
570        }
571
572        // This should never be reached, as t is already checked for >= 1.0
573        return self.gradient.last().unwrap().0;
574    }
575}