Skip to main content

euv_engine/sprite/
struct.rs

1use super::*;
2
3/// Defines a single frame in a sprite animation.
4#[derive(Clone, Copy, Data, Debug, Default, New, PartialEq, PartialOrd)]
5pub struct SpriteFrame {
6    /// The source rectangle within the sprite sheet image.
7    #[get(type(copy))]
8    #[set(pub(crate))]
9    #[get_mut(pub(crate))]
10    pub source: Rect,
11    /// The duration this frame should be displayed, in seconds.
12    #[get(type(copy))]
13    #[get_mut(pub(crate))]
14    #[set(pub(crate))]
15    pub duration: f64,
16}
17
18/// Defines a sprite sheet with uniform frame grid dimensions.
19#[derive(Clone, Data, Debug, New, PartialEq)]
20pub struct SpriteSheet {
21    /// The source image element loaded from an asset.
22    #[get_mut(pub(crate))]
23    #[set(pub(crate))]
24    #[get(pub(crate))]
25    pub(crate) image: HtmlImageElement,
26    /// The width of each individual frame in pixels.
27    #[get(pub(crate), type(copy))]
28    #[get_mut(pub(crate))]
29    #[set(pub(crate))]
30    pub(crate) frame_width: f64,
31    /// The height of each individual frame in pixels.
32    #[get(pub(crate), type(copy))]
33    #[get_mut(pub(crate))]
34    #[set(pub(crate))]
35    pub(crate) frame_height: f64,
36    /// The number of columns in the sprite sheet grid.
37    #[get(type(copy))]
38    #[get_mut(pub(crate))]
39    #[set(pub(crate))]
40    pub columns: u32,
41    /// The number of rows in the sprite sheet grid.
42    #[get(type(copy))]
43    #[get_mut(pub(crate))]
44    #[set(pub(crate))]
45    pub rows: u32,
46}
47
48/// A named sequence of frames that form an animation.
49#[derive(Clone, Data, Debug, New, PartialEq)]
50pub struct SpriteAnimation {
51    /// The name identifying this animation (e.g., `"idle"`, `"walk"`).
52    #[get_mut(pub(crate))]
53    #[set(pub(crate))]
54    pub name: String,
55    /// The ordered list of frames in this animation.
56    #[set(pub(crate))]
57    #[get_mut(pub(crate))]
58    pub frames: Vec<SpriteFrame>,
59    /// The playback mode (loop, once, ping-pong).
60    #[get(type(copy))]
61    #[get_mut(pub(crate))]
62    #[set(pub(crate))]
63    pub mode: AnimationMode,
64}
65
66/// Manages the playback state of sprite animations.
67#[derive(Clone, Data, Debug, New, PartialEq)]
68pub struct Animator {
69    /// The currently active animation, if any.
70    ///
71    /// `get_current_animation` is public: `engine/tests/animator` inspects the
72    /// active clip from outside this crate. `get_mut` / `set` stay
73    /// crate-internal — clip changes go through the playback API.
74    #[get_mut(pub(crate))]
75    #[get(type(clone))]
76    #[set(pub(crate))]
77    #[new(skip)]
78    pub current_animation: Option<SpriteAnimation>,
79    /// The index of the current frame being displayed.
80    #[get(type(copy))]
81    #[get_mut(pub(crate))]
82    #[set(pub(crate))]
83    #[new(skip)]
84    pub current_frame_index: usize,
85    /// The elapsed time within the current frame, in seconds.
86    #[get(type(copy))]
87    #[get_mut(pub(crate))]
88    #[set(pub(crate))]
89    #[new(skip)]
90    pub elapsed_time: f64,
91    /// The current playback state.
92    #[get(type(copy))]
93    #[get_mut(pub(crate))]
94    #[set(pub(crate))]
95    pub state: AnimationState,
96    /// The direction of playback (1 = forward, -1 = backward) for ping-pong mode.
97    #[get(type(copy))]
98    #[get_mut(pub(crate))]
99    #[set(pub(crate))]
100    pub direction: i32,
101    /// Whether to flip the sprite horizontally when rendering.
102    #[get(type(copy), pub(crate))]
103    #[get_mut(pub(crate))]
104    #[new(skip)]
105    #[set(pub(crate))]
106    pub(crate) flip_x: bool,
107    /// Whether to flip the sprite vertically when rendering.
108    #[get(type(copy))]
109    #[get_mut(pub(crate))]
110    #[new(skip)]
111    #[set(pub(crate))]
112    pub flip_y: bool,
113}
114
115/// The nine source or destination sub-rectangles produced by a nine-slice split.
116///
117/// Stored as a three-by-three grid in reading order — row 0 is the top
118/// (`TOP_LEFT`, `TOP`, `TOP_RIGHT`), row 1 the middle (`LEFT`, `CENTER`,
119/// `RIGHT`), row 2 the bottom (`BOTTOM_LEFT`, `BOTTOM`, `BOTTOM_RIGHT`).
120/// The named accessors read through those indices, so callers get
121/// self-documenting access while the storage stays a single array that
122/// can be iterated directly when emitting the nine `drawImage` calls.
123#[derive(Clone, Copy, Data, Debug, New, PartialEq)]
124pub struct NineSliceRects {
125    /// The nine sub-rectangles in reading order, row-major.
126    #[get(type(copy))]
127    #[get_mut(pub(crate))]
128    #[set(pub(crate))]
129    pub grid: [[Rect; 3]; 3],
130}
131
132/// The four per-edge border insets that carve an image into nine patches.
133///
134/// This is the pure geometry half of a nine-slice: it holds no image
135/// handle, so the split can be computed and tested without a DOM. Insets
136/// are clamped so `left + right` never exceeds the source width and
137/// `top + bottom` never exceeds the source height; a degenerate center
138/// patch collapses to zero size rather than inverting.
139#[derive(Clone, Copy, Data, Debug, Default, New, PartialEq)]
140pub struct NineSliceInsets {
141    /// The left border inset in source pixels.
142    #[get(type(copy))]
143    #[get_mut(pub(crate))]
144    #[set(pub(crate))]
145    pub left: f64,
146    /// The right border inset in source pixels.
147    #[get(type(copy))]
148    #[get_mut(pub(crate))]
149    #[set(pub(crate))]
150    pub right: f64,
151    /// The top border inset in source pixels.
152    #[get(type(copy))]
153    #[get_mut(pub(crate))]
154    #[set(pub(crate))]
155    pub top: f64,
156    /// The bottom border inset in source pixels.
157    #[get(type(copy))]
158    #[get_mut(pub(crate))]
159    #[set(pub(crate))]
160    pub bottom: f64,
161}
162
163/// A source image paired with the insets that split it into nine patches.
164///
165/// Corners keep their natural size, the four edge patches stretch along
166/// one axis only, and the center patch stretches along both — so a
167/// bordered panel can be resized to any destination size without
168/// distorting its corners. The geometry itself lives on
169/// [`NineSliceInsets`]; this type only carries the image handle so it
170/// can be stored inside a deferred `DrawCommand`.
171#[derive(Clone, Data, Debug, New, PartialEq)]
172pub struct NineSlice {
173    /// The source image element containing the nine patches.
174    #[get(pub(crate), type(clone))]
175    #[get_mut(pub(crate))]
176    #[set(pub(crate))]
177    pub(crate) image: HtmlImageElement,
178    /// The per-edge border insets defining the split.
179    #[get(pub(crate), type(copy))]
180    #[get_mut(pub(crate))]
181    #[set(pub(crate))]
182    pub(crate) insets: NineSliceInsets,
183}
184
185/// Normalized texture coordinates for one sub-rectangle of an atlas image.
186///
187/// Values are fractions of the atlas dimensions in the range 0.0 to 1.0,
188/// with `(0.0, 0.0)` at the image's top-left corner. The V axis runs
189/// downward to match the top-left origin used by `Rect`.
190#[derive(Clone, Copy, Data, Debug, New, PartialEq)]
191pub struct UvRect {
192    /// The normalized left edge (U at the rectangle's left).
193    #[get(type(copy))]
194    #[get_mut(pub(crate))]
195    #[set(pub(crate))]
196    pub u0: f64,
197    /// The normalized top edge (V at the rectangle's top).
198    #[get(type(copy))]
199    #[get_mut(pub(crate))]
200    #[set(pub(crate))]
201    pub v0: f64,
202    /// The normalized right edge (U at the rectangle's right).
203    #[get(type(copy))]
204    #[get_mut(pub(crate))]
205    #[set(pub(crate))]
206    pub u1: f64,
207    /// The normalized bottom edge (V at the rectangle's bottom).
208    #[get(type(copy))]
209    #[get_mut(pub(crate))]
210    #[set(pub(crate))]
211    pub v1: f64,
212}
213
214/// The name-to-rectangle index of a sprite atlas, without the image handle.
215///
216/// Separating the index from the image keeps the lookup half of an atlas
217/// free of any DOM dependency, so region bookkeeping can be exercised
218/// without a live `HtmlImageElement`.
219#[derive(Clone, Data, Debug, Default, New, PartialEq)]
220pub struct AtlasRegions {
221    /// The named source rectangles keyed by sprite name.
222    #[get_mut(pub(crate))]
223    #[get(pub(crate))]
224    #[set(pub(crate))]
225    pub(crate) regions: HashMap<String, Rect>,
226}
227
228/// A named collection of sprite regions packed into one shared image.
229///
230/// Packing many sprites into a single texture keeps the number of canvas
231/// draw calls (and image decode stalls) low. Each entry stores a source
232/// rectangle in pixels; [`SpriteAtlas::uv`] converts it to normalized
233/// coordinates for GPU samplers, and [`SpriteAtlas::draw`] blits it
234/// directly through the 2D canvas API.
235#[derive(Clone, Data, Debug, New, PartialEq)]
236pub struct SpriteAtlas {
237    /// The shared image element holding every packed sprite.
238    #[get(pub(crate), type(clone))]
239    #[get_mut(pub(crate))]
240    #[set(pub(crate))]
241    pub(crate) image: HtmlImageElement,
242    /// The named source rectangles keyed by sprite name.
243    #[get(pub(crate), type(clone))]
244    #[get_mut(pub(crate))]
245    #[set(pub(crate))]
246    pub(crate) regions: AtlasRegions,
247}