pub type Point2 = cgmath::Point2<f32>;
pub type Vector2 = cgmath::Vector2<f32>;
use crate::graphics::DrawParam;
use cgmath::{Matrix4, Transform, Vector4};
#[derive(Debug, Clone)]
#[repr(C)]
pub(crate) struct InstanceAttributes {
pub source: Vector4<f32>,
pub color: Vector4<f32>,
pub model: Matrix4<f32>,
}
impl Default for InstanceAttributes {
fn default() -> InstanceAttributes {
InstanceAttributes {
source: Vector4::new(0., 0., 0., 0.),
color: Vector4::new(0., 0., 0., 0.),
model: Matrix4::one(),
}
}
}
impl From<&DrawParam> for InstanceAttributes {
fn from(param: &DrawParam) -> Self {
InstanceAttributes {
model: param.trans.to_bare_matrix().into(),
source: Vector4::new(param.src.x, param.src.y, param.src.w, param.src.h),
color: Vector4::new(param.color.r, param.color.g, param.color.b, param.color.a),
}
}
}
#[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
impl Rect {
pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
Rect { x, y, w, h }
}
pub fn fraction(x: f32, y: f32, w: f32, h: f32, reference: &Rect) -> Rect {
Rect {
x: x / reference.w,
y: y / reference.h,
w: w / reference.w,
h: h / reference.h,
}
}
pub const fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
Rect {
x: x as f32,
y: y as f32,
w: w as f32,
h: h as f32,
}
}
pub const fn zero() -> Self {
Self::new(0.0, 0.0, 0.0, 0.0)
}
pub const fn one() -> Self {
Self::new(0.0, 0.0, 1.0, 1.0)
}
pub fn point(&self) -> mint::Point2<f32> {
mint::Point2 {
x: self.x,
y: self.y,
}
}
pub fn center(&self) -> mint::Point2<f32> {
mint::Point2 {
x: self.x + self.w / 2.,
y: self.y + self.h / 2.,
}
}
pub fn left(&self) -> f32 {
self.x
}
pub fn right(&self) -> f32 {
self.x + self.w
}
pub fn top(&self) -> f32 {
self.y
}
pub fn bottom(&self) -> f32 {
self.y + self.h
}
pub fn contains<P: Into<mint::Point2<f32>>>(&self, point: P) -> bool {
let point: mint::Point2<_> = point.into();
point.x >= self.left()
&& point.x <= self.right()
&& point.y <= self.bottom()
&& point.y >= self.top()
}
pub fn overlaps(&self, other: &Rect) -> bool {
self.left() <= other.right()
&& self.right() >= other.left()
&& self.top() <= other.bottom()
&& self.bottom() >= other.top()
}
pub fn translate<V: Into<mint::Vector2<f32>>>(&mut self, offset: V) {
let offset: mint::Vector2<f32> = offset.into();
self.x += offset.x;
self.y += offset.y;
}
pub fn move_to<P: Into<mint::Point2<f32>>>(&mut self, destination: P) {
let destination = destination.into();
self.x = destination.x;
self.y = destination.y;
}
pub fn scale(&mut self, sx: f32, sy: f32) {
self.w *= sx;
self.h *= sy;
}
pub fn combine_with(self, other: Rect) -> Rect {
let x = f32::min(self.x, other.x);
let y = f32::min(self.y, other.y);
let w = f32::max(self.right(), other.right()) - x;
let h = f32::max(self.bottom(), other.bottom()) - y;
Rect { x, y, w, h }
}
pub fn rotate(&mut self, rotation: f32) {
use cgmath::{Basis2, Rotation, Rotation2};
let rotation: Basis2<f32> = Rotation2::from_angle(cgmath::Rad(rotation));
let x0 = self.x;
let y0 = self.y;
let x1 = self.right();
let y1 = self.bottom();
let points = [
rotation.rotate_point(cgmath::Point2::new(x0, y0)),
rotation.rotate_point(cgmath::Point2::new(x0, y1)),
rotation.rotate_point(cgmath::Point2::new(x1, y0)),
rotation.rotate_point(cgmath::Point2::new(x1, y1)),
];
let p0 = points[0];
let mut x_max = p0.x;
let mut x_min = p0.x;
let mut y_max = p0.y;
let mut y_min = p0.y;
for p in &points {
x_max = f32::max(x_max, p.x);
x_min = f32::min(x_min, p.x);
y_max = f32::max(y_max, p.y);
y_min = f32::min(y_min, p.y);
}
*self = Rect {
w: x_max - x_min,
h: y_max - y_min,
x: x_min,
y: y_min,
}
}
}
impl approx::AbsDiffEq for Rect {
type Epsilon = f32;
fn default_epsilon() -> Self::Epsilon {
f32::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
f32::abs_diff_eq(&self.x, &other.x, epsilon)
&& f32::abs_diff_eq(&self.y, &other.y, epsilon)
&& f32::abs_diff_eq(&self.w, &other.w, epsilon)
&& f32::abs_diff_eq(&self.h, &other.h, epsilon)
}
}
impl approx::RelativeEq for Rect {
fn default_max_relative() -> Self::Epsilon {
f32::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
f32::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& f32::relative_eq(&self.y, &other.y, epsilon, max_relative)
&& f32::relative_eq(&self.w, &other.w, epsilon, max_relative)
&& f32::relative_eq(&self.h, &other.h, epsilon, max_relative)
}
}
impl From<[f32; 4]> for Rect {
fn from(val: [f32; 4]) -> Self {
Rect::new(val[0], val[1], val[2], val[3])
}
}
impl From<Rect> for [f32; 4] {
fn from(val: Rect) -> Self {
[val.x, val.y, val.w, val.h]
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub const WHITE: Color = Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub const BLACK: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const RED: Color = Color {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const GREEN: Color = Color {
r: 0.0,
g: 1.0,
b: 0.0,
a: 1.0,
};
pub const BLUE: Color = Color {
r: 0.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
pub const CYAN: Color = Color {
r: 0.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub const MAGENTA: Color = Color {
r: 1.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
pub const YELLOW: Color = Color {
r: 1.0,
g: 1.0,
b: 0.0,
a: 1.0,
};
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Color { r, g, b, a }
}
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
Color::from((r, g, b, a))
}
pub fn from_rgb(r: u8, g: u8, b: u8) -> Color {
Color::from((r, g, b))
}
pub fn to_rgba(self) -> (u8, u8, u8, u8) {
self.into()
}
pub fn to_rgb(self) -> (u8, u8, u8) {
self.into()
}
pub fn from_rgba_u32(c: u32) -> Color {
let c = c.to_be_bytes();
Color::from((c[0], c[1], c[2], c[3]))
}
pub fn from_rgb_u32(c: u32) -> Color {
let c = c.to_be_bytes();
Color::from((c[1], c[2], c[3]))
}
pub fn to_rgba_u32(self) -> u32 {
let (r, g, b, a): (u8, u8, u8, u8) = self.into();
u32::from_be_bytes([r, g, b, a])
}
pub fn to_rgb_u32(self) -> u32 {
let (r, g, b, _a): (u8, u8, u8, u8) = self.into();
u32::from_be_bytes([0, r, g, b])
}
}
impl From<(u8, u8, u8, u8)> for Color {
fn from(val: (u8, u8, u8, u8)) -> Self {
let (r, g, b, a) = val;
let rf = (f32::from(r)) / 255.0;
let gf = (f32::from(g)) / 255.0;
let bf = (f32::from(b)) / 255.0;
let af = (f32::from(a)) / 255.0;
Color::new(rf, gf, bf, af)
}
}
impl From<(u8, u8, u8)> for Color {
fn from(val: (u8, u8, u8)) -> Self {
let (r, g, b) = val;
Color::from((r, g, b, 255))
}
}
impl From<[f32; 4]> for Color {
fn from(val: [f32; 4]) -> Self {
Color::new(val[0], val[1], val[2], val[3])
}
}
impl From<(f32, f32, f32)> for Color {
fn from(val: (f32, f32, f32)) -> Self {
let (r, g, b) = val;
Color::new(r, g, b, 1.0)
}
}
impl From<(f32, f32, f32, f32)> for Color {
fn from(val: (f32, f32, f32, f32)) -> Self {
let (r, g, b, a) = val;
Color::new(r, g, b, a)
}
}
impl From<Color> for (u8, u8, u8, u8) {
fn from(color: Color) -> Self {
let r = (color.r * 255.0) as u8;
let g = (color.g * 255.0) as u8;
let b = (color.b * 255.0) as u8;
let a = (color.a * 255.0) as u8;
(r, g, b, a)
}
}
impl From<Color> for (u8, u8, u8) {
fn from(color: Color) -> Self {
let (r, g, b, _) = color.into();
(r, g, b)
}
}
impl From<Color> for [f32; 4] {
fn from(color: Color) -> Self {
[color.r, color.g, color.b, color.a]
}
}
#[allow(clippy::from_over_into)]
impl Into<String> for Color {
fn into(self) -> String {
format!(
"#{:02x}{:02x}{:02x}{:02x}",
(self.r * 255.) as i32,
(self.g * 255.) as i32,
(self.b * 255.) as i32,
(self.a * 255.) as i32
)
}
}
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub(crate) struct LinearColor {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl From<Color> for LinearColor {
fn from(c: Color) -> Self {
fn f(component: f32) -> f32 {
let a = 0.055;
if component <= 0.04045 {
component / 12.92
} else {
((component + a) / (1.0 + a)).powf(2.4)
}
}
LinearColor {
r: f(c.r),
g: f(c.g),
b: f(c.b),
a: c.a,
}
}
}
impl From<LinearColor> for Color {
fn from(c: LinearColor) -> Self {
fn f(component: f32) -> f32 {
let a = 0.055;
if component <= 0.003_130_8 {
component * 12.92
} else {
(1.0 + a) * component.powf(1.0 / 2.4)
}
}
Color {
r: f(c.r),
g: f(c.g),
b: f(c.b),
a: c.a,
}
}
}
impl From<LinearColor> for [f32; 4] {
fn from(color: LinearColor) -> Self {
[color.r, color.g, color.b, color.a]
}
}
#[cfg(feature = "mesh")]
mod draw_mode {
use crate::graphics::{FillOptions, StrokeOptions};
#[derive(Debug, Copy, Clone)]
pub enum DrawMode {
Stroke(crate::graphics::StrokeOptions),
Fill(crate::graphics::FillOptions),
}
impl DrawMode {
pub fn stroke(width: f32) -> DrawMode {
DrawMode::Stroke(StrokeOptions::default().with_line_width(width))
}
pub fn fill() -> DrawMode {
DrawMode::Fill(FillOptions::default())
}
}
}
#[cfg(feature = "mesh")]
pub use draw_mode::*;