ranim_items/vitem/
mod.rs

1//! Quadratic Bezier Concatenated Item
2//!
3//! VItem itself is composed with 3d bezier path segments, but when *ranim* renders VItem,
4//! it assumes that all points are in the same plane to calculate depth information.
5//! Which means that ranim actually renders a **projection** of the VItem onto a plane.
6//!
7//! The projection target plane has the initial basis and normal defined as `(DVec3::X, DVec3::Y)` and `DVec3::Z` respectively, and it contains the first point of the VItem.
8//!
9//! So the normal way to use a [`VItem`] is to make sure that all points are in the same plane, at this time the **projection** is equivalent to the VItem itself. Or you may break this, and let ranim renders the **projection** of it.
10// pub mod arrow;
11/// Geometry items
12pub mod geometry;
13/// Svg item
14pub mod svg;
15/// Simple text items
16pub mod text;
17/// Typst items
18pub mod typst;
19
20use color::{AlphaColor, Srgb, palette::css};
21use glam::{DVec3, Vec4, vec4};
22use ranim_core::anchor::Aabb;
23use ranim_core::core_item::CoreItem;
24use ranim_core::core_item::vitem::Basis2d;
25use ranim_core::{Extract, color, glam};
26
27use ranim_core::{
28    components::{PointVec, VecResizeTrait, rgba::Rgba, vpoint::VPointVec, width::Width},
29    prelude::{Alignable, Empty, FillColor, Opacity, Partial, StrokeWidth},
30    traits::{PointsFunc, RotateTransform, ScaleTransform, ShiftTransform, StrokeColor},
31};
32
33/// A vectorized item.
34///
35/// It is built from four components:
36/// - [`VItem::vpoints`]: the vpoints of the item, see [`VPointVec`].
37/// - [`VItem::stroke_widths`]: the stroke widths of the item, see [`Width`].
38/// - [`VItem::stroke_rgbas`]: the stroke colors of the item, see [`Rgba`].
39/// - [`VItem::fill_rgbas`]: the fill colors of the item, see [`Rgba`].
40///
41/// You can construct a [`VItem`] from a list of VPoints, see [`VPointVec`]:
42///
43/// ```rust
44/// let vitem = VItem::from_vpoints(vec![
45///     dvec3(0.0, 0.0, 0.0),
46///     dvec3(1.0, 0.0, 0.0),
47///     dvec3(0.5, 1.0, 0.0),
48/// ]);
49/// ```
50#[derive(Debug, Clone, PartialEq, ranim_macros::Interpolatable)]
51pub struct VItem {
52    /// The 2d basis used for projecting the item during rendering.
53    ///
54    /// See [`Basis2d`]
55    pub basis: Basis2d,
56    /// vpoints data
57    pub vpoints: VPointVec,
58    /// stroke widths
59    pub stroke_widths: PointVec<Width>,
60    /// stroke rgbas
61    pub stroke_rgbas: PointVec<Rgba>,
62    /// fill rgbas
63    pub fill_rgbas: PointVec<Rgba>,
64}
65
66impl PointsFunc for VItem {
67    fn apply_points_func(&mut self, f: impl Fn(&mut [DVec3])) -> &mut Self {
68        self.vpoints.apply_points_func(f);
69        self
70    }
71}
72
73impl Aabb for VItem {
74    fn aabb(&self) -> [DVec3; 2] {
75        self.vpoints.aabb()
76    }
77}
78
79impl ShiftTransform for VItem {
80    fn shift(&mut self, shift: DVec3) -> &mut Self {
81        self.vpoints.shift(shift);
82        self
83    }
84}
85
86impl RotateTransform for VItem {
87    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
88        self.vpoints.rotate_on_axis(axis, angle);
89        self.basis.rotate_on_axis(axis, angle);
90        self
91    }
92}
93
94impl ScaleTransform for VItem {
95    fn scale(&mut self, scale: DVec3) -> &mut Self {
96        self.vpoints.scale(scale);
97        self
98    }
99}
100
101// impl AffineTransform for VItem {
102//     fn affine_transform_at_point(&mut self, mat: DAffine3, origin: DVec3) -> &mut Self {
103//         self.vpoints.affine_transform_at_point(mat, origin);
104//         self
105//     }
106// }
107
108/// Default stroke width
109pub use ranim_core::core_item::vitem::DEFAULT_STROKE_WIDTH;
110
111impl VItem {
112    /// Close the VItem
113    pub fn close(&mut self) -> &mut Self {
114        if self.vpoints.last() != self.vpoints.first() && !self.vpoints.is_empty() {
115            let start = self.vpoints[0];
116            let end = self.vpoints[self.vpoints.len() - 1];
117            self.extend_vpoints(&[(start + end) / 2.0, start]);
118        }
119        self
120    }
121    /// Shrink to center
122    pub fn shrink(&mut self) -> &mut Self {
123        let bb = self.aabb();
124        self.vpoints.0 = vec![bb[1]; self.vpoints.len()];
125        self
126    }
127    /// Set the vpoints of the VItem
128    pub fn set_points(&mut self, vpoints: Vec<DVec3>) {
129        self.vpoints.0 = vpoints;
130    }
131    /// Get anchor points
132    pub fn get_anchor(&self, idx: usize) -> Option<&DVec3> {
133        self.vpoints.get(idx * 2)
134    }
135    /// Set the projection of the VItem
136    pub fn with_basis(mut self, basis: Basis2d) -> Self {
137        self.basis = basis;
138        self
139    }
140    /// Set the projection of the VItem
141    pub fn set_proj(&mut self, basis: Basis2d) {
142        self.basis = basis;
143    }
144    /// Construct a [`VItem`] form vpoints
145    pub fn from_vpoints(vpoints: Vec<DVec3>) -> Self {
146        let stroke_widths = vec![DEFAULT_STROKE_WIDTH.into(); vpoints.len().div_ceil(2)];
147        let stroke_rgbas = vec![vec4(1.0, 1.0, 1.0, 1.0).into(); vpoints.len().div_ceil(2)];
148        let fill_rgbas = vec![vec4(0.0, 0.0, 0.0, 0.0).into(); vpoints.len().div_ceil(2)];
149        Self {
150            basis: Basis2d::default(),
151            vpoints: VPointVec(vpoints),
152            stroke_rgbas: stroke_rgbas.into(),
153            stroke_widths: stroke_widths.into(),
154            fill_rgbas: fill_rgbas.into(),
155        }
156    }
157    /// Extend vpoints of the VItem
158    pub fn extend_vpoints(&mut self, vpoints: &[DVec3]) {
159        self.vpoints.extend(vpoints.to_vec());
160
161        let len = self.vpoints.len();
162        self.fill_rgbas.resize_with_last(len.div_ceil(2));
163        self.stroke_rgbas.resize_with_last(len.div_ceil(2));
164        self.stroke_widths.resize_with_last(len.div_ceil(2));
165    }
166
167    pub(crate) fn get_render_points(&self) -> Vec<Vec4> {
168        self.vpoints
169            .iter()
170            .zip(self.vpoints.get_closepath_flags())
171            .map(|(p, f)| p.as_vec3().extend(f.into()))
172            .collect()
173    }
174    /// Put start and end on
175    pub fn put_start_and_end_on(&mut self, start: DVec3, end: DVec3) -> &mut Self {
176        self.vpoints.put_start_and_end_on(start, end);
177        self
178    }
179}
180
181impl From<VItem> for ranim_core::core_item::vitem::VItem {
182    fn from(value: VItem) -> Self {
183        Self {
184            origin: value.vpoints.first().unwrap().as_vec3(),
185            basis: value.basis,
186            points: value.get_render_points(),
187            fill_rgbas: value.fill_rgbas.iter().cloned().collect(),
188            stroke_rgbas: value.stroke_rgbas.iter().cloned().collect(),
189            stroke_widths: value.stroke_widths.iter().cloned().collect(),
190        }
191    }
192}
193
194impl Extract for VItem {
195    type Target = CoreItem;
196    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
197        ranim_core::core_item::vitem::VItem::from(self.clone()).extract_into(buf);
198    }
199}
200
201// MARK: Anim traits impl
202impl Alignable for VItem {
203    fn is_aligned(&self, other: &Self) -> bool {
204        self.vpoints.is_aligned(&other.vpoints)
205            && self.stroke_widths.is_aligned(&other.stroke_widths)
206            && self.stroke_rgbas.is_aligned(&other.stroke_rgbas)
207            && self.fill_rgbas.is_aligned(&other.fill_rgbas)
208    }
209    fn align_with(&mut self, other: &mut Self) {
210        self.vpoints.align_with(&mut other.vpoints);
211        let len = self.vpoints.len().div_ceil(2);
212        self.stroke_rgbas.resize_preserving_order(len);
213        other.stroke_rgbas.resize_preserving_order(len);
214        self.stroke_widths.resize_preserving_order(len);
215        other.stroke_widths.resize_preserving_order(len);
216        self.fill_rgbas.resize_preserving_order(len);
217        other.fill_rgbas.resize_preserving_order(len);
218    }
219}
220
221impl Opacity for VItem {
222    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
223        self.stroke_rgbas.set_opacity(opacity);
224        self.fill_rgbas.set_opacity(opacity);
225        self
226    }
227}
228
229impl Partial for VItem {
230    fn get_partial(&self, range: std::ops::Range<f64>) -> Self {
231        let vpoints = self.vpoints.get_partial(range.clone());
232        let stroke_rgbas = self.stroke_rgbas.get_partial(range.clone());
233        let stroke_widths = self.stroke_widths.get_partial(range.clone());
234        let fill_rgbas = self.fill_rgbas.get_partial(range.clone());
235        Self {
236            basis: self.basis,
237            vpoints,
238            stroke_widths,
239            stroke_rgbas,
240            fill_rgbas,
241        }
242    }
243    fn get_partial_closed(&self, range: std::ops::Range<f64>) -> Self {
244        let mut partial = self.get_partial(range);
245        partial.close();
246        partial
247    }
248}
249
250impl Empty for VItem {
251    fn empty() -> Self {
252        Self {
253            basis: Basis2d::default(),
254            vpoints: VPointVec(vec![DVec3::ZERO; 3]),
255            stroke_widths: vec![0.0.into(); 2].into(),
256            stroke_rgbas: vec![Vec4::ZERO.into(); 2].into(),
257            fill_rgbas: vec![Vec4::ZERO.into(); 2].into(),
258        }
259    }
260}
261
262impl FillColor for VItem {
263    fn fill_color(&self) -> AlphaColor<Srgb> {
264        self.fill_rgbas
265            .first()
266            .map(|&rgba| rgba.into())
267            .unwrap_or(css::WHITE)
268    }
269    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
270        self.fill_rgbas
271            .iter_mut()
272            .for_each(|rgba| *rgba = color.into());
273        self
274    }
275    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
276        self.fill_rgbas.set_opacity(opacity);
277        self
278    }
279}
280
281impl StrokeColor for VItem {
282    fn stroke_color(&self) -> AlphaColor<Srgb> {
283        self.stroke_rgbas
284            .first()
285            .map(|&rgba| rgba.into())
286            .unwrap_or(css::WHITE)
287    }
288    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
289        self.stroke_rgbas
290            .iter_mut()
291            .for_each(|rgba| *rgba = color.into());
292        self
293    }
294    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
295        self.stroke_rgbas.set_opacity(opacity);
296        self
297    }
298}
299
300impl StrokeWidth for VItem {
301    fn stroke_width(&self) -> f32 {
302        self.stroke_widths[0].0
303    }
304    fn apply_stroke_func(&mut self, f: impl for<'a> Fn(&'a mut [Width])) -> &mut Self {
305        f(self.stroke_widths.as_mut());
306        self
307    }
308}