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, Eq)]
26pub struct BlockLocal;
27
28impl Space for BlockLocal {
29 const ZERO_SPAN_IS_EMPTY: bool = true;
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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]
274 pub fn distance_to_sqr(self, point: DVec3) -> f64 {
275 let dx = f64::max(f64::max(self.min.x - point.x, point.x - self.max.x), 0.0);
276 let dy = f64::max(f64::max(self.min.y - point.y, point.y - self.max.y), 0.0);
277 let dz = f64::max(f64::max(self.min.z - point.z, point.z - self.max.z), 0.0);
278 dx * dx + dy * dy + dz * dz
279 }
280
281 #[must_use]
283 pub fn distance_to_sqr_aabb(self, other: Self) -> f64 {
284 let dx = f64::max(
285 f64::max(self.min.x - other.max.x, other.min.x - self.max.x),
286 0.0,
287 );
288 let dy = f64::max(
289 f64::max(self.min.y - other.max.y, other.min.y - self.max.y),
290 0.0,
291 );
292 let dz = f64::max(
293 f64::max(self.min.z - other.max.z, other.min.z - self.max.z),
294 0.0,
295 );
296 dx * dx + dy * dy + dz * dz
297 }
298
299 #[must_use]
303 pub const fn closest_point_to(self, point: DVec3) -> DVec3 {
304 DVec3::new(
305 point.x.clamp(self.min.x, self.max.x),
306 point.y.clamp(self.min.y, self.max.y),
307 point.z.clamp(self.min.z, self.max.z),
308 )
309 }
310}
311
312impl<I> Aabb<IVec3, I> {
313 #[must_use]
315 #[inline]
316 pub const fn min_corner(&self) -> IVec3 {
317 self.min
318 }
319
320 #[must_use]
322 #[inline]
323 pub const fn max_corner(&self) -> IVec3 {
324 self.max
325 }
326
327 #[must_use]
329 #[inline]
330 pub const fn min_x(&self) -> i32 {
331 self.min.x
332 }
333
334 #[must_use]
336 #[inline]
337 pub const fn min_y(&self) -> i32 {
338 self.min.y
339 }
340
341 #[must_use]
343 #[inline]
344 pub const fn min_z(&self) -> i32 {
345 self.min.z
346 }
347
348 #[must_use]
350 #[inline]
351 pub const fn max_x(&self) -> i32 {
352 self.max.x
353 }
354
355 #[must_use]
357 #[inline]
358 pub const fn max_y(&self) -> i32 {
359 self.max.y
360 }
361
362 #[must_use]
364 #[inline]
365 pub const fn max_z(&self) -> i32 {
366 self.max.z
367 }
368}
369
370impl<T: AabbVector, I> Aabb<T, I> {
371 #[must_use]
373 pub fn deflate(self, amount: T::Scalar) -> Self {
374 self.inflate(-amount)
375 }
376}
377
378impl<T: AabbVector, I: Space> Aabb<T, I> {
379 #[inline]
380 fn axis_overlaps(min1: T::Scalar, max1: T::Scalar, min2: T::Scalar, max2: T::Scalar) -> bool {
381 if I::ZERO_SPAN_IS_EMPTY {
382 min1 < max2 && max1 > min2
383 } else {
384 min1 <= max2 && max1 >= min2
385 }
386 }
387
388 #[inline]
389 fn axis_contains(min: T::Scalar, max: T::Scalar, v: T::Scalar) -> bool {
390 if I::ZERO_SPAN_IS_EMPTY {
391 v >= min && v < max
392 } else {
393 v >= min && v <= max
394 }
395 }
396
397 pub fn is_empty(&self) -> bool {
399 if I::ZERO_SPAN_IS_EMPTY {
400 self.min.x() >= self.max.x()
401 || self.min.y() >= self.max.y()
402 || self.min.z() >= self.max.z()
403 } else {
404 self.min.x() > self.max.x()
405 || self.min.y() > self.max.y()
406 || self.min.z() > self.max.z()
407 }
408 }
409
410 #[must_use]
412 pub fn intersects(self, other: Self) -> bool {
413 self.intersects_bounds(other.min, other.max)
414 }
415
416 #[must_use]
418 pub fn intersects_bounds(self, min: T, max: T) -> bool {
419 Self::axis_overlaps(self.min.x(), self.max.x(), min.x(), max.x())
420 && Self::axis_overlaps(self.min.y(), self.max.y(), min.y(), max.y())
421 && Self::axis_overlaps(self.min.z(), self.max.z(), min.z(), max.z())
422 }
423
424 #[must_use]
426 pub fn intersects_xz(
427 self,
428 min_x: T::Scalar,
429 min_z: T::Scalar,
430 max_x: T::Scalar,
431 max_z: T::Scalar,
432 ) -> bool {
433 Self::axis_overlaps(self.min.x(), self.max.x(), min_x, max_x)
434 && Self::axis_overlaps(self.min.z(), self.max.z(), min_z, max_z)
435 }
436
437 #[must_use]
439 pub fn contains(self, pos: T) -> bool {
440 self.contains_xyz(pos.x(), pos.y(), pos.z())
441 }
442
443 #[must_use]
445 pub fn contains_xyz(self, x: T::Scalar, y: T::Scalar, z: T::Scalar) -> bool {
446 Self::axis_contains(self.min.x(), self.max.x(), x)
447 && Self::axis_contains(self.min.y(), self.max.y(), y)
448 && Self::axis_contains(self.min.z(), self.max.z(), z)
449 }
450}
451
452impl<T: AabbVector, I: Space> Aabb<T, I> {
453 #[inline]
454 fn span(raw: T::Scalar) -> T::Scalar {
455 if I::ZERO_SPAN_IS_EMPTY {
456 raw
457 } else {
458 raw + T::Scalar::from(1u8)
459 }
460 }
461
462 #[must_use]
464 pub fn width(&self) -> T::Scalar {
465 Self::span(self.max.x() - self.min.x())
466 }
467
468 #[must_use]
470 pub fn height(&self) -> T::Scalar {
471 Self::span(self.max.y() - self.min.y())
472 }
473
474 #[must_use]
476 pub fn depth(&self) -> T::Scalar {
477 Self::span(self.max.z() - self.min.z())
478 }
479
480 #[must_use]
482 pub fn center(&self) -> T {
483 let two = T::Scalar::from(2u8);
484 T::new(
485 self.min.x() + Self::span(self.max.x() - self.min.x()) / two,
486 self.min.y() + Self::span(self.max.y() - self.min.y()) / two,
487 self.min.z() + Self::span(self.max.z() - self.min.z()) / two,
488 )
489 }
490}
491
492impl<I: Space> Aabb<IVec3, I> {
493 #[must_use]
495 pub fn contains_blockpos(self, pos: BlockPos) -> bool {
496 self.contains(pos.0)
497 }
498}
499
500impl<I: Space> Aabb<DVec3, I> {
501 pub const FULL_BLOCK: Self = Self::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
503
504 pub const EMPTY: Self = Self::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
506
507 #[must_use]
509 pub const fn new(
510 min_x: f64,
511 min_y: f64,
512 min_z: f64,
513 max_x: f64,
514 max_y: f64,
515 max_z: f64,
516 ) -> Self {
517 let (min_x, max_x) = ordered_pair(min_x, max_x);
518 let (min_y, max_y) = ordered_pair(min_y, max_y);
519 let (min_z, max_z) = ordered_pair(min_z, max_z);
520 Self {
521 min: DVec3::new(min_x, min_y, min_z),
522 max: DVec3::new(max_x, max_y, max_z),
523 p: PhantomData,
524 }
525 }
526
527 #[must_use]
531 pub fn of_size(center: DVec3, size_x: f64, size_y: f64, size_z: f64) -> Self {
532 let half_x = size_x / 2.0;
533 let half_y = size_y / 2.0;
534 let half_z = size_z / 2.0;
535 Self::new(
536 center.x - half_x,
537 center.y - half_y,
538 center.z - half_z,
539 center.x + half_x,
540 center.y + half_y,
541 center.z + half_z,
542 )
543 }
544
545 #[must_use]
547 pub fn size(self) -> f64 {
548 (self.width() + self.height() + self.depth()) / 3.0
549 }
550
551 #[must_use]
553 pub fn expand_towards(self, delta: DVec3) -> Self {
554 Self {
555 min: self.min + delta.min(DVec3::ZERO),
556 max: self.max + delta.max(DVec3::ZERO),
557 p: PhantomData,
558 }
559 }
560
561 #[must_use]
566 pub fn contract(self, delta: DVec3) -> Self {
567 let mut min = self.min;
568 let mut max = self.max;
569
570 if delta.x < 0.0 {
571 min.x -= delta.x;
572 } else if delta.x > 0.0 {
573 max.x -= delta.x;
574 }
575
576 if delta.y < 0.0 {
577 min.y -= delta.y;
578 } else if delta.y > 0.0 {
579 max.y -= delta.y;
580 }
581
582 if delta.z < 0.0 {
583 min.z -= delta.z;
584 } else if delta.z > 0.0 {
585 max.z -= delta.z;
586 }
587
588 Self {
589 min,
590 max,
591 p: PhantomData,
592 }
593 }
594}
595
596impl Aabb<DVec3, BlockLocal> {
597 #[must_use]
599 pub fn at_block(self, pos: BlockPos) -> Aabb<DVec3, World> {
600 let offset = DVec3::new(f64::from(pos.x()), f64::from(pos.y()), f64::from(pos.z()));
601 Aabb {
602 min: self.min + offset,
603 max: self.max + offset,
604 p: PhantomData,
605 }
606 }
607}
608
609impl Aabb<DVec3, World> {
610 #[must_use]
612 pub fn entity_box(x: f64, y: f64, z: f64, half_width: f64, height: f64) -> Self {
613 Self::new(
614 x - half_width,
615 y,
616 z - half_width,
617 x + half_width,
618 y + height,
619 z + half_width,
620 )
621 }
622
623 #[must_use]
625 pub fn intersects_block(self, pos: BlockPos) -> bool {
626 let min = DVec3::new(f64::from(pos.x()), f64::from(pos.y()), f64::from(pos.z()));
627 let max = min + DVec3::ONE;
628 self.intersects_bounds(min, max)
629 }
630}
631
632impl Aabb<IVec3, Structure> {
633 #[must_use]
635 pub const fn new(pos1: IVec3, pos2: IVec3) -> Self {
636 let (min_x, max_x) = ordered_pair_i32(pos1.x, pos2.x);
637 let (min_y, max_y) = ordered_pair_i32(pos1.y, pos2.y);
638 let (min_z, max_z) = ordered_pair_i32(pos1.z, pos2.z);
639 Self {
640 min: IVec3::new(min_x, min_y, min_z),
641 max: IVec3::new(max_x, max_y, max_z),
642 p: PhantomData,
643 }
644 }
645
646 #[must_use]
648 pub const fn from_corners(a: BlockPos, b: BlockPos) -> Self {
649 Self::new(a.0, b.0)
650 }
651
652 #[must_use]
654 pub fn distance_to_sqr(self, point: DVec3) -> f64 {
655 let min_x = f64::from(self.min_x());
656 let min_y = f64::from(self.min_y());
657 let min_z = f64::from(self.min_z());
658 let max_x = f64::from(self.max_x());
659 let max_y = f64::from(self.max_y());
660 let max_z = f64::from(self.max_z());
661
662 let dx = f64::max(f64::max(min_x - point.x, point.x - max_x), 0.0);
663 let dy = f64::max(f64::max(min_y - point.y, point.y - max_y), 0.0);
664 let dz = f64::max(f64::max(min_z - point.z, point.z - max_z), 0.0);
665 dx * dx + dy * dy + dz * dz
666 }
667}
668
669#[cfg(test)]
670mod tests {
671 use super::*;
672
673 #[test]
674 fn constructors_normalize_endpoints_like_vanilla() {
675 let aabb = WorldAabb::new(3.0, 4.0, 5.0, 1.0, 2.0, 0.0);
676 assert_eq!(aabb.min_x(), 1.0);
677 assert_eq!(aabb.min_y(), 2.0);
678 assert_eq!(aabb.min_z(), 0.0);
679 assert_eq!(aabb.max_x(), 3.0);
680 assert_eq!(aabb.max_y(), 4.0);
681 assert_eq!(aabb.max_z(), 5.0);
682 }
683
684 #[test]
685 fn inflate_and_deflate_normalize_inverted_bounds() {
686 let aabb = WorldAabb::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0).deflate(0.75);
687 assert_eq!(aabb.min_corner(), DVec3::splat(0.25));
688 assert_eq!(aabb.max_corner(), DVec3::splat(0.75));
689
690 let bbox = BoundingBox::new(IVec3::ZERO, IVec3::splat(5)).inflate(-4);
691 assert_eq!(bbox.min_corner(), IVec3::splat(1));
692 assert_eq!(bbox.max_corner(), IVec3::splat(4));
693 }
694
695 #[test]
696 fn of_size_builds_vanilla_centered_box() {
697 let aabb = WorldAabb::of_size(DVec3::new(10.0, 64.0, -2.0), 2.0, 4.0, 6.0);
698
699 assert_eq!(aabb.min_corner(), DVec3::new(9.0, 62.0, -5.0));
700 assert_eq!(aabb.max_corner(), DVec3::new(11.0, 66.0, 1.0));
701 }
702
703 #[test]
704 fn block_local_aabb_translates_to_world_space() {
705 let local = BlockLocalAabb::new(0.0, 0.25, 0.0, 1.0, 0.75, 1.0);
706 let world = local.at_block(BlockPos::new(10, 64, -5));
707
708 assert_eq!(world.min_x(), 10.0);
709 assert_eq!(world.min_y(), 64.25);
710 assert_eq!(world.min_z(), -5.0);
711 assert_eq!(world.max_x(), 11.0);
712 assert_eq!(world.max_y(), 64.75);
713 assert_eq!(world.max_z(), -4.0);
714 }
715
716 #[test]
717 fn contains_uses_vanilla_exclusive_max_edge() {
718 let aabb = WorldAabb::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
719
720 assert!(aabb.contains_xyz(0.0, 0.5, 0.5));
721 assert!(aabb.contains_xyz(0.999, 0.5, 0.5));
722 assert!(!aabb.contains_xyz(1.0, 0.5, 0.5));
723 }
724
725 #[test]
726 fn world_aabb_distance_to_sqr_uses_nearest_surface_point() {
727 let aabb = WorldAabb::new(1.0, 2.0, 3.0, 4.0, 6.0, 8.0);
728
729 assert_eq!(aabb.distance_to_sqr(DVec3::new(2.0, 3.0, 4.0)), 0.0);
730 assert_eq!(aabb.distance_to_sqr(DVec3::new(0.0, 1.0, 1.0)), 6.0);
731 assert_eq!(aabb.distance_to_sqr(DVec3::new(5.0, 7.0, 9.0)), 3.0);
732 }
733
734 #[test]
735 fn distance_to_sqr_aabb_uses_nearest_gap_between_boxes() {
736 let aabb = WorldAabb::new(1.0, 2.0, 3.0, 4.0, 6.0, 8.0);
737
738 assert_eq!(
740 aabb.distance_to_sqr_aabb(WorldAabb::new(2.0, 3.0, 4.0, 5.0, 7.0, 9.0)),
741 0.0
742 );
743 assert_eq!(
745 aabb.distance_to_sqr_aabb(WorldAabb::new(-1.0, 0.0, 0.0, 0.0, 1.0, 1.0)),
746 6.0
747 );
748 assert_eq!(
750 aabb.distance_to_sqr_aabb(WorldAabb::new(5.0, 7.0, 9.0, 6.0, 8.0, 10.0)),
751 3.0
752 );
753 }
754
755 #[test]
756 fn closest_point_to_clamps_to_box_bounds() {
757 let aabb = WorldAabb::new(1.0, 2.0, 3.0, 4.0, 6.0, 8.0);
758
759 assert_eq!(
760 aabb.closest_point_to(DVec3::new(0.0, 4.0, 10.0)),
761 DVec3::new(1.0, 4.0, 8.0)
762 );
763 assert_eq!(
764 aabb.closest_point_to(DVec3::new(2.0, 3.0, 4.0)),
765 DVec3::new(2.0, 3.0, 4.0)
766 );
767 }
768
769 #[test]
770 fn expand_towards_covers_start_and_end() {
771 let aabb = WorldAabb::new(1.0, 1.0, 1.0, 2.0, 2.0, 2.0);
772 let swept = aabb.expand_towards(DVec3::new(-0.5, 1.5, 0.0));
773
774 assert_eq!(swept.min_x(), 0.5);
775 assert_eq!(swept.min_y(), 1.0);
776 assert_eq!(swept.min_z(), 1.0);
777 assert_eq!(swept.max_x(), 2.0);
778 assert_eq!(swept.max_y(), 3.5);
779 assert_eq!(swept.max_z(), 2.0);
780 }
781}