good_web_game/graphics/drawparam.rs
1use crate::graphics::{Color, Rect};
2use cgmath::Matrix;
3
4/// A struct that represents where to put a `Drawable`.
5///
6/// This can either be a set of individual components, or
7/// a single `Matrix4` transform.
8#[derive(Debug, Copy, Clone, PartialEq)]
9pub enum Transform {
10 /// Transform made of individual values
11 Values {
12 /// The position to draw the graphic expressed as a `Point2`.
13 dest: mint::Point2<f32>,
14 /// The orientation of the graphic in radians.
15 rotation: f32,
16 /// The x/y scale factors expressed as a `Vector2`.
17 scale: mint::Vector2<f32>,
18 /// An offset from the center for transform operations like scale/rotation,
19 /// with `0,0` meaning the origin and `1,1` meaning the opposite corner from the origin.
20 /// By default these operations are done from the top-left corner, so to rotate something
21 /// from the center specify `Point2::new(0.5, 0.5)` here.
22 offset: mint::Point2<f32>,
23 },
24 /// Transform made of an arbitrary matrix.
25 ///
26 /// It should represent the final model matrix of the given drawable. This is useful for
27 /// situations where, for example, you build your own hierarchy system, where you calculate
28 /// matrices of each hierarchy item and store a calculated world-space model matrix of an item.
29 /// This lets you implement transform stacks, skeletal animations, etc.
30 Matrix(mint::ColumnMatrix4<f32>),
31}
32
33impl Default for Transform {
34 fn default() -> Self {
35 Transform::Values {
36 dest: mint::Point2 { x: 0.0, y: 0.0 },
37 rotation: 0.0,
38 scale: mint::Vector2 { x: 1.0, y: 1.0 },
39 offset: mint::Point2 { x: 0.0, y: 0.0 },
40 }
41 }
42}
43
44impl Transform {
45 /// Crunches the transform down to a single matrix, if it's not one already.
46 pub fn to_matrix(self) -> Self {
47 Transform::Matrix(self.to_bare_matrix())
48 }
49
50 /// Same as `to_matrix()` but just returns a bare `mint` matrix.
51 pub fn to_bare_matrix(self) -> mint::ColumnMatrix4<f32> {
52 match self {
53 Transform::Matrix(m) => m,
54 Transform::Values {
55 dest,
56 rotation,
57 scale,
58 offset,
59 } => {
60 // Calculate a matrix equivalent to doing this:
61 // type Vec3 = na::Vector3<f32>;
62 // let o = offset;
63 // let translate = na::Matrix4::new_translation(&Vec3::new(dest.x, dest.y, 0.0));
64 // let offset = na::Matrix4::new_translation(&Vec3::new(offset.x, offset.y, 0.0));
65 // let offset_inverse =
66 // na::Matrix4::new_translation(&Vec3::new(-o.x, -o.y, 0.0));
67 // let axis_angle = Vec3::z() * *rotation;
68 // let rotation = na::Matrix4::new_rotation(axis_angle);
69 // let scale = na::Matrix4::new_nonuniform_scaling(&Vec3::new(scale.x, scale.y, 1.0));
70 // translate * rotation * scale * offset_inverse
71 //
72 // Doing the bits manually is faster though, or at least was last I checked.
73 let (sinr, cosr) = rotation.sin_cos();
74 let m00 = cosr * scale.x;
75 let m01 = -sinr * scale.y;
76 let m10 = sinr * scale.x;
77 let m11 = cosr * scale.y;
78 let m03 = offset.x * (-m00) - offset.y * m01 + dest.x;
79 let m13 = offset.y * (-m11) - offset.x * m10 + dest.y;
80 // Welp, this transpose fixes some bug that makes nothing draw,
81 // that was introduced in commit 2c6b3cc03f34fb240f4246f5a68c75bd85b60eae.
82 // The best part is, I don't know if this code is wrong, or whether there's
83 // some reversed matrix multiply or such somewhere else that this cancel
84 // out. Probably the former though.
85 cgmath::Matrix4::new(
86 m00, m01, 0.0, m03, // oh rustfmt you so fine
87 m10, m11, 0.0, m13, // you so fine you blow my mind
88 0.0, 0.0, 1.0, 0.0, // but leave my matrix formatting alone
89 0.0, 0.0, 0.0, 1.0, // plz
90 )
91 .transpose()
92 .into()
93 }
94 }
95 }
96}
97
98#[derive(Debug, Copy, Clone, PartialEq)]
99pub struct DrawParam {
100 /// A portion of the drawable to clip, as a fraction of the whole image.
101 /// Defaults to the whole image `(0,0 to 1,1)` if omitted.
102 pub src: Rect,
103 /// A color to draw the target with.
104 /// Default: white.
105 pub color: Color,
106 /// Where to put the `Drawable`.
107 pub trans: Transform,
108}
109
110impl Default for DrawParam {
111 fn default() -> Self {
112 DrawParam {
113 src: Rect::one(),
114 color: Color::WHITE,
115 trans: Transform::default(),
116 }
117 }
118}
119
120impl DrawParam {
121 /// Create a new DrawParam with default values.
122 pub fn new() -> Self {
123 Self::default()
124 }
125
126 /// Set the source rect
127 pub fn src(mut self, src: Rect) -> Self {
128 self.src = src;
129 self
130 }
131
132 /// Set the dest point
133 pub fn dest<P>(mut self, dest_: P) -> Self
134 where
135 P: Into<mint::Point2<f32>>,
136 {
137 if let Transform::Values { ref mut dest, .. } = self.trans {
138 let p: mint::Point2<f32> = dest_.into();
139 *dest = p;
140 self
141 } else {
142 panic!("Cannot set values for a DrawParam matrix")
143 }
144 }
145
146 /// Set the drawable color. This will be blended with whatever
147 /// color the drawn object already is.
148 pub fn color(mut self, color: Color) -> Self {
149 self.color = color;
150 self
151 }
152
153 /// Set the rotation of the drawable.
154 pub fn rotation(mut self, rot: f32) -> Self {
155 if let Transform::Values {
156 ref mut rotation, ..
157 } = self.trans
158 {
159 *rotation = rot;
160 self
161 } else {
162 panic!("Cannot set values for a DrawParam matrix")
163 }
164 }
165
166 /// Set the scaling factors of the drawable.
167 pub fn scale<V>(mut self, scale_: V) -> Self
168 where
169 V: Into<mint::Vector2<f32>>,
170 {
171 if let Transform::Values { ref mut scale, .. } = self.trans {
172 let p: mint::Vector2<f32> = scale_.into();
173 *scale = p;
174 self
175 } else {
176 panic!("Cannot set values for a DrawParam matrix")
177 }
178 }
179
180 /// Set the transformation offset of the drawable.
181 pub fn offset<P>(mut self, offset_: P) -> Self
182 where
183 P: Into<mint::Point2<f32>>,
184 {
185 if let Transform::Values { ref mut offset, .. } = self.trans {
186 let p: mint::Point2<f32> = offset_.into();
187 *offset = p;
188 self
189 } else {
190 panic!("Cannot set values for a DrawParam matrix")
191 }
192 }
193
194 /// Set the transformation matrix of the drawable.
195 pub fn transform<M>(mut self, transform: M) -> Self
196 where
197 M: Into<mint::ColumnMatrix4<f32>>,
198 {
199 self.trans = Transform::Matrix(transform.into());
200 self
201 }
202}
203
204/// Create a `DrawParam` from a location.
205/// Note that this takes a single-element tuple.
206/// It's a little weird but keeps the trait implementations
207/// from clashing.
208impl<P> From<(P,)> for DrawParam
209where
210 P: Into<mint::Point2<f32>>,
211{
212 fn from(location: (P,)) -> Self {
213 DrawParam::new().dest(location.0)
214 }
215}
216
217/// Create a `DrawParam` from a location and color
218impl<P> From<(P, Color)> for DrawParam
219where
220 P: Into<mint::Point2<f32>>,
221{
222 fn from((location, color): (P, Color)) -> Self {
223 DrawParam::new().dest(location).color(color)
224 }
225}
226
227/// Create a `DrawParam` from a location, rotation and color
228impl<P> From<(P, f32, Color)> for DrawParam
229where
230 P: Into<mint::Point2<f32>>,
231{
232 fn from((location, rotation, color): (P, f32, Color)) -> Self {
233 DrawParam::new()
234 .dest(location)
235 .rotation(rotation)
236 .color(color)
237 }
238}
239
240/// Create a `DrawParam` from a location, rotation, offset and color
241impl<P> From<(P, f32, P, Color)> for DrawParam
242where
243 P: Into<mint::Point2<f32>>,
244{
245 fn from((location, rotation, offset, color): (P, f32, P, Color)) -> Self {
246 DrawParam::new()
247 .dest(location)
248 .rotation(rotation)
249 .offset(offset)
250 .color(color)
251 }
252}
253
254/// Create a `DrawParam` from a location, rotation, offset, scale and color
255impl<P, V> From<(P, f32, P, V, Color)> for DrawParam
256where
257 P: Into<mint::Point2<f32>>,
258 V: Into<mint::Vector2<f32>>,
259{
260 fn from((location, rotation, offset, scale, color): (P, f32, P, V, Color)) -> Self {
261 DrawParam::new()
262 .dest(location)
263 .rotation(rotation)
264 .offset(offset)
265 .scale(scale)
266 .color(color)
267 }
268}