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