ranim_items/vitem/geometry/
line.rs

1use ranim_core::{
2    Extract,
3    color::{AlphaColor, Srgb},
4    core_item::{CoreItem, vitem::DEFAULT_STROKE_WIDTH},
5    glam::DVec3,
6    traits::{
7        Aabb, Discard as _, Locate, Opacity, RotateTransform, ScaleTransform, ShiftTransform,
8        StrokeColor, StrokeWidth, With as _,
9    },
10};
11use ranim_macros::Interpolatable;
12
13use crate::vitem::VItem;
14
15/// A line segment.
16#[derive(Debug, Clone, Interpolatable)]
17pub struct Line {
18    /// The start and end points of the line.
19    pub points: [DVec3; 2],
20    /// The distance two endpoints extends or shrinks from its original position.
21    /// Positive value means extension and negative value means shrinking.
22    pub extrude: [f64; 2],
23    /// Stroke RGBA values.
24    pub stroke_rgba: AlphaColor<Srgb>,
25    /// Stroke width.
26    pub stroke_width: f32,
27}
28
29impl Line {
30    /// Creates a new line segment with the given start and end points.
31    pub fn new(start: DVec3, end: DVec3) -> Self {
32        Self {
33            points: [start, end],
34            extrude: [0., 0.],
35            stroke_rgba: AlphaColor::WHITE,
36            stroke_width: DEFAULT_STROKE_WIDTH,
37        }
38    }
39
40    /// Inverts the direction of the line segment.
41    pub fn invert(&mut self) -> &mut Self {
42        self.points.reverse();
43        self.extrude.reverse();
44        self
45    }
46
47    /// Returns the start and end points of the line segment considering the extrusion distance.
48    pub fn points_with_extrude(&self) -> [DVec3; 2] {
49        let [p1, p2] = self.points;
50        let [ext1, ext2] = self.extrude;
51        let d = (p2 - p1).normalize();
52        [p1 - d * ext1, p2 + d * ext2]
53    }
54}
55
56impl Locate<Line> for f64 {
57    fn locate(&self, target: &Line) -> DVec3 {
58        let [p1, p2] = target.points;
59        p1.lerp(p2, *self)
60    }
61}
62
63impl Aabb for Line {
64    fn aabb(&self) -> [DVec3; 2] {
65        self.points_with_extrude().aabb()
66    }
67}
68
69impl ShiftTransform for Line {
70    fn shift(&mut self, offset: DVec3) -> &mut Self {
71        self.points.shift(offset);
72        self
73    }
74}
75
76impl RotateTransform for Line {
77    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
78        self.points.rotate_on_axis(axis, angle);
79        self
80    }
81}
82
83impl ScaleTransform for Line {
84    fn scale(&mut self, scale: DVec3) -> &mut Self {
85        let [p1, p2] = self.points;
86        let k = ((p2 - p1).normalize() * scale).length();
87        self.points.scale(scale);
88        self.extrude.iter_mut().for_each(|e| *e *= k);
89        self
90    }
91}
92
93impl StrokeColor for Line {
94    fn stroke_color(&self) -> AlphaColor<Srgb> {
95        self.stroke_rgba
96    }
97
98    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
99        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
100        self
101    }
102
103    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
104        self.stroke_rgba = color;
105        self
106    }
107}
108
109impl From<Line> for VItem {
110    fn from(value: Line) -> Self {
111        let [p1, p2] = value.points_with_extrude();
112        VItem::from_vpoints(vec![p1, (p1 + p2) / 2., p2]).with(|item| {
113            item.set_stroke_color(value.stroke_rgba)
114                .set_stroke_width(value.stroke_width)
115                .discard()
116        })
117    }
118}
119
120impl Opacity for Line {
121    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
122        self.set_stroke_opacity(opacity);
123        self
124    }
125}
126
127impl Extract for Line {
128    type Target = CoreItem;
129
130    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
131        VItem::from(self.clone()).extract_into(buf);
132    }
133}