use bevy_ecs::prelude::*;
use windows_numerics::Matrix3x2;
use super::{D2DRect, LayoutScale, Offset, Size, transform_rect_axis_aligned};
use crate::ecs::types::Rect;
#[derive(Component, Debug, Clone, Copy, PartialEq)]
#[component(on_add = on_arrangement_add)]
pub struct Arrangement {
pub offset: Offset,
pub scale: LayoutScale,
pub size: Size,
}
impl Arrangement {
pub fn local_bounds(&self) -> D2DRect {
Rect {
left: 0.0,
top: 0.0,
right: self.size.width,
bottom: self.size.height,
}
}
}
impl Default for Arrangement {
fn default() -> Self {
Self {
offset: Offset::default(),
scale: LayoutScale::default(),
size: Size::default(),
}
}
}
fn on_arrangement_add(
mut world: bevy_ecs::world::DeferredWorld,
hook: bevy_ecs::lifecycle::HookContext,
) {
world
.commands()
.entity(hook.entity)
.insert((GlobalArrangement::default(), ArrangementTreeChanged));
}
#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub struct GlobalArrangement {
pub transform: Matrix3x2,
pub bounds: D2DRect,
}
impl GlobalArrangement {
#[inline]
pub fn scale_x(&self) -> f32 {
self.transform.M11
}
#[inline]
pub fn scale_y(&self) -> f32 {
self.transform.M22
}
#[inline]
pub fn scale(&self) -> (f32, f32) {
(self.transform.M11, self.transform.M22)
}
#[inline]
pub fn offset_x(&self) -> f32 {
self.transform.M31
}
#[inline]
pub fn offset_y(&self) -> f32 {
self.transform.M32
}
#[inline]
pub fn offset(&self) -> (f32, f32) {
(self.transform.M31, self.transform.M32)
}
#[inline]
pub fn width(&self) -> f32 {
self.bounds.right - self.bounds.left
}
#[inline]
pub fn height(&self) -> f32 {
self.bounds.bottom - self.bounds.top
}
#[inline]
pub fn size(&self) -> (f32, f32) {
(self.width(), self.height())
}
}
impl Default for GlobalArrangement {
fn default() -> Self {
Self {
transform: Matrix3x2::identity(),
bounds: Rect {
left: 0.0,
top: 0.0,
right: 0.0,
bottom: 0.0,
},
}
}
}
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct ArrangementTreeChanged;
impl From<Offset> for Matrix3x2 {
fn from(offset: Offset) -> Self {
Matrix3x2::translation(offset.x, offset.y)
}
}
impl From<LayoutScale> for Matrix3x2 {
fn from(scale: LayoutScale) -> Self {
Matrix3x2::scale(scale.x, scale.y)
}
}
impl From<Arrangement> for Matrix3x2 {
fn from(arr: Arrangement) -> Self {
let scale: Matrix3x2 = arr.scale.into();
let translation: Matrix3x2 = arr.offset.into();
translation * scale
}
}
impl From<Arrangement> for GlobalArrangement {
fn from(arrangement: Arrangement) -> Self {
let transform: Matrix3x2 = arrangement.into();
let local_bounds = arrangement.local_bounds();
let bounds = transform_rect_axis_aligned(&local_bounds, &transform);
Self { transform, bounds }
}
}
impl std::ops::Mul<Arrangement> for GlobalArrangement {
type Output = GlobalArrangement;
fn mul(self, rhs: Arrangement) -> Self::Output {
let parent_scale_x = self.transform.M11;
let parent_scale_y = self.transform.M22;
let child_matrix: Matrix3x2 = rhs.into();
let result_transform = self.transform * child_matrix;
let scaled_offset = Offset {
x: rhs.offset.x * parent_scale_x,
y: rhs.offset.y * parent_scale_y,
};
let result_scale_x = result_transform.M11;
let result_scale_y = result_transform.M22;
let result_bounds = D2DRect {
left: self.bounds.left + scaled_offset.x,
top: self.bounds.top + scaled_offset.y,
right: self.bounds.left + scaled_offset.x + rhs.size.width * result_scale_x,
bottom: self.bounds.top + scaled_offset.y + rhs.size.height * result_scale_y,
};
GlobalArrangement {
transform: result_transform,
bounds: result_bounds,
}
}
}