bevy_rectray/
rect.rs

1use std::ops::{Mul, Neg};
2
3use bevy::ecs::entity::Entity;
4use bevy::ecs::{component::Component, reflect::ReflectComponent};
5use bevy::math::{Quat, Rect, Vec2};
6use bevy::reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
7use bevy::transform::components::Transform;
8use serde::{Deserialize, Serialize};
9
10use crate::Transform2D;
11
12/// Anchor of a sprite, this is a more concise implementation than bevy's.
13///
14/// If a field is `Inherit` it will use `anchor` if possible.
15#[derive(Debug, Clone, Copy, Default, PartialEq, Reflect, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct Anchor(Vec2);
18
19impl Anchor {
20    pub const INHERIT: Self = Self(Vec2::NAN);
21    pub const BOTTOM_LEFT: Self = Self(Vec2::new(-0.5, -0.5));
22    pub const BOTTOM_CENTER: Self = Self(Vec2::new(0.0, -0.5));
23    pub const BOTTOM_RIGHT: Self = Self(Vec2::new(0.5, -0.5));
24    pub const CENTER_LEFT: Self = Self(Vec2::new(-0.5, 0.0));
25    pub const CENTER: Self = Self(Vec2::ZERO);
26    pub const CENTER_RIGHT: Self = Self(Vec2::new(0.5, 0.0));
27    pub const TOP_LEFT: Self = Self(Vec2::new(-0.5, 0.5));
28    pub const TOP_CENTER: Self = Self(Vec2::new(0.0, 0.5));
29    pub const TOP_RIGHT: Self = Self(Vec2::new(0.5, 0.5));
30
31    pub const fn new(v: Vec2) -> Self {
32        Self(v)
33    }
34
35    pub const fn custom(x: f32, y: f32) -> Self {
36        Self(Vec2::new(x, y))
37    }
38
39    pub fn is_inherit(&self) -> bool {
40        self.0.x.is_nan()
41    }
42
43    pub const fn as_vec(&self) -> Vec2 {
44        self.0
45    }
46
47    pub fn as_unit(&self) -> Vec2 {
48        self.0 + Vec2::new(0.5, 0.5)
49    }
50
51    pub const fn x(&self) -> f32 {
52        self.0.x
53    }
54
55    pub const fn y(&self) -> f32 {
56        self.0.y
57    }
58
59    pub fn or(self, other: Self) -> Self {
60        if self.is_inherit() {
61            other
62        } else {
63            self
64        }
65    }
66
67    pub fn str_name(&self) -> &'static str {
68        match (self.0.x, self.0.y) {
69            x if x.0.is_nan() || x.1.is_nan() => "Inherit",
70            (x, y) if x < -0.16 && y < -0.16 => "BottomLeft",
71            (x, y) if x < -0.16 && y > 0.16 => "TopLeft",
72            (x, _) if x < -0.16 => "CenterLeft",
73            (x, y) if x > 0.16 && y < -0.16 => "BottomRight",
74            (x, y) if x > 0.16 && y > 0.16 => "TopRight",
75            (x, _) if x > 0.16 => "CenterRight",
76            (_, y) if y < -0.16 => "BottomCenter",
77            (_, y) if y > 0.16 => "TopCenter",
78            _ => "Center",
79        }
80    }
81}
82
83impl Neg for Anchor {
84    type Output = Anchor;
85
86    fn neg(self) -> Self::Output {
87        Self(-self.0)
88    }
89}
90
91impl Mul<Vec2> for Anchor {
92    type Output = Vec2;
93
94    fn mul(self, rhs: Vec2) -> Self::Output {
95        self.0 * rhs
96    }
97}
98
99impl Mul<Anchor> for Vec2 {
100    type Output = Vec2;
101
102    fn mul(self, rhs: Anchor) -> Self::Output {
103        self * rhs.0
104    }
105}
106
107impl From<Anchor> for Vec2 {
108    fn from(val: Anchor) -> Self {
109        val.0
110    }
111}
112
113impl From<Vec2> for Anchor {
114    fn from(val: Vec2) -> Self {
115        Anchor(val)
116    }
117}
118
119/// A rotated 2D rectangle.
120///
121/// Note: `scale` is independent from dimension.
122#[derive(Debug, Clone, Copy, Component, PartialEq, Default, Serialize, Deserialize, Reflect)]
123#[reflect(Component, Default, Serialize, Deserialize)]
124pub struct RotatedRect {
125    /// Center of the rect.
126    pub center: Vec2,
127    /// Size of the rect.
128    pub dimension: Vec2,
129    /// Rotation of the Rect.
130    pub rotation: f32,
131    /// Z depth of the Rect.
132    pub z: f32,
133    /// Scale of the rect.
134    pub scale: Vec2,
135    #[serde(skip)]
136    /// Entity of the frame.
137    pub frame_entity: Option<Entity>,
138}
139
140/// Relevant info about a parent.
141#[doc(hidden)]
142#[derive(Debug, Copy, Clone)]
143pub struct ParentInfo {
144    pub dimension: Vec2,
145    pub center: Vec2,
146    pub anchor: Option<Vec2>,
147    pub affine: Transform2,
148    pub frame: Entity,
149    pub frame_rect: Rect,
150}
151
152#[doc(hidden)]
153#[derive(Debug, Copy, Clone)]
154pub struct Transform2 {
155    pub translation: Vec2,
156    pub rotation: f32,
157    pub scale: Vec2,
158}
159
160impl Transform2 {
161    pub const IDENTITY: Transform2 = Transform2 {
162        translation: Vec2::ZERO,
163        rotation: 0.,
164        scale: Vec2::ONE,
165    };
166
167    pub fn mul(&self, other: Transform2) -> Self {
168        Transform2 {
169            translation: self.transform_point2(other.translation),
170            rotation: self.rotation + other.rotation,
171            scale: self.scale * other.scale,
172        }
173    }
174
175    pub fn transform_point2(&self, point: Vec2) -> Vec2 {
176        Vec2::from_angle(self.rotation).rotate(point) * self.scale + self.translation
177    }
178}
179
180impl ParentInfo {
181    pub fn with_anchor(mut self, anc: Vec2) -> Self {
182        self.anchor = Some(anc);
183        self
184    }
185}
186
187impl RotatedRect {
188    /// Find the frame space position of an anchor.
189    #[inline]
190    pub fn anchor(&self, anchor: Anchor) -> Vec2 {
191        Vec2::from_angle(self.rotation).rotate(self.dimension * anchor) + self.center
192    }
193
194    // Half dimension
195    #[inline]
196    pub fn half_dim(&self) -> Vec2 {
197        self.dimension / 2.
198    }
199
200    /// convert a frame space point to local space, centered on `Center`.
201    #[inline]
202    pub fn local_space(&self, position: Vec2) -> Vec2 {
203        Vec2::from_angle(-self.rotation).rotate(position - self.center)
204    }
205
206    pub(crate) fn transform2_at(&self, center: Vec2) -> Transform2 {
207        Transform2 {
208            translation: self.anchor((-center).into()),
209            rotation: self.rotation,
210            scale: self.scale,
211        }
212    }
213
214    pub fn transform_at(&self, center: Vec2) -> Transform {
215        Transform {
216            translation: self.anchor((-center).into()).extend(self.z),
217            rotation: Quat::from_rotation_z(self.rotation),
218            scale: self.scale.extend(1.0),
219        }
220    }
221
222    pub(crate) fn under_transform2(mut self, transform: Transform2) -> Self {
223        self.center = transform.transform_point2(self.center);
224        self.rotation += transform.rotation;
225        self.scale *= transform.scale;
226        self
227    }
228
229    /// Create an [`RotatedRect`] representing the sprite's position in parent space.
230    #[inline]
231    pub fn construct(
232        parent: &ParentInfo,
233        transform: &Transform2D,
234        dimension: Vec2,
235        frame: Entity,
236    ) -> Self {
237        let parent_anchor = parent.anchor.unwrap_or(transform.get_parent_anchor());
238        let anchor = transform.anchor.as_vec();
239        Self::construct2(parent, transform, parent_anchor, anchor, dimension, frame)
240    }
241
242    /// Create an [`RotatedRect`] representing the sprite's position in parent space.
243    #[inline]
244    #[doc(hidden)]
245    pub fn construct2(
246        parent: &ParentInfo,
247        transform: &Transform2D,
248        parent_anchor: Vec2,
249        anchor: Vec2,
250        dimension: Vec2,
251        frame: Entity,
252    ) -> Self {
253        let root = parent.dimension * parent_anchor;
254        // apply offset and dimension
255        let self_center = root + transform.offset + (transform.get_center() - anchor) * dimension;
256        Self {
257            center: self_center,
258            dimension,
259            z: transform.z,
260            rotation: transform.rotation,
261            scale: transform.scale,
262            frame_entity: Some(frame),
263        }
264    }
265
266    /// Determines if inside a [`Rect`].
267    pub fn aabb(&self) -> Rect {
268        let bl = self.anchor(Anchor::BOTTOM_LEFT);
269        let tr = self.center * 2.0 - bl;
270        Rect {
271            min: bl.min(tr),
272            max: bl.max(tr),
273        }
274    }
275
276    /// Determines if inside a [`Rect`].
277    pub fn is_inside(&self, rect: Rect) -> bool {
278        let bl = self.anchor(Anchor::BOTTOM_LEFT);
279        let tr = self.center * 2.0 - bl;
280        rect.contains(bl) && rect.contains(tr)
281    }
282
283    /// Nudge the [`RotatedRect`] inside the [`Rect`],
284    /// does nothing if aabb is larger than [`Rect`].
285    pub fn nudge_inside(&mut self, bounds: Rect) {
286        let aabb = self.aabb();
287        if aabb.size().cmpgt(bounds.size()).any() {
288            return;
289        }
290        nudge_aabb_with(&mut self.center, aabb, bounds);
291    }
292
293    /// Nudge the [`RotatedRect`] inside the [`Rect`],
294    /// does nothing if aabb is larger than [`Rect`].
295    pub(crate) fn nudge_inside_ext(&self, bounds: Rect, value: &mut Vec2) {
296        let aabb = self.aabb();
297        if aabb.size().cmpgt(bounds.size()).any() {
298            return;
299        }
300        nudge_aabb_with(value, aabb, bounds);
301    }
302}
303
304fn nudge_aabb_with(output: &mut Vec2, aabb: Rect, bounds: Rect) {
305    if aabb.min.x < bounds.min.x {
306        output.x += bounds.min.x - aabb.min.x;
307    } else if aabb.max.x > bounds.max.x {
308        output.x -= aabb.max.x - bounds.max.x;
309    }
310
311    if aabb.min.y < bounds.min.y {
312        output.y += bounds.min.y - aabb.min.y;
313    } else if aabb.max.y > bounds.max.y {
314        output.y -= aabb.max.y - bounds.max.y;
315    }
316}