use super::{Canvas, Color, GraphicsContext, LinearColor, Rect};
use crate::context::Has;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Transform {
Values {
dest: mint::Point2<f32>,
rotation: f32,
scale: mint::Vector2<f32>,
offset: mint::Point2<f32>,
},
Matrix(mint::ColumnMatrix4<f32>),
}
impl Default for Transform {
fn default() -> Self {
Transform::Values {
dest: mint::Point2 { x: 0.0, y: 0.0 },
rotation: 0.0,
scale: mint::Vector2 { x: 1.0, y: 1.0 },
offset: mint::Point2 { x: 0.0, y: 0.0 },
}
}
}
impl Transform {
#[must_use]
pub fn to_matrix(self) -> Self {
Transform::Matrix(self.to_bare_matrix())
}
#[must_use]
pub fn to_bare_matrix(self) -> mint::ColumnMatrix4<f32> {
match self {
Transform::Matrix(m) => m,
Transform::Values {
dest,
rotation,
scale,
offset,
} => {
let (sinr, cosr) = rotation.sin_cos();
let m00 = cosr * scale.x;
let m01 = -sinr * scale.y;
let m10 = sinr * scale.x;
let m11 = cosr * scale.y;
let m03 = offset.x * (-m00) - offset.y * m01 + dest.x;
let m13 = offset.y * (-m11) - offset.x * m10 + dest.y;
glam::Mat4::from_cols_array(&[
m00, m01, 0.0, m03, m10, m11, 0.0, m13, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ])
.transpose()
.into()
}
}
}
}
pub type ZIndex = i32;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DrawParam {
pub src: Rect,
pub color: Color,
pub transform: Transform,
pub z: ZIndex,
}
impl Default for DrawParam {
fn default() -> Self {
DrawParam {
src: Rect::one(),
color: Color::WHITE,
transform: Transform::default(),
z: 0,
}
}
}
impl DrawParam {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn src(mut self, src: Rect) -> Self {
self.src = src;
self
}
pub(crate) fn get_dest_mut(&mut self) -> &mut mint::Point2<f32> {
if let Transform::Values { dest, .. } = &mut self.transform {
dest
} else {
panic!("Cannot calculate destination value for a DrawParam matrix")
}
}
pub fn dest<P>(mut self, dest: P) -> Self
where
P: Into<mint::Point2<f32>>,
{
*self.get_dest_mut() = dest.into();
self
}
#[must_use]
pub fn dest_rect(self, rect: Rect) -> Self {
self.dest(rect.point()).scale(rect.size())
}
pub fn color(mut self, color: impl Into<Color>) -> Self {
self.color = color.into();
self
}
#[must_use]
pub fn rotation(mut self, rot: f32) -> Self {
if let Transform::Values {
ref mut rotation, ..
} = self.transform
{
*rotation = rot;
self
} else {
panic!("Cannot set values for a DrawParam matrix")
}
}
pub fn scale<V>(mut self, scale_: V) -> Self
where
V: Into<mint::Vector2<f32>>,
{
if let Transform::Values { ref mut scale, .. } = self.transform {
let p: mint::Vector2<f32> = scale_.into();
*scale = p;
self
} else {
panic!("Cannot set values for a DrawParam matrix")
}
}
pub fn offset<P>(mut self, offset_: P) -> Self
where
P: Into<mint::Point2<f32>>,
{
if let Transform::Values { ref mut offset, .. } = self.transform {
let p: mint::Point2<f32> = offset_.into();
*offset = p;
self
} else {
panic!("Cannot set values for a DrawParam matrix")
}
}
pub fn transform<M>(mut self, transform: M) -> Self
where
M: Into<mint::ColumnMatrix4<f32>>,
{
self.transform = Transform::Matrix(transform.into());
self
}
pub fn z(mut self, z: ZIndex) -> Self {
self.z = z;
self
}
}
impl<P> From<P> for DrawParam
where
P: Into<mint::Point2<f32>>,
{
fn from(location: P) -> Self {
DrawParam::new().dest(location)
}
}
pub trait Drawable {
fn draw(&self, canvas: &mut Canvas, param: impl Into<DrawParam>);
fn dimensions(&self, gfx: &impl Has<GraphicsContext>) -> Option<Rect>;
}
#[derive(Debug, Copy, Clone, crevice::std140::AsStd140)]
pub(crate) struct DrawUniforms {
pub color: mint::Vector4<f32>,
pub src_rect: mint::Vector4<f32>,
pub transform: mint::ColumnMatrix4<f32>,
}
#[allow(unsafe_code)]
unsafe impl bytemuck::Zeroable for DrawUniforms {}
#[allow(unsafe_code)]
unsafe impl bytemuck::Pod for DrawUniforms {}
impl DrawUniforms {
pub fn from_param(param: &DrawParam, image_scale: Option<mint::Vector2<f32>>) -> Self {
let (scale_x, scale_y) = if let Some(image_scale) = image_scale {
(image_scale.x * param.src.w, image_scale.y * param.src.h)
} else {
(1., 1.)
};
let param = match param.transform {
Transform::Values { scale, .. } => param.scale(mint::Vector2 {
x: scale.x * scale_x,
y: scale.y * scale_y,
}),
Transform::Matrix(m) => param.transform(
glam::Mat4::from(m) * glam::Mat4::from_scale(glam::vec3(scale_x, scale_y, 1.)),
),
};
let color = LinearColor::from(param.color);
DrawUniforms {
color: <[f32; 4]>::from(color).into(),
src_rect: mint::Vector4 {
x: param.src.x,
y: param.src.y,
z: param.src.x + param.src.w,
w: param.src.y + param.src.h,
},
transform: param.transform.to_bare_matrix(),
}
}
}