rootvg_image/
primitive.rs

1use bytemuck::{Pod, Zeroable};
2use rootvg_core::math::{Angle, Point, Rect, Scale, Size, Transform, Vector};
3
4use crate::texture::RcTexture;
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct ImagePrimitive {
8    pub texture: RcTexture,
9    pub vertex: ImageVertex,
10}
11
12impl ImagePrimitive {
13    pub fn new(texture: RcTexture, position: Point) -> Self {
14        let size = texture.size();
15
16        Self {
17            texture,
18            vertex: ImageVertex {
19                position: position.into(),
20                size: [size.width as f32, size.height as f32],
21                ..Default::default()
22            },
23        }
24    }
25
26    pub fn builder(texture: RcTexture) -> ImagePrimitiveBuilder {
27        ImagePrimitiveBuilder::new(texture)
28    }
29
30    pub fn position(&self) -> Point {
31        Point::new(self.vertex.position[0], self.vertex.position[1])
32    }
33
34    pub fn set_position(&mut self, position: Point) {
35        self.vertex.position = position.into();
36    }
37}
38
39pub struct ImagePrimitiveBuilder {
40    primitive: ImagePrimitive,
41}
42
43impl ImagePrimitiveBuilder {
44    pub fn new(texture: RcTexture) -> Self {
45        Self {
46            primitive: ImagePrimitive::new(texture, Point::default()),
47        }
48    }
49
50    /// The position of the top-left corner of the image (before rotation) in logical points.
51    pub fn position(mut self, position: Point) -> Self {
52        self.primitive.vertex.position = position.into();
53        self
54    }
55
56    pub fn scale(mut self, scale_x: Scale, scale_y: Scale) -> Self {
57        self.primitive.vertex.size[0] *= scale_x.0;
58        self.primitive.vertex.size[1] *= scale_y.0;
59        self
60    }
61
62    pub fn rotation(mut self, angle: Angle, origin_normal: Point) -> Self {
63        let transform = Transform::translation(-origin_normal.x, -origin_normal.y)
64            .then_rotate(angle)
65            .then_translate(Vector::new(origin_normal.x, origin_normal.y));
66
67        self.primitive.vertex.transform = transform.to_array();
68        self.primitive.vertex.has_transform = 1;
69        self
70    }
71
72    pub fn transform(mut self, transform: Transform) -> Self {
73        self.primitive.vertex.transform = transform.to_array();
74        self.primitive.vertex.has_transform = 1;
75        self
76    }
77
78    pub fn unnormalized_uv_rect(mut self, uv_rect: Rect) -> Self {
79        let normal_uv_rect = normalized_uv_rect(
80            uv_rect,
81            Size::new(self.primitive.vertex.size[0], self.primitive.vertex.size[1]),
82        );
83
84        self.primitive.vertex.normalized_uv_pos = normal_uv_rect.origin.into();
85        self.primitive.vertex.normalized_uv_size = normal_uv_rect.size.into();
86        self
87    }
88
89    pub fn normalized_uv_rect(mut self, uv_rect: Rect) -> Self {
90        self.primitive.vertex.normalized_uv_pos = uv_rect.origin.into();
91        self.primitive.vertex.normalized_uv_size = uv_rect.size.into();
92        self
93    }
94
95    pub fn build(self) -> ImagePrimitive {
96        self.primitive
97    }
98}
99
100impl From<ImagePrimitiveBuilder> for ImagePrimitive {
101    fn from(i: ImagePrimitiveBuilder) -> Self {
102        i.build()
103    }
104}
105
106#[repr(C)]
107#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
108pub struct ImageVertex {
109    /// The position of the top-left corner of the [`Image`] (before rotation)
110    /// in logical points.
111    pub position: [f32; 2],
112
113    /// The size of the [`Image`] in logical points.
114    pub size: [f32; 2],
115
116    /// The position of the top-left uv coordinate in the texture, normalized to
117    /// therange `[0.0, 1.0]`
118    ///
119    /// By default this is set to `[0.0, 0.0]`
120    pub normalized_uv_pos: [f32; 2],
121
122    /// The size of the rect in the texture, normalized to the range `[0.0, 1.0]`
123    ///
124    /// By default this is set to `[1.0, 1.0]`
125    pub normalized_uv_size: [f32; 2],
126
127    /// A 2d transform represented by a column-major 3 by 3 matrix, compressed down
128    /// to 3 by 2.
129    ///
130    /// Note that `size` is not included in the `transform`.
131    pub transform: [f32; 6],
132
133    /// Whether or not to apply the `transform` matrix. This is used to optimize
134    /// images with no transformations.
135    ///
136    /// Note that `size` is not included in the `transform`.
137    ///
138    /// By default this is set to `0` (false).
139    pub has_transform: u32,
140}
141
142impl Default for ImageVertex {
143    fn default() -> Self {
144        Self {
145            position: [0.0; 2],
146            size: [0.0; 2],
147            normalized_uv_pos: [0.0; 2],
148            normalized_uv_size: [1.0; 2],
149            transform: [0.0; 6],
150            has_transform: 0,
151        }
152    }
153}
154
155fn normalized_uv_rect(uv_rect: Rect, texture_size: Size) -> Rect {
156    Rect::new(
157        Point::new(
158            uv_rect.origin.x / texture_size.width,
159            uv_rect.origin.y / texture_size.height,
160        ),
161        Size::new(
162            uv_rect.size.width / texture_size.width,
163            uv_rect.size.height / texture_size.height,
164        ),
165    )
166}