Skip to content

Commit 64b0d9b

Browse files
committed
Reimplement Rect2 functions
1 parent 3c81e8c commit 64b0d9b

File tree

1 file changed

+175
-1
lines changed

1 file changed

+175
-1
lines changed

godot-core/src/builtin/rect2.rs

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use godot_ffi as sys;
88
use sys::{ffi_methods, GodotFfi};
99

10-
use super::{real, Rect2i, Vector2};
10+
use super::{real, Rect2i, RectSide, Vector2};
1111

1212
/// 2D axis-aligned bounding box.
1313
///
@@ -44,6 +44,180 @@ impl Rect2 {
4444
}
4545
}
4646

47+
/// Returns a rectangle with equivalent position and area, modified so that the top-left corner is the origin and `width` and `height` are positive.
48+
///
49+
/// _Godot equivalent: `Rect2.abs()`_
50+
#[inline]
51+
pub fn abs(&self) -> Self {
52+
Self {
53+
position: self.position + Vector2::new(self.size.x.min(0.0), self.size.y.min(0.0)),
54+
size: self.size.abs(),
55+
}
56+
}
57+
58+
/// Returns true if this rectangle (inclusively) encloses `b`. This is true when `self` covers all the area of `b`, and possibly (but not necessarily) more.
59+
///
60+
/// _Godot equivalent: `Rect2.encloses(Rect2 b)`_
61+
#[inline]
62+
pub fn encloses(&self, b: Rect2) -> bool {
63+
b.position.x >= self.position.x
64+
&& b.position.y >= self.position.y
65+
&& b.position.x + b.size.x <= self.position.x + self.size.x
66+
&& b.position.y + b.size.y <= self.position.y + self.size.y
67+
}
68+
69+
/// Returns a copy of this rectangle expanded to include a given point.
70+
///
71+
/// Note: This method is not reliable for `Rect2` with a negative size. Use [`abs`][Self::abs]
72+
/// to get a positive sized equivalent rectangle for expanding.
73+
///
74+
/// _Godot equivalent: `Rect2.expand(Vector2 to)`_
75+
#[inline]
76+
pub fn expand(&self, to: &Vector2) -> Self {
77+
self.merge(&Rect2::new(*to, Vector2::ZERO))
78+
}
79+
80+
/// Returns a larger rectangle that contains this `Rect2` and `b`.
81+
///
82+
/// Note: This method is not reliable for `Rect2` with a negative size. Use [`abs`][Self::abs]
83+
/// to get a positive sized equivalent rectangle for merging.
84+
///
85+
/// _Godot equivalent: `Rect2.merge(Rect2 b)`_
86+
#[inline]
87+
pub fn merge(&self, b: &Self) -> Self {
88+
let position = Vector2::new(
89+
self.position.x.min(b.position.x),
90+
self.position.y.min(b.position.y),
91+
);
92+
let end = Vector2::new(
93+
(self.position.x + self.size.x).max(b.position.x + b.size.x),
94+
(self.position.y + self.size.y).max(b.position.y + b.size.y),
95+
);
96+
97+
Self {
98+
position,
99+
size: end - position,
100+
}
101+
}
102+
103+
/// Returns the area of the rectangle.
104+
///
105+
/// _Godot equivalent: `Rect2.get_area()`_
106+
#[inline]
107+
pub fn area(&self) -> real {
108+
self.size.x * self.size.y
109+
}
110+
111+
/// Returns the center of the Rect2, which is equal to `position + (size / 2)`.
112+
///
113+
/// _Godot equivalent: `Rect2.get_center()`_
114+
#[inline]
115+
pub fn center(&self) -> Vector2 {
116+
self.position + (self.size / 2.0)
117+
}
118+
119+
/// Returns a copy of the Rect2 grown by the specified `amount` on all sides.
120+
///
121+
/// _Godot equivalent: `Rect2.grow(float amount)`_
122+
#[inline]
123+
pub fn grow(&self, amount: real) -> Self {
124+
let position = self.position - Vector2::new(amount, amount);
125+
let size = self.size + Vector2::new(amount, amount) * 2.0;
126+
127+
Self { position, size }
128+
}
129+
130+
/// Returns a copy of the Rect2 grown by the specified amount on each side individually.
131+
///
132+
/// _Godot equivalent: `Rect2.grow_individual(float left, float top, float right, float bottom)`_
133+
#[inline]
134+
pub fn grow_individual(&self, left: real, top: real, right: real, bottom: real) -> Self {
135+
Self::from_components(
136+
self.position.x - left,
137+
self.position.y - top,
138+
self.size.x + left + right,
139+
self.size.y + top + bottom,
140+
)
141+
}
142+
143+
/// Returns a copy of the `Rect2` grown by the specified `amount` on the specified `RectSide`.
144+
///
145+
/// `amount` may be negative, but care must be taken: If the resulting `size` has
146+
/// negative components the computation may be incorrect.
147+
///
148+
/// _Godot equivalent: `Rect2.grow_side(int side, float amount)`_
149+
#[inline]
150+
pub fn grow_side(&self, side: &RectSide, amount: real) -> Self {
151+
match side {
152+
RectSide::Left => self.grow_individual(amount, 0.0, 0.0, 0.0),
153+
RectSide::Top => self.grow_individual(0.0, amount, 0.0, 0.0),
154+
RectSide::Right => self.grow_individual(0.0, 0.0, amount, 0.0),
155+
RectSide::Bottom => self.grow_individual(0.0, 0.0, 0.0, amount),
156+
}
157+
}
158+
159+
/// Returns `true` if the Rect2 has area, and `false` if the Rect2 is linear, empty, or has a negative size. See also `get_area`.
160+
///
161+
/// _Godot equivalent: `Rect2.has_area()`_
162+
#[inline]
163+
pub fn has_area(&self) -> bool {
164+
self.size.x > 0.0 && self.size.y > 0.0
165+
}
166+
167+
/// Returns `true` if the Rect2 contains a point. By convention, the right and bottom edges of the Rect2 are considered exclusive, so points on these edges are not included.
168+
///
169+
/// Note: This method is not reliable for Rect2 with a negative size. Use `abs` to get a positive sized equivalent rectangle to check for contained points.
170+
///
171+
/// _Godot equivalent: `Rect2.has_area()`_
172+
#[inline]
173+
pub fn has_point(&self, point: Vector2) -> bool {
174+
let point = point - self.position;
175+
176+
point.abs() == point && point.x < self.size.x && point.y < self.size.y
177+
}
178+
179+
/// Returns the intersection of this Rect2 and `b`. If the rectangles do not intersect, an empty Rect2 is returned.
180+
///
181+
/// _Godot equivalent: `Rect2.intersection(Rect2 b)`_
182+
#[inline]
183+
pub fn intersection(&self, b: &Self) -> Option<Self> {
184+
if !self.intersects(b, true) {
185+
return None;
186+
}
187+
188+
let mut rect = *b;
189+
rect.position.x = rect.position.x.max(self.position.x);
190+
rect.position.y = rect.position.y.max(self.position.y);
191+
192+
let end = self.end();
193+
let end_b = b.end();
194+
195+
rect.size.x = end.x.min(end_b.x) - rect.position.x;
196+
rect.size.y = end.y.min(end_b.y) - rect.position.y;
197+
198+
Some(rect)
199+
}
200+
201+
/// Returns `true` if the Rect2 overlaps with `b` (i.e. they have at least one point in common).
202+
///
203+
/// If `include_borders` is `true`, they will also be considered overlapping if their borders touch, even without intersection.
204+
///
205+
/// _Godot equivalent: `Rect2.intersects(Rect2 b, bool include_borders)`_
206+
#[inline]
207+
pub fn intersects(&self, b: &Self, include_borders: bool) -> bool {
208+
if include_borders {
209+
return self.position.x <= b.position.x + b.size.x
210+
&& self.position.x + self.size.x >= b.position.x
211+
&& self.position.y <= b.position.y + b.size.y
212+
&& self.position.y + self.size.y >= b.position.y;
213+
}
214+
215+
self.position.x < b.position.x + b.size.x
216+
&& self.position.x + self.size.x > b.position.x
217+
&& self.position.y < b.position.y + b.size.y
218+
&& self.position.y + self.size.y > b.position.y
219+
}
220+
47221
/// Create a new `Rect2` from a `Rect2i`, using `as` for `i32` to `real` conversions.
48222
///
49223
/// _Godot equivalent: `Rect2(Rect2i from)`_

0 commit comments

Comments
 (0)