Skip to content

Commit 08ea421

Browse files
bors[bot]juliohqBromeon
authored
Merge #242
242: Reimplement Rect2 functions r=Bromeon a=juliohq Reimplement Rect2 functions as proposed by #209. It's missing to reimplement `is_infinite()` somehow, since it needs an `inf` type. Co-authored-by: juliohq <[email protected]> Co-authored-by: Jan Haller <[email protected]>
2 parents 00a3005 + df42396 commit 08ea421

File tree

4 files changed

+289
-22
lines changed

4 files changed

+289
-22
lines changed

godot-core/src/builtin/aabb.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,12 @@ impl Aabb {
3838
/// Create a new `Aabb` with the first corner at `position` and opposite corner at `end`.
3939
#[inline]
4040
pub fn from_corners(position: Vector3, end: Vector3) -> Self {
41-
Self {
42-
position,
43-
size: position + end,
44-
}
41+
// Cannot use floating point arithmetic in const functions.
42+
Self::new(position, end - position)
4543
}
4644

47-
/// Returns an AABB with equivalent position and size,
48-
/// modified so that the most-negative corner is the origin and the size is positive.
45+
/// Returns an AABB with the same geometry, with most-negative corner as `position` and non-negative `size`.
46+
#[inline]
4947
pub fn abs(&self) -> Self {
5048
Aabb {
5149
position: self.position + self.size.coord_min(Vector3::ZERO),
@@ -141,6 +139,7 @@ impl Aabb {
141139
}
142140

143141
/// Returns true if the AABB has a volume, and false if the AABB is flat, linear, empty, or has a negative size.
142+
#[inline]
144143
pub fn has_volume(&self) -> bool {
145144
self.size.x > 0.0 && self.size.y > 0.0 && self.size.z > 0.0
146145
}
@@ -168,6 +167,7 @@ impl Aabb {
168167
}
169168

170169
/// Returns `true` if this AABB is finite, by calling `@GlobalScope.is_finite` on each component.
170+
#[inline]
171171
pub fn is_finite(&self) -> bool {
172172
self.position.is_finite() && self.size.is_finite()
173173
}
@@ -187,6 +187,7 @@ impl Aabb {
187187
}
188188

189189
/// Returns the normalized longest axis of the AABB.
190+
#[inline]
190191
pub fn longest_axis(&self) -> Vector3 {
191192
match self.longest_axis_index() {
192193
Vector3Axis::X => Vector3::RIGHT,
@@ -196,16 +197,19 @@ impl Aabb {
196197
}
197198

198199
/// Returns the index of the longest axis of the AABB (according to Vector3's AXIS_* constants).
200+
#[inline]
199201
pub fn longest_axis_index(&self) -> Vector3Axis {
200202
self.size.max_axis_index()
201203
}
202204

203205
/// Returns the scalar length of the longest axis of the AABB.
206+
#[inline]
204207
pub fn longest_axis_size(&self) -> real {
205208
self.size.x.max(self.size.y.max(self.size.z))
206209
}
207210

208211
/// Returns the normalized shortest axis of the AABB.
212+
#[inline]
209213
pub fn shortest_axis(&self) -> Vector3 {
210214
match self.shortest_axis_index() {
211215
Vector3Axis::X => Vector3::RIGHT,
@@ -215,16 +219,19 @@ impl Aabb {
215219
}
216220

217221
/// Returns the index of the shortest axis of the AABB (according to Vector3::AXIS* enum).
222+
#[inline]
218223
pub fn shortest_axis_index(&self) -> Vector3Axis {
219224
self.size.min_axis_index()
220225
}
221226

222227
/// Returns the scalar length of the shortest axis of the AABB.
228+
#[inline]
223229
pub fn shortest_axis_size(&self) -> real {
224230
self.size.x.min(self.size.y.min(self.size.z))
225231
}
226232

227233
/// Returns the support point in a given direction. This is useful for collision detection algorithms.
234+
#[inline]
228235
pub fn support(&self, dir: Vector3) -> Vector3 {
229236
let half_extents = self.size * 0.5;
230237
let relative_center_point = self.position + half_extents;
@@ -238,9 +245,12 @@ impl Aabb {
238245
half_extents * signs + relative_center_point
239246
}
240247

241-
/// Returns `true` if the AABB overlaps with `b` (i.e. they have at least one point in common).
248+
/// Checks whether two AABBs have at least one point in common.
242249
///
243-
/// _Godot equivalent: `AABB.intersects(AABB b, bool include_borders = false)`_
250+
/// Also returns `true` if the AABBs only touch each other (share a point/edge/face).
251+
/// See [`intersects_exclude_borders`][Self::intersects_exclude_borders] if you want to return `false` in that case.
252+
///
253+
/// _Godot equivalent: `AABB.intersects(AABB b, bool include_borders = true)`_
244254
#[inline]
245255
pub fn intersects(&self, b: &Aabb) -> bool {
246256
let end = self.end();
@@ -253,9 +263,13 @@ impl Aabb {
253263
&& self.position.z <= end_b.z
254264
}
255265

256-
/// Returns `true` if the AABB overlaps with `b` (i.e. they have at least one inner point in common).
266+
/// Checks whether two AABBs have at least one _inner_ point in common (not on the borders).
257267
///
258-
/// _Godot equivalent: `AABB.intersects(AABB b, bool include_borders = true)`_
268+
/// Returns `false` if the AABBs only touch each other (share a point/edge/face).
269+
/// See [`intersects`][Self::intersects] if you want to return `true` in that case.
270+
///
271+
/// _Godot equivalent: `AABB.intersects(AABB b, bool include_borders = false)`_
272+
#[inline]
259273
pub fn intersects_exclude_borders(&self, &b: &Aabb) -> bool {
260274
let end = self.end();
261275
let end_b = b.end();
@@ -269,6 +283,7 @@ impl Aabb {
269283
}
270284

271285
/// Returns `true` if the AABB is on both sides of a plane.
286+
#[inline]
272287
pub fn intersects_plane(&self, plane: &Plane) -> bool {
273288
// The set of the edges of the AABB.
274289
let points = [
@@ -301,6 +316,7 @@ impl Aabb {
301316
///
302317
/// # Panics
303318
/// If `self.size` is negative.
319+
#[inline]
304320
pub fn intersects_ray(&self, from: Vector3, dir: Vector3) -> bool {
305321
self.assert_nonnegative();
306322

@@ -320,6 +336,7 @@ impl Aabb {
320336
///
321337
/// # Panics
322338
/// If `self.size` is negative.
339+
#[inline]
323340
pub fn intersects_segment(&self, from: Vector3, to: Vector3) -> bool {
324341
self.assert_nonnegative();
325342

godot-core/src/builtin/rect2.rs

+165-12
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use godot_ffi as sys;
88
use sys::{ffi_methods, GodotFfi};
99

1010
use crate::builtin::math::ApproxEq;
11-
use crate::builtin::{real, Rect2i, Vector2};
11+
use crate::builtin::{real, Rect2i, RectSide, Vector2};
1212

1313
/// 2D axis-aligned bounding box.
1414
///
@@ -35,6 +35,13 @@ impl Rect2 {
3535
Self { position, size }
3636
}
3737

38+
/// Create a new `Rect2` with the first corner at `position` and the opposite corner at `end`.
39+
#[inline]
40+
pub fn from_corners(position: Vector2, end: Vector2) -> Self {
41+
// Cannot use floating point arithmetic in const functions.
42+
Self::new(position, end - position)
43+
}
44+
3845
/// Create a new `Rect2` from four reals representing position `(x,y)` and size `(width,height)`.
3946
///
4047
/// _Godot equivalent: `Rect2(float x, float y, float width, float height)`_
@@ -57,33 +64,180 @@ impl Rect2 {
5764
}
5865
}
5966

60-
/// Create a new `Rect2` with the first corner at `position` and the opposite corner at `end`.
67+
/// Returns a rectangle with the same geometry, with top-left corner as `position` and non-negative size.
6168
#[inline]
62-
pub fn from_corners(position: Vector2, end: Vector2) -> Self {
69+
pub fn abs(&self) -> Self {
6370
Self {
64-
position,
65-
size: position + end,
71+
position: self.position + self.size.coord_min(Vector2::ZERO),
72+
size: self.size.abs(),
6673
}
6774
}
6875

69-
/// The end of the `Rect2` calculated as `position + size`.
76+
/// Whether `self` covers at least the entire area of `b` (and possibly more).
77+
#[inline]
78+
pub fn encloses(&self, b: Rect2) -> bool {
79+
let end = self.end();
80+
let b_end = b.end();
81+
82+
b.position.x >= self.position.x
83+
&& b.position.y >= self.position.y
84+
&& b_end.x <= end.x
85+
&& b_end.y <= end.y
86+
}
87+
88+
/// Returns a copy of this rectangle expanded to include a given point.
89+
///
90+
/// Note: This method is not reliable for `Rect2` with a negative size. Use [`abs`][Self::abs]
91+
/// to get a positive sized equivalent rectangle for expanding.
92+
#[inline]
93+
pub fn expand(&self, to: Vector2) -> Self {
94+
self.merge(Rect2::new(to, Vector2::ZERO))
95+
}
96+
97+
/// Returns a larger rectangle that contains this `Rect2` and `b`.
98+
///
99+
/// Note: This method is not reliable for `Rect2` with a negative size. Use [`abs`][Self::abs]
100+
/// to get a positive sized equivalent rectangle for merging.
101+
#[inline]
102+
pub fn merge(&self, b: Self) -> Self {
103+
let position = self.position.coord_min(b.position);
104+
let end = self.end().coord_max(b.end());
105+
106+
Self::from_corners(position, end)
107+
}
108+
109+
/// Returns the area of the rectangle.
110+
#[inline]
111+
pub fn area(&self) -> real {
112+
self.size.x * self.size.y
113+
}
114+
115+
/// Returns the center of the Rect2, which is equal to `position + (size / 2)`.
116+
#[inline]
117+
pub fn center(&self) -> Vector2 {
118+
self.position + (self.size / 2.0)
119+
}
120+
121+
/// Returns a copy of the Rect2 grown by the specified `amount` on all sides.
122+
#[inline]
123+
#[must_use]
124+
pub fn grow(&self, amount: real) -> Self {
125+
let position = self.position - Vector2::new(amount, amount);
126+
let size = self.size + Vector2::new(amount, amount) * 2.0;
127+
128+
Self { position, size }
129+
}
130+
131+
/// Returns a copy of the Rect2 grown by the specified amount on each side individually.
132+
#[inline]
133+
pub fn grow_individual(&self, left: real, top: real, right: real, bottom: real) -> Self {
134+
Self::from_components(
135+
self.position.x - left,
136+
self.position.y - top,
137+
self.size.x + left + right,
138+
self.size.y + top + bottom,
139+
)
140+
}
141+
142+
/// Returns a copy of the `Rect2` grown by the specified `amount` on the specified `RectSide`.
70143
///
71-
/// _Godot equivalent: `Rect2.size` property_
72-
#[doc(alias = "size")]
144+
/// `amount` may be negative, but care must be taken: If the resulting `size` has
145+
/// negative components the computation may be incorrect.
146+
#[inline]
147+
pub fn grow_side(&self, side: RectSide, amount: real) -> Self {
148+
match side {
149+
RectSide::Left => self.grow_individual(amount, 0.0, 0.0, 0.0),
150+
RectSide::Top => self.grow_individual(0.0, amount, 0.0, 0.0),
151+
RectSide::Right => self.grow_individual(0.0, 0.0, amount, 0.0),
152+
RectSide::Bottom => self.grow_individual(0.0, 0.0, 0.0, amount),
153+
}
154+
}
155+
156+
/// Returns `true` if the Rect2 has area, and `false` if the Rect2 is linear, empty, or has a negative size. See also `get_area`.
157+
#[inline]
158+
pub fn has_area(&self) -> bool {
159+
self.size.x > 0.0 && self.size.y > 0.0
160+
}
161+
162+
/// 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.
163+
///
164+
/// 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.
165+
#[inline]
166+
pub fn has_point(&self, point: Vector2) -> bool {
167+
let point = point - self.position;
168+
169+
point.abs() == point && point.x < self.size.x && point.y < self.size.y
170+
}
171+
172+
/// Returns the intersection of this Rect2 and `b`. If the rectangles do not intersect, an empty Rect2 is returned.
173+
#[inline]
174+
pub fn intersection(&self, b: Self) -> Option<Self> {
175+
if !self.intersects(b) {
176+
return None;
177+
}
178+
179+
let mut rect = b;
180+
rect.position = rect.position.coord_max(self.position);
181+
182+
let end = self.end();
183+
let end_b = b.end();
184+
rect.size = end.coord_min(end_b) - rect.position;
185+
186+
Some(rect)
187+
}
188+
189+
/// Checks whether two rectangles have at least one point in common.
190+
///
191+
/// Also returns `true` if the rects only touch each other (share a point/edge).
192+
/// See [`intersects_exclude_borders`][Self::intersects_exclude_borders] if you want to return `false` in that case.
193+
///
194+
/// _Godot equivalent: `Rect2.intersects(Rect2 b, bool include_borders = true)`_
195+
#[inline]
196+
pub fn intersects(&self, b: Self) -> bool {
197+
let end = self.end();
198+
let end_b = b.end();
199+
200+
self.position.x <= end_b.x
201+
&& end.x >= b.position.x
202+
&& self.position.y <= end_b.y
203+
&& end.y >= b.position.y
204+
}
205+
206+
/// Checks whether two rectangles have at least one _inner_ point in common (not on the borders).
207+
///
208+
/// Returns `false` if the rects only touch each other (share a point/edge).
209+
/// See [`intersects`][Self::intersects] if you want to return `true` in that case.
210+
///
211+
/// _Godot equivalent: `Rect2.intersects(AABB b, bool include_borders = false)`_
212+
#[inline]
213+
pub fn intersects_exclude_borders(&self, b: Self) -> bool {
214+
let end = self.end();
215+
let end_b = b.end();
216+
217+
self.position.x < end_b.x
218+
&& end.x > b.position.x
219+
&& self.position.y < end_b.y
220+
&& end.y > b.position.y
221+
}
222+
223+
/// Returns `true` if this Rect2 is finite, by calling `@GlobalScope.is_finite` on each component.
224+
#[inline]
225+
pub fn is_finite(&self) -> bool {
226+
self.position.is_finite() && self.size.is_finite()
227+
}
228+
229+
/// The end of the `Rect2` calculated as `position + size`.
73230
#[inline]
74231
pub fn end(&self) -> Vector2 {
75232
self.position + self.size
76233
}
77234

78235
/// Set size based on desired end-point.
79-
///
80-
/// _Godot equivalent: `Rect2.size` property_
81236
#[inline]
82237
pub fn set_end(&mut self, end: Vector2) {
83238
self.size = end - self.position
84239
}
85240

86-
/* Add in when `Rect2::abs()` is implemented.
87241
/// Assert that the size of the `Rect2` is not negative.
88242
///
89243
/// Certain functions will fail to give a correct result if the size is negative.
@@ -95,7 +249,6 @@ impl Rect2 {
95249
self.size
96250
);
97251
}
98-
*/
99252
}
100253

101254
// SAFETY:

itest/rust/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod packed_array_test;
3030
mod plane_test;
3131
mod projection_test;
3232
mod quaternion_test;
33+
mod rect2_test;
3334
mod rect2i_test;
3435
mod rid_test;
3536
mod signal_test;

0 commit comments

Comments
 (0)