use std::ops::{Mul, Neg};
use bevy::ecs::entity::Entity;
use bevy::ecs::{component::Component, reflect::ReflectComponent};
use bevy::math::{Quat, Rect, Vec2};
use bevy::reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
use bevy::transform::components::Transform;
use serde::{Deserialize, Serialize};
use crate::Transform2D;
#[derive(Debug, Clone, Copy, Default, PartialEq, Reflect, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Anchor(Vec2);
impl Anchor {
pub const INHERIT: Self = Self(Vec2::NAN);
pub const BOTTOM_LEFT: Self = Self(Vec2::new(-0.5, -0.5));
pub const BOTTOM_CENTER: Self = Self(Vec2::new(0.0, -0.5));
pub const BOTTOM_RIGHT: Self = Self(Vec2::new(0.5, -0.5));
pub const CENTER_LEFT: Self = Self(Vec2::new(-0.5, 0.0));
pub const CENTER: Self = Self(Vec2::ZERO);
pub const CENTER_RIGHT: Self = Self(Vec2::new(0.5, 0.0));
pub const TOP_LEFT: Self = Self(Vec2::new(-0.5, 0.5));
pub const TOP_CENTER: Self = Self(Vec2::new(0.0, 0.5));
pub const TOP_RIGHT: Self = Self(Vec2::new(0.5, 0.5));
pub const fn new(v: Vec2) -> Self {
Self(v)
}
pub const fn custom(x: f32, y: f32) -> Self {
Self(Vec2::new(x, y))
}
pub fn is_inherit(&self) -> bool {
self.0.x.is_nan()
}
pub const fn as_vec(&self) -> Vec2 {
self.0
}
pub fn as_unit(&self) -> Vec2 {
self.0 + Vec2::new(0.5, 0.5)
}
pub const fn x(&self) -> f32 {
self.0.x
}
pub const fn y(&self) -> f32 {
self.0.y
}
pub fn or(self, other: Self) -> Self {
if self.is_inherit() {
other
} else {
self
}
}
pub fn str_name(&self) -> &'static str {
match (self.0.x, self.0.y) {
x if x.0.is_nan() || x.1.is_nan() => "Inherit",
(x, y) if x < -0.16 && y < -0.16 => "BottomLeft",
(x, y) if x < -0.16 && y > 0.16 => "TopLeft",
(x, _) if x < -0.16 => "CenterLeft",
(x, y) if x > 0.16 && y < -0.16 => "BottomRight",
(x, y) if x > 0.16 && y > 0.16 => "TopRight",
(x, _) if x > 0.16 => "CenterRight",
(_, y) if y < -0.16 => "BottomCenter",
(_, y) if y > 0.16 => "TopCenter",
_ => "Center",
}
}
}
impl Neg for Anchor {
type Output = Anchor;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl Mul<Vec2> for Anchor {
type Output = Vec2;
fn mul(self, rhs: Vec2) -> Self::Output {
self.0 * rhs
}
}
impl Mul<Anchor> for Vec2 {
type Output = Vec2;
fn mul(self, rhs: Anchor) -> Self::Output {
self * rhs.0
}
}
impl From<Anchor> for Vec2 {
fn from(val: Anchor) -> Self {
val.0
}
}
impl From<Vec2> for Anchor {
fn from(val: Vec2) -> Self {
Anchor(val)
}
}
#[derive(Debug, Clone, Copy, Component, PartialEq, Default, Serialize, Deserialize, Reflect)]
#[reflect(Component, Default, Serialize, Deserialize)]
pub struct RotatedRect {
pub center: Vec2,
pub dimension: Vec2,
pub rotation: f32,
pub z: f32,
pub scale: Vec2,
#[serde(skip)]
pub frame_entity: Option<Entity>,
}
#[doc(hidden)]
#[derive(Debug, Copy, Clone)]
pub struct ParentInfo {
pub dimension: Vec2,
pub center: Vec2,
pub anchor: Option<Vec2>,
pub affine: Transform2,
pub frame: Entity,
pub frame_rect: Rect,
}
#[doc(hidden)]
#[derive(Debug, Copy, Clone)]
pub struct Transform2 {
pub translation: Vec2,
pub rotation: f32,
pub scale: Vec2,
}
impl Transform2 {
pub const IDENTITY: Transform2 = Transform2 {
translation: Vec2::ZERO,
rotation: 0.,
scale: Vec2::ONE,
};
pub fn mul(&self, other: Transform2) -> Self {
Transform2 {
translation: self.transform_point2(other.translation),
rotation: self.rotation + other.rotation,
scale: self.scale * other.scale,
}
}
pub fn transform_point2(&self, point: Vec2) -> Vec2 {
Vec2::from_angle(self.rotation).rotate(point) * self.scale + self.translation
}
}
impl ParentInfo {
pub fn with_anchor(mut self, anc: Vec2) -> Self {
self.anchor = Some(anc);
self
}
}
impl RotatedRect {
#[inline]
pub fn anchor(&self, anchor: Anchor) -> Vec2 {
Vec2::from_angle(self.rotation).rotate(self.dimension * anchor) + self.center
}
#[inline]
pub fn half_dim(&self) -> Vec2 {
self.dimension / 2.
}
#[inline]
pub fn local_space(&self, position: Vec2) -> Vec2 {
Vec2::from_angle(-self.rotation).rotate(position - self.center)
}
pub(crate) fn transform2_at(&self, center: Vec2) -> Transform2 {
Transform2 {
translation: self.anchor((-center).into()),
rotation: self.rotation,
scale: self.scale,
}
}
pub fn transform_at(&self, center: Vec2) -> Transform {
Transform {
translation: self.anchor((-center).into()).extend(self.z),
rotation: Quat::from_rotation_z(self.rotation),
scale: self.scale.extend(1.0),
}
}
pub(crate) fn under_transform2(mut self, transform: Transform2) -> Self {
self.center = transform.transform_point2(self.center);
self.rotation += transform.rotation;
self.scale *= transform.scale;
self
}
#[inline]
pub fn construct(
parent: &ParentInfo,
transform: &Transform2D,
dimension: Vec2,
frame: Entity,
) -> Self {
let parent_anchor = parent.anchor.unwrap_or(transform.get_parent_anchor());
let anchor = transform.anchor.as_vec();
Self::construct2(parent, transform, parent_anchor, anchor, dimension, frame)
}
#[inline]
#[doc(hidden)]
pub fn construct2(
parent: &ParentInfo,
transform: &Transform2D,
parent_anchor: Vec2,
anchor: Vec2,
dimension: Vec2,
frame: Entity,
) -> Self {
let root = parent.dimension * parent_anchor;
let self_center = root + transform.offset + (transform.get_center() - anchor) * dimension;
Self {
center: self_center,
dimension,
z: transform.z,
rotation: transform.rotation,
scale: transform.scale,
frame_entity: Some(frame),
}
}
pub fn aabb(&self) -> Rect {
let bl = self.anchor(Anchor::BOTTOM_LEFT);
let tr = self.center * 2.0 - bl;
Rect {
min: bl.min(tr),
max: bl.max(tr),
}
}
pub fn is_inside(&self, rect: Rect) -> bool {
let bl = self.anchor(Anchor::BOTTOM_LEFT);
let tr = self.center * 2.0 - bl;
rect.contains(bl) && rect.contains(tr)
}
pub fn nudge_inside(&mut self, bounds: Rect) {
let aabb = self.aabb();
if aabb.size().cmpgt(bounds.size()).any() {
return;
}
nudge_aabb_with(&mut self.center, aabb, bounds);
}
pub(crate) fn nudge_inside_ext(&self, bounds: Rect, value: &mut Vec2) {
let aabb = self.aabb();
if aabb.size().cmpgt(bounds.size()).any() {
return;
}
nudge_aabb_with(value, aabb, bounds);
}
}
fn nudge_aabb_with(output: &mut Vec2, aabb: Rect, bounds: Rect) {
if aabb.min.x < bounds.min.x {
output.x += bounds.min.x - aabb.min.x;
} else if aabb.max.x > bounds.max.x {
output.x -= aabb.max.x - bounds.max.x;
}
if aabb.min.y < bounds.min.y {
output.y += bounds.min.y - aabb.min.y;
} else if aabb.max.y > bounds.max.y {
output.y -= aabb.max.y - bounds.max.y;
}
}