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}