good_web_game/graphics/
types.rs

1pub type Point2 = cgmath::Point2<f32>;
2pub type Vector2 = cgmath::Vector2<f32>;
3
4use crate::graphics::DrawParam;
5use cgmath::{Matrix4, Transform, Vector4};
6
7#[derive(Debug, Clone)]
8#[repr(C)]
9pub(crate) struct InstanceAttributes {
10    pub source: Vector4<f32>,
11    pub color: Vector4<f32>,
12    pub model: Matrix4<f32>,
13}
14
15impl Default for InstanceAttributes {
16    fn default() -> InstanceAttributes {
17        InstanceAttributes {
18            source: Vector4::new(0., 0., 0., 0.),
19            color: Vector4::new(0., 0., 0., 0.),
20            model: Matrix4::one(),
21        }
22    }
23}
24
25impl From<&DrawParam> for InstanceAttributes {
26    fn from(param: &DrawParam) -> Self {
27        InstanceAttributes {
28            model: param.trans.to_bare_matrix().into(),
29            source: Vector4::new(param.src.x, param.src.y, param.src.w, param.src.h),
30            color: Vector4::new(param.color.r, param.color.g, param.color.b, param.color.a),
31        }
32    }
33}
34
35/// A simple 2D rectangle.
36///
37/// The origin of the rectangle is at the top-left,
38/// with x increasing to the right and y increasing down.
39#[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
40pub struct Rect {
41    /// X coordinate of the left edge of the rect.
42    pub x: f32,
43    /// Y coordinate of the top edge of the rect.
44    pub y: f32,
45    /// Total width of the rect
46    pub w: f32,
47    /// Total height of the rect.
48    pub h: f32,
49}
50
51impl Rect {
52    /// Create a new `Rect`.
53    pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
54        Rect { x, y, w, h }
55    }
56
57    /// Creates a new `Rect` a la Love2D's `love.graphics.newQuad`,
58    /// as a fraction of the reference rect's size.
59    pub fn fraction(x: f32, y: f32, w: f32, h: f32, reference: &Rect) -> Rect {
60        Rect {
61            x: x / reference.w,
62            y: y / reference.h,
63            w: w / reference.w,
64            h: h / reference.h,
65        }
66    }
67
68    /// Create a new rect from `i32` coordinates.
69    pub const fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
70        Rect {
71            x: x as f32,
72            y: y as f32,
73            w: w as f32,
74            h: h as f32,
75        }
76    }
77
78    /// Create a new `Rect` with all values zero.
79    pub const fn zero() -> Self {
80        Self::new(0.0, 0.0, 0.0, 0.0)
81    }
82
83    /// Creates a new `Rect` at `0,0` with width and height 1.
84    pub const fn one() -> Self {
85        Self::new(0.0, 0.0, 1.0, 1.0)
86    }
87
88    /// Gets the `Rect`'s x and y coordinates as a `Point2`.
89    pub fn point(&self) -> mint::Point2<f32> {
90        mint::Point2 {
91            x: self.x,
92            y: self.y,
93        }
94    }
95
96    /// Returns the center of the `Rect` as a `Point2`.
97    pub fn center(&self) -> mint::Point2<f32> {
98        mint::Point2 {
99            x: self.x + self.w / 2.,
100            y: self.y + self.h / 2.,
101        }
102    }
103
104    /// Returns the left edge of the `Rect`
105    pub fn left(&self) -> f32 {
106        self.x
107    }
108
109    /// Returns the right edge of the `Rect`
110    pub fn right(&self) -> f32 {
111        self.x + self.w
112    }
113
114    /// Returns the top edge of the `Rect`
115    pub fn top(&self) -> f32 {
116        self.y
117    }
118
119    /// Returns the bottom edge of the `Rect`
120    pub fn bottom(&self) -> f32 {
121        self.y + self.h
122    }
123
124    /// Checks whether the `Rect` contains a `Point`
125    pub fn contains<P: Into<mint::Point2<f32>>>(&self, point: P) -> bool {
126        let point: mint::Point2<_> = point.into();
127
128        point.x >= self.left()
129            && point.x <= self.right()
130            && point.y <= self.bottom()
131            && point.y >= self.top()
132    }
133
134    /// Checks whether the `Rect` overlaps another `Rect`
135    pub fn overlaps(&self, other: &Rect) -> bool {
136        self.left() <= other.right()
137            && self.right() >= other.left()
138            && self.top() <= other.bottom()
139            && self.bottom() >= other.top()
140    }
141
142    /// Translates the `Rect` by an offset of (x, y)
143    pub fn translate<V: Into<mint::Vector2<f32>>>(&mut self, offset: V) {
144        let offset: mint::Vector2<f32> = offset.into();
145
146        self.x += offset.x;
147        self.y += offset.y;
148    }
149
150    /// Moves the `Rect`'s origin to (x, y)
151    pub fn move_to<P: Into<mint::Point2<f32>>>(&mut self, destination: P) {
152        let destination = destination.into();
153
154        self.x = destination.x;
155        self.y = destination.y;
156    }
157
158    /// Scales the `Rect` by a factor of (sx, sy),
159    /// growing towards the bottom-left
160    pub fn scale(&mut self, sx: f32, sy: f32) {
161        self.w *= sx;
162        self.h *= sy;
163    }
164
165    /// Returns a new `Rect` that includes all points of these two `Rect`s.
166    pub fn combine_with(self, other: Rect) -> Rect {
167        let x = f32::min(self.x, other.x);
168        let y = f32::min(self.y, other.y);
169        let w = f32::max(self.right(), other.right()) - x;
170        let h = f32::max(self.bottom(), other.bottom()) - y;
171        Rect { x, y, w, h }
172    }
173
174    /// Calculated the new Rect around the rotated one.
175    pub fn rotate(&mut self, rotation: f32) {
176        use cgmath::{Basis2, Rotation, Rotation2};
177
178        let rotation: Basis2<f32> = Rotation2::from_angle(cgmath::Rad(rotation));
179        let x0 = self.x;
180        let y0 = self.y;
181        let x1 = self.right();
182        let y1 = self.bottom();
183        let points = [
184            rotation.rotate_point(cgmath::Point2::new(x0, y0)),
185            rotation.rotate_point(cgmath::Point2::new(x0, y1)),
186            rotation.rotate_point(cgmath::Point2::new(x1, y0)),
187            rotation.rotate_point(cgmath::Point2::new(x1, y1)),
188        ];
189        let p0 = points[0];
190        let mut x_max = p0.x;
191        let mut x_min = p0.x;
192        let mut y_max = p0.y;
193        let mut y_min = p0.y;
194        for p in &points {
195            x_max = f32::max(x_max, p.x);
196            x_min = f32::min(x_min, p.x);
197            y_max = f32::max(y_max, p.y);
198            y_min = f32::min(y_min, p.y);
199        }
200        *self = Rect {
201            w: x_max - x_min,
202            h: y_max - y_min,
203            x: x_min,
204            y: y_min,
205        }
206    }
207}
208
209impl approx::AbsDiffEq for Rect {
210    type Epsilon = f32;
211
212    fn default_epsilon() -> Self::Epsilon {
213        f32::default_epsilon()
214    }
215
216    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
217        f32::abs_diff_eq(&self.x, &other.x, epsilon)
218            && f32::abs_diff_eq(&self.y, &other.y, epsilon)
219            && f32::abs_diff_eq(&self.w, &other.w, epsilon)
220            && f32::abs_diff_eq(&self.h, &other.h, epsilon)
221    }
222}
223
224impl approx::RelativeEq for Rect {
225    fn default_max_relative() -> Self::Epsilon {
226        f32::default_max_relative()
227    }
228
229    fn relative_eq(
230        &self,
231        other: &Self,
232        epsilon: Self::Epsilon,
233        max_relative: Self::Epsilon,
234    ) -> bool {
235        f32::relative_eq(&self.x, &other.x, epsilon, max_relative)
236            && f32::relative_eq(&self.y, &other.y, epsilon, max_relative)
237            && f32::relative_eq(&self.w, &other.w, epsilon, max_relative)
238            && f32::relative_eq(&self.h, &other.h, epsilon, max_relative)
239    }
240}
241
242impl From<[f32; 4]> for Rect {
243    fn from(val: [f32; 4]) -> Self {
244        Rect::new(val[0], val[1], val[2], val[3])
245    }
246}
247
248impl From<Rect> for [f32; 4] {
249    fn from(val: Rect) -> Self {
250        [val.x, val.y, val.w, val.h]
251    }
252}
253
254/// A RGBA color in the `sRGB` color space represented as `f32`'s in the range `[0.0-1.0]`
255///
256/// For convenience, [`WHITE`](constant.WHITE.html) and [`BLACK`](constant.BLACK.html) are provided.
257#[derive(Copy, Clone, PartialEq, Debug)]
258pub struct Color {
259    /// Red component
260    pub r: f32,
261    /// Green component
262    pub g: f32,
263    /// Blue component
264    pub b: f32,
265    /// Alpha component
266    pub a: f32,
267}
268
269impl Color {
270    /// White (#FFFFFFFF)
271    pub const WHITE: Color = Color {
272        r: 1.0,
273        g: 1.0,
274        b: 1.0,
275        a: 1.0,
276    };
277
278    /// Black (#000000FF)
279    pub const BLACK: Color = Color {
280        r: 0.0,
281        g: 0.0,
282        b: 0.0,
283        a: 1.0,
284    };
285
286    /// Red
287    pub const RED: Color = Color {
288        r: 1.0,
289        g: 0.0,
290        b: 0.0,
291        a: 1.0,
292    };
293
294    /// Green
295    pub const GREEN: Color = Color {
296        r: 0.0,
297        g: 1.0,
298        b: 0.0,
299        a: 1.0,
300    };
301
302    /// Blue
303    pub const BLUE: Color = Color {
304        r: 0.0,
305        g: 0.0,
306        b: 1.0,
307        a: 1.0,
308    };
309
310    /// Cyan
311    pub const CYAN: Color = Color {
312        r: 0.0,
313        g: 1.0,
314        b: 1.0,
315        a: 1.0,
316    };
317
318    /// Magenta
319    pub const MAGENTA: Color = Color {
320        r: 1.0,
321        g: 0.0,
322        b: 1.0,
323        a: 1.0,
324    };
325
326    /// Yellow
327    pub const YELLOW: Color = Color {
328        r: 1.0,
329        g: 1.0,
330        b: 0.0,
331        a: 1.0,
332    };
333
334    /// Create a new `Color` from four `f32`'s in the range `[0.0-1.0]`
335    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
336        Color { r, g, b, a }
337    }
338
339    /// Create a new `Color` from four `u8`'s in the range `[0-255]`
340    pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
341        Color::from((r, g, b, a))
342    }
343
344    /// Create a new `Color` from three u8's in the range `[0-255]`,
345    /// with the alpha component fixed to 255 (opaque)
346    pub fn from_rgb(r: u8, g: u8, b: u8) -> Color {
347        Color::from((r, g, b))
348    }
349
350    /// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s
351    /// components.
352    pub fn to_rgba(self) -> (u8, u8, u8, u8) {
353        self.into()
354    }
355
356    /// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s
357    /// components.
358    pub fn to_rgb(self) -> (u8, u8, u8) {
359        self.into()
360    }
361
362    /// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color`
363    pub fn from_rgba_u32(c: u32) -> Color {
364        let c = c.to_be_bytes();
365
366        Color::from((c[0], c[1], c[2], c[3]))
367    }
368
369    /// Convert a packed `u32` containing `0x00RRGGBB` into a `Color`.
370    /// This lets you do things like `Color::from_rgb_u32(0xCD09AA)` easily if you want.
371    pub fn from_rgb_u32(c: u32) -> Color {
372        let c = c.to_be_bytes();
373
374        Color::from((c[1], c[2], c[3]))
375    }
376
377    /// Convert a `Color` into a packed `u32`, containing `0xRRGGBBAA` as bytes.
378    pub fn to_rgba_u32(self) -> u32 {
379        let (r, g, b, a): (u8, u8, u8, u8) = self.into();
380
381        u32::from_be_bytes([r, g, b, a])
382    }
383
384    /// Convert a `Color` into a packed `u32`, containing `0x00RRGGBB` as bytes.
385    pub fn to_rgb_u32(self) -> u32 {
386        let (r, g, b, _a): (u8, u8, u8, u8) = self.into();
387
388        u32::from_be_bytes([0, r, g, b])
389    }
390}
391
392impl From<(u8, u8, u8, u8)> for Color {
393    /// Convert a `(R, G, B, A)` tuple of `u8`'s in the range `[0-255]` into a `Color`
394    fn from(val: (u8, u8, u8, u8)) -> Self {
395        let (r, g, b, a) = val;
396        let rf = (f32::from(r)) / 255.0;
397        let gf = (f32::from(g)) / 255.0;
398        let bf = (f32::from(b)) / 255.0;
399        let af = (f32::from(a)) / 255.0;
400        Color::new(rf, gf, bf, af)
401    }
402}
403
404impl From<(u8, u8, u8)> for Color {
405    /// Convert a `(R, G, B)` tuple of `u8`'s in the range `[0-255]` into a `Color`,
406    /// with a value of 255 for the alpha element (i.e., no transparency.)
407    fn from(val: (u8, u8, u8)) -> Self {
408        let (r, g, b) = val;
409        Color::from((r, g, b, 255))
410    }
411}
412
413impl From<[f32; 4]> for Color {
414    /// Turns an `[R, G, B, A] array of `f32`'s into a `Color` with no format changes.
415    /// All inputs should be in the range `[0.0-1.0]`.
416    fn from(val: [f32; 4]) -> Self {
417        Color::new(val[0], val[1], val[2], val[3])
418    }
419}
420
421impl From<(f32, f32, f32)> for Color {
422    /// Convert a `(R, G, B)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color`,
423    /// with a value of 1.0 to for the alpha element (ie, no transparency.)
424    fn from(val: (f32, f32, f32)) -> Self {
425        let (r, g, b) = val;
426        Color::new(r, g, b, 1.0)
427    }
428}
429
430impl From<(f32, f32, f32, f32)> for Color {
431    /// Convert a `(R, G, B, A)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color`
432    fn from(val: (f32, f32, f32, f32)) -> Self {
433        let (r, g, b, a) = val;
434        Color::new(r, g, b, a)
435    }
436}
437
438impl From<Color> for (u8, u8, u8, u8) {
439    /// Convert a `Color` into a `(R, G, B, A)` tuple of `u8`'s in the range of `[0-255]`.
440    fn from(color: Color) -> Self {
441        let r = (color.r * 255.0) as u8;
442        let g = (color.g * 255.0) as u8;
443        let b = (color.b * 255.0) as u8;
444        let a = (color.a * 255.0) as u8;
445        (r, g, b, a)
446    }
447}
448
449impl From<Color> for (u8, u8, u8) {
450    /// Convert a `Color` into a `(R, G, B)` tuple of `u8`'s in the range of `[0-255]`,
451    /// ignoring the alpha term.
452    fn from(color: Color) -> Self {
453        let (r, g, b, _) = color.into();
454        (r, g, b)
455    }
456}
457
458impl From<Color> for [f32; 4] {
459    /// Convert a `Color` into an `[R, G, B, A]` array of `f32`'s in the range of `[0.0-1.0]`.
460    fn from(color: Color) -> Self {
461        [color.r, color.g, color.b, color.a]
462    }
463}
464
465#[allow(clippy::from_over_into)]
466impl Into<String> for Color {
467    fn into(self) -> String {
468        format!(
469            "#{:02x}{:02x}{:02x}{:02x}",
470            (self.r * 255.) as i32,
471            (self.g * 255.) as i32,
472            (self.b * 255.) as i32,
473            (self.a * 255.) as i32
474        )
475    }
476}
477
478/// A RGBA color in the *linear* color space,
479/// suitable for shoving into a shader.
480#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
481pub(crate) struct LinearColor {
482    /// Red component
483    pub r: f32,
484    /// Green component
485    pub g: f32,
486    /// Blue component
487    pub b: f32,
488    /// Alpha component
489    pub a: f32,
490}
491
492impl From<Color> for LinearColor {
493    /// Convert an (sRGB) Color into a linear color,
494    /// per https://en.wikipedia.org/wiki/Srgb#The_reverse_transformation
495    fn from(c: Color) -> Self {
496        fn f(component: f32) -> f32 {
497            let a = 0.055;
498            if component <= 0.04045 {
499                component / 12.92
500            } else {
501                ((component + a) / (1.0 + a)).powf(2.4)
502            }
503        }
504        LinearColor {
505            r: f(c.r),
506            g: f(c.g),
507            b: f(c.b),
508            a: c.a,
509        }
510    }
511}
512
513impl From<LinearColor> for Color {
514    fn from(c: LinearColor) -> Self {
515        fn f(component: f32) -> f32 {
516            let a = 0.055;
517            if component <= 0.003_130_8 {
518                component * 12.92
519            } else {
520                (1.0 + a) * component.powf(1.0 / 2.4)
521            }
522        }
523        Color {
524            r: f(c.r),
525            g: f(c.g),
526            b: f(c.b),
527            a: c.a,
528        }
529    }
530}
531
532impl From<LinearColor> for [f32; 4] {
533    fn from(color: LinearColor) -> Self {
534        [color.r, color.g, color.b, color.a]
535    }
536}
537
538#[cfg(feature = "mesh")]
539mod draw_mode {
540    use crate::graphics::{FillOptions, StrokeOptions};
541
542    /// Specifies whether a shape should be drawn
543    /// filled or as an outline.
544    #[derive(Debug, Copy, Clone)]
545    pub enum DrawMode {
546        /// A stroked line with given parameters, see `StrokeOptions` documentation.
547        Stroke(crate::graphics::StrokeOptions),
548        /// A filled shape with given parameters, see `FillOptions` documentation.
549        Fill(crate::graphics::FillOptions),
550    }
551
552    impl DrawMode {
553        /// Constructs a DrawMode that draws a stroke with the given width
554        pub fn stroke(width: f32) -> DrawMode {
555            DrawMode::Stroke(StrokeOptions::default().with_line_width(width))
556        }
557
558        /// Constructs a DrawMode that fills shapes
559        pub fn fill() -> DrawMode {
560            DrawMode::Fill(FillOptions::default())
561        }
562    }
563}
564
565#[cfg(feature = "mesh")]
566pub use draw_mode::*;