1use std::marker::PhantomData;
4use std::ops::{Add, Div, Neg, Sub};
5
6use glam::{DVec3, IVec3};
7
8use crate::{BlockPos, axis::Axis};
9
10const fn ordered_pair(a: f64, b: f64) -> (f64, f64) {
11 if a <= b { (a, b) } else { (b, a) }
12}
13
14const fn ordered_pair_i32(a: i32, b: i32) -> (i32, i32) {
15 if a <= b { (a, b) } else { (b, a) }
16}
17
18pub trait Space {
20 const ZERO_SPAN_IS_EMPTY: bool;
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct BlockLocal;
27
28impl Space for BlockLocal {
29 const ZERO_SPAN_IS_EMPTY: bool = true;
30}
31
32#[derive(Debug, Clone, Copy, PartialEq)]
34pub struct World;
35
36impl Space for World {
37 const ZERO_SPAN_IS_EMPTY: bool = true;
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub struct Structure;
43
44impl Space for Structure {
45 const ZERO_SPAN_IS_EMPTY: bool = false;
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub struct Aabb<T, I> {
54 min: T,
56 max: T,
58 p: PhantomData<I>,
59}
60
61pub type BlockLocalAabb = Aabb<DVec3, BlockLocal>;
63
64pub type WorldAabb = Aabb<DVec3, World>;
66
67pub type BoundingBox = Aabb<IVec3, Structure>;
69
70pub trait AabbVector: Copy + Add<Output = Self> + Sub<Output = Self> {
72 type Scalar: Copy
74 + PartialOrd
75 + Add<Output = Self::Scalar>
76 + Sub<Output = Self::Scalar>
77 + Div<Output = Self::Scalar>
78 + Neg<Output = Self::Scalar>
79 + From<u8>;
80
81 fn new(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self;
83
84 fn x(self) -> Self::Scalar;
86
87 fn y(self) -> Self::Scalar;
89
90 fn z(self) -> Self::Scalar;
92
93 #[must_use]
95 fn min(self, other: Self) -> Self;
96
97 #[must_use]
99 fn max(self, other: Self) -> Self;
100}
101
102impl AabbVector for DVec3 {
103 type Scalar = f64;
104
105 fn new(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
106 DVec3::new(x, y, z)
107 }
108
109 fn x(self) -> Self::Scalar {
110 self.x
111 }
112
113 fn y(self) -> Self::Scalar {
114 self.y
115 }
116
117 fn z(self) -> Self::Scalar {
118 self.z
119 }
120
121 fn min(self, other: Self) -> Self {
122 self.min(other)
123 }
124
125 fn max(self, other: Self) -> Self {
126 self.max(other)
127 }
128}
129
130impl AabbVector for IVec3 {
131 type Scalar = i32;
132
133 fn new(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
134 IVec3::new(x, y, z)
135 }
136
137 fn x(self) -> Self::Scalar {
138 self.x
139 }
140
141 fn y(self) -> Self::Scalar {
142 self.y
143 }
144
145 fn z(self) -> Self::Scalar {
146 self.z
147 }
148
149 fn min(self, other: Self) -> Self {
150 self.min(other)
151 }
152
153 fn max(self, other: Self) -> Self {
154 self.max(other)
155 }
156}
157
158impl<T: AabbVector, I> Aabb<T, I> {
159 #[must_use]
161 pub fn min(&self, axis: Axis) -> T::Scalar {
162 match axis {
163 Axis::X => self.min.x(),
164 Axis::Y => self.min.y(),
165 Axis::Z => self.min.z(),
166 }
167 }
168
169 #[must_use]
171 pub fn max(&self, axis: Axis) -> T::Scalar {
172 match axis {
173 Axis::X => self.max.x(),
174 Axis::Y => self.max.y(),
175 Axis::Z => self.max.z(),
176 }
177 }
178
179 #[must_use]
181 pub fn from_min_max(min: T, max: T) -> Self {
182 Self {
183 min: min.min(max),
184 max: min.max(max),
185 p: PhantomData,
186 }
187 }
188
189 #[must_use]
191 pub fn translate(self, delta: T) -> Self {
192 Self {
193 min: self.min + delta,
194 max: self.max + delta,
195 p: PhantomData,
196 }
197 }
198
199 #[must_use]
201 pub fn inflate(self, amount: T::Scalar) -> Self {
202 self.inflate_xyz(amount, amount, amount)
203 }
204
205 #[must_use]
207 pub fn inflate_xyz(self, x: T::Scalar, y: T::Scalar, z: T::Scalar) -> Self {
208 let delta = T::new(x, y, z);
209 Self::from_min_max(self.min - delta, self.max + delta)
210 }
211
212 #[must_use]
214 pub fn encapsulating(a: &Self, b: &Self) -> Self {
215 Self {
216 min: a.min.min(b.min),
217 max: a.max.max(b.max),
218 p: PhantomData,
219 }
220 }
221}
222
223impl<I> Aabb<DVec3, I> {
224 #[must_use]
226 pub const fn min_corner(&self) -> DVec3 {
227 self.min
228 }
229
230 #[must_use]
232 pub const fn max_corner(&self) -> DVec3 {
233 self.max
234 }
235
236 #[must_use]
238 pub const fn min_x(&self) -> f64 {
239 self.min.x
240 }
241
242 #[must_use]
244 pub const fn min_y(&self) -> f64 {
245 self.min.y
246 }
247
248 #[must_use]
250 pub const fn min_z(&self) -> f64 {
251 self.min.z
252 }
253
254 #[must_use]
256 pub const fn max_x(&self) -> f64 {
257 self.max.x
258 }
259
260 #[must_use]
262 pub const fn max_y(&self) -> f64 {
263 self.max.y
264 }
265
266 #[must_use]
268 pub const fn max_z(&self) -> f64 {
269 self.max.z
270 }
271
272 #[must_use]
276 pub fn distance_to_sqr(self, point: DVec3) -> f64 {
277 let dx = f64::max(f64::max(self.min.x - point.x, point.x - self.max.x), 0.0);
278 let dy = f64::max(f64::max(self.min.y - point.y, point.y - self.max.y), 0.0);
279 let dz = f64::max(f64::max(self.min.z - point.z, point.z - self.max.z), 0.0);
280 dx * dx + dy * dy + dz * dz
281 }
282
283 #[must_use]
287 pub const fn closest_point_to(self, point: DVec3) -> DVec3 {
288 DVec3::new(
289 point.x.clamp(self.min.x, self.max.x),
290 point.y.clamp(self.min.y, self.max.y),
291 point.z.clamp(self.min.z, self.max.z),
292 )
293 }
294}
295
296impl<I> Aabb<IVec3, I> {
297 #[must_use]
299 #[inline]
300 pub const fn min_corner(&self) -> IVec3 {
301 self.min
302 }
303
304 #[must_use]
306 #[inline]
307 pub const fn max_corner(&self) -> IVec3 {
308 self.max
309 }
310
311 #[must_use]
313 #[inline]
314 pub const fn min_x(&self) -> i32 {
315 self.min.x
316 }
317
318 #[must_use]
320 #[inline]
321 pub const fn min_y(&self) -> i32 {
322 self.min.y
323 }
324
325 #[must_use]
327 #[inline]
328 pub const fn min_z(&self) -> i32 {
329 self.min.z
330 }
331
332 #[must_use]
334 #[inline]
335 pub const fn max_x(&self) -> i32 {
336 self.max.x
337 }
338
339 #[must_use]
341 #[inline]
342 pub const fn max_y(&self) -> i32 {
343 self.max.y
344 }
345
346 #[must_use]
348 #[inline]
349 pub const fn max_z(&self) -> i32 {
350 self.max.z
351 }
352}
353
354impl<T: AabbVector, I> Aabb<T, I> {
355 #[must_use]
357 pub fn deflate(self, amount: T::Scalar) -> Self {
358 self.inflate(-amount)
359 }
360}
361
362impl<T: AabbVector, I: Space> Aabb<T, I> {
363 #[inline]
364 fn axis_overlaps(min1: T::Scalar, max1: T::Scalar, min2: T::Scalar, max2: T::Scalar) -> bool {
365 if I::ZERO_SPAN_IS_EMPTY {
366 min1 < max2 && max1 > min2
367 } else {
368 min1 <= max2 && max1 >= min2
369 }
370 }
371
372 #[inline]
373 fn axis_contains(min: T::Scalar, max: T::Scalar, v: T::Scalar) -> bool {
374 if I::ZERO_SPAN_IS_EMPTY {
375 v >= min && v < max
376 } else {
377 v >= min && v <= max
378 }
379 }
380
381 pub fn is_empty(&self) -> bool {
383 if I::ZERO_SPAN_IS_EMPTY {
384 self.min.x() >= self.max.x()
385 || self.min.y() >= self.max.y()
386 || self.min.z() >= self.max.z()
387 } else {
388 self.min.x() > self.max.x()
389 || self.min.y() > self.max.y()
390 || self.min.z() > self.max.z()
391 }
392 }
393
394 #[must_use]
396 pub fn intersects(self, other: Self) -> bool {
397 self.intersects_bounds(other.min, other.max)
398 }
399
400 #[must_use]
402 pub fn intersects_bounds(self, min: T, max: T) -> bool {
403 Self::axis_overlaps(self.min.x(), self.max.x(), min.x(), max.x())
404 && Self::axis_overlaps(self.min.y(), self.max.y(), min.y(), max.y())
405 && Self::axis_overlaps(self.min.z(), self.max.z(), min.z(), max.z())
406 }
407
408 #[must_use]
410 pub fn intersects_xz(
411 self,
412 min_x: T::Scalar,
413 min_z: T::Scalar,
414 max_x: T::Scalar,
415 max_z: T::Scalar,
416 ) -> bool {
417 Self::axis_overlaps(self.min.x(), self.max.x(), min_x, max_x)
418 && Self::axis_overlaps(self.min.z(), self.max.z(), min_z, max_z)
419 }
420
421 #[must_use]
423 pub fn contains(self, pos: T) -> bool {
424 self.contains_xyz(pos.x(), pos.y(), pos.z())
425 }
426
427 #[must_use]
429 pub fn contains_xyz(self, x: T::Scalar, y: T::Scalar, z: T::Scalar) -> bool {
430 Self::axis_contains(self.min.x(), self.max.x(), x)
431 && Self::axis_contains(self.min.y(), self.max.y(), y)
432 && Self::axis_contains(self.min.z(), self.max.z(), z)
433 }
434}
435
436impl<T: AabbVector, I: Space> Aabb<T, I> {
437 #[inline]
438 fn span(raw: T::Scalar) -> T::Scalar {
439 if I::ZERO_SPAN_IS_EMPTY {
440 raw
441 } else {
442 raw + T::Scalar::from(1u8)
443 }
444 }
445
446 #[must_use]
448 pub fn width(&self) -> T::Scalar {
449 Self::span(self.max.x() - self.min.x())
450 }
451
452 #[must_use]
454 pub fn height(&self) -> T::Scalar {
455 Self::span(self.max.y() - self.min.y())
456 }
457
458 #[must_use]
460 pub fn depth(&self) -> T::Scalar {
461 Self::span(self.max.z() - self.min.z())
462 }
463
464 #[must_use]
466 pub fn center(&self) -> T {
467 let two = T::Scalar::from(2u8);
468 T::new(
469 self.min.x() + Self::span(self.max.x() - self.min.x()) / two,
470 self.min.y() + Self::span(self.max.y() - self.min.y()) / two,
471 self.min.z() + Self::span(self.max.z() - self.min.z()) / two,
472 )
473 }
474}
475
476impl<I: Space> Aabb<IVec3, I> {
477 #[must_use]
479 pub fn contains_blockpos(self, pos: BlockPos) -> bool {
480 self.contains(pos.0)
481 }
482}
483
484impl<I: Space> Aabb<DVec3, I> {
485 pub const FULL_BLOCK: Self = Self::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
487
488 pub const EMPTY: Self = Self::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
490
491 #[must_use]
493 pub const fn new(
494 min_x: f64,
495 min_y: f64,
496 min_z: f64,
497 max_x: f64,
498 max_y: f64,
499 max_z: f64,
500 ) -> Self {
501 let (min_x, max_x) = ordered_pair(min_x, max_x);
502 let (min_y, max_y) = ordered_pair(min_y, max_y);
503 let (min_z, max_z) = ordered_pair(min_z, max_z);
504 Self {
505 min: DVec3::new(min_x, min_y, min_z),
506 max: DVec3::new(max_x, max_y, max_z),
507 p: PhantomData,
508 }
509 }
510
511 #[must_use]
515 pub fn of_size(center: DVec3, size_x: f64, size_y: f64, size_z: f64) -> Self {
516 let half_x = size_x / 2.0;
517 let half_y = size_y / 2.0;
518 let half_z = size_z / 2.0;
519 Self::new(
520 center.x - half_x,
521 center.y - half_y,
522 center.z - half_z,
523 center.x + half_x,
524 center.y + half_y,
525 center.z + half_z,
526 )
527 }
528
529 #[must_use]
531 pub fn size(self) -> f64 {
532 (self.width() + self.height() + self.depth()) / 3.0
533 }
534
535 #[must_use]
537 pub fn expand_towards(self, delta: DVec3) -> Self {
538 Self {
539 min: self.min + delta.min(DVec3::ZERO),
540 max: self.max + delta.max(DVec3::ZERO),
541 p: PhantomData,
542 }
543 }
544
545 #[must_use]
552 pub fn contract(self, delta: DVec3) -> Self {
553 let mut min = self.min;
554 let mut max = self.max;
555
556 if delta.x < 0.0 {
557 min.x -= delta.x;
558 } else if delta.x > 0.0 {
559 max.x -= delta.x;
560 }
561
562 if delta.y < 0.0 {
563 min.y -= delta.y;
564 } else if delta.y > 0.0 {
565 max.y -= delta.y;
566 }
567
568 if delta.z < 0.0 {
569 min.z -= delta.z;
570 } else if delta.z > 0.0 {
571 max.z -= delta.z;
572 }
573
574 Self {
575 min,
576 max,
577 p: PhantomData,
578 }
579 }
580}
581
582impl Aabb<DVec3, BlockLocal> {
583 #[must_use]
585 pub fn at_block(self, pos: BlockPos) -> Aabb<DVec3, World> {
586 let offset = DVec3::new(f64::from(pos.x()), f64::from(pos.y()), f64::from(pos.z()));
587 Aabb {
588 min: self.min + offset,
589 max: self.max + offset,
590 p: PhantomData,
591 }
592 }
593}
594
595impl Aabb<DVec3, World> {
596 #[must_use]
598 pub fn entity_box(x: f64, y: f64, z: f64, half_width: f64, height: f64) -> Self {
599 Self::new(
600 x - half_width,
601 y,
602 z - half_width,
603 x + half_width,
604 y + height,
605 z + half_width,
606 )
607 }
608
609 #[must_use]
611 pub fn intersects_block(self, pos: BlockPos) -> bool {
612 let min = DVec3::new(f64::from(pos.x()), f64::from(pos.y()), f64::from(pos.z()));
613 let max = min + DVec3::ONE;
614 self.intersects_bounds(min, max)
615 }
616}
617
618impl Aabb<IVec3, Structure> {
619 #[must_use]
621 pub const fn new(pos1: IVec3, pos2: IVec3) -> Self {
622 let (min_x, max_x) = ordered_pair_i32(pos1.x, pos2.x);
623 let (min_y, max_y) = ordered_pair_i32(pos1.y, pos2.y);
624 let (min_z, max_z) = ordered_pair_i32(pos1.z, pos2.z);
625 Self {
626 min: IVec3::new(min_x, min_y, min_z),
627 max: IVec3::new(max_x, max_y, max_z),
628 p: PhantomData,
629 }
630 }
631
632 #[must_use]
634 pub const fn from_corners(a: BlockPos, b: BlockPos) -> Self {
635 Self::new(a.0, b.0)
636 }
637
638 #[must_use]
642 pub fn distance_to_sqr(self, point: DVec3) -> f64 {
643 let min_x = f64::from(self.min_x());
644 let min_y = f64::from(self.min_y());
645 let min_z = f64::from(self.min_z());
646 let max_x = f64::from(self.max_x());
647 let max_y = f64::from(self.max_y());
648 let max_z = f64::from(self.max_z());
649
650 let dx = f64::max(f64::max(min_x - point.x, point.x - max_x), 0.0);
651 let dy = f64::max(f64::max(min_y - point.y, point.y - max_y), 0.0);
652 let dz = f64::max(f64::max(min_z - point.z, point.z - max_z), 0.0);
653 dx * dx + dy * dy + dz * dz
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660
661 #[test]
662 fn constructors_normalize_endpoints_like_vanilla() {
663 let aabb = WorldAabb::new(3.0, 4.0, 5.0, 1.0, 2.0, 0.0);
664 assert_eq!(aabb.min_x(), 1.0);
665 assert_eq!(aabb.min_y(), 2.0);
666 assert_eq!(aabb.min_z(), 0.0);
667 assert_eq!(aabb.max_x(), 3.0);
668 assert_eq!(aabb.max_y(), 4.0);
669 assert_eq!(aabb.max_z(), 5.0);
670 }
671
672 #[test]
673 fn inflate_and_deflate_normalize_inverted_bounds() {
674 let aabb = WorldAabb::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0).deflate(0.75);
675 assert_eq!(aabb.min_corner(), DVec3::splat(0.25));
676 assert_eq!(aabb.max_corner(), DVec3::splat(0.75));
677
678 let bbox = BoundingBox::new(IVec3::ZERO, IVec3::splat(5)).inflate(-4);
679 assert_eq!(bbox.min_corner(), IVec3::splat(1));
680 assert_eq!(bbox.max_corner(), IVec3::splat(4));
681 }
682
683 #[test]
684 fn of_size_builds_vanilla_centered_box() {
685 let aabb = WorldAabb::of_size(DVec3::new(10.0, 64.0, -2.0), 2.0, 4.0, 6.0);
686
687 assert_eq!(aabb.min_corner(), DVec3::new(9.0, 62.0, -5.0));
688 assert_eq!(aabb.max_corner(), DVec3::new(11.0, 66.0, 1.0));
689 }
690
691 #[test]
692 fn block_local_aabb_translates_to_world_space() {
693 let local = BlockLocalAabb::new(0.0, 0.25, 0.0, 1.0, 0.75, 1.0);
694 let world = local.at_block(BlockPos::new(10, 64, -5));
695
696 assert_eq!(world.min_x(), 10.0);
697 assert_eq!(world.min_y(), 64.25);
698 assert_eq!(world.min_z(), -5.0);
699 assert_eq!(world.max_x(), 11.0);
700 assert_eq!(world.max_y(), 64.75);
701 assert_eq!(world.max_z(), -4.0);
702 }
703
704 #[test]
705 fn contains_uses_vanilla_exclusive_max_edge() {
706 let aabb = WorldAabb::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
707
708 assert!(aabb.contains_xyz(0.0, 0.5, 0.5));
709 assert!(aabb.contains_xyz(0.999, 0.5, 0.5));
710 assert!(!aabb.contains_xyz(1.0, 0.5, 0.5));
711 }
712
713 #[test]
714 fn world_aabb_distance_to_sqr_uses_nearest_surface_point() {
715 let aabb = WorldAabb::new(1.0, 2.0, 3.0, 4.0, 6.0, 8.0);
716
717 assert_eq!(aabb.distance_to_sqr(DVec3::new(2.0, 3.0, 4.0)), 0.0);
718 assert_eq!(aabb.distance_to_sqr(DVec3::new(0.0, 1.0, 1.0)), 6.0);
719 assert_eq!(aabb.distance_to_sqr(DVec3::new(5.0, 7.0, 9.0)), 3.0);
720 }
721
722 #[test]
723 fn closest_point_to_clamps_to_box_bounds() {
724 let aabb = WorldAabb::new(1.0, 2.0, 3.0, 4.0, 6.0, 8.0);
725
726 assert_eq!(
727 aabb.closest_point_to(DVec3::new(0.0, 4.0, 10.0)),
728 DVec3::new(1.0, 4.0, 8.0)
729 );
730 assert_eq!(
731 aabb.closest_point_to(DVec3::new(2.0, 3.0, 4.0)),
732 DVec3::new(2.0, 3.0, 4.0)
733 );
734 }
735
736 #[test]
737 fn expand_towards_covers_start_and_end() {
738 let aabb = WorldAabb::new(1.0, 1.0, 1.0, 2.0, 2.0, 2.0);
739 let swept = aabb.expand_towards(DVec3::new(-0.5, 1.5, 0.0));
740
741 assert_eq!(swept.min_x(), 0.5);
742 assert_eq!(swept.min_y(), 1.0);
743 assert_eq!(swept.min_z(), 1.0);
744 assert_eq!(swept.max_x(), 2.0);
745 assert_eq!(swept.max_y(), 3.5);
746 assert_eq!(swept.max_z(), 2.0);
747 }
748}