Skip to content

Commit 26ab79b

Browse files
feat: add support for point radii
1 parent 1ea279d commit 26ab79b

1 file changed

Lines changed: 94 additions & 3 deletions

File tree

capt/src/lib.rs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ where
390390
starts: Box<[I]>,
391391
/// The sets of afforded points for each cell.
392392
afforded: [Box<[MySimd<A, L>]>; K],
393+
r_point: A,
393394
}
394395

395396
#[repr(C)]
@@ -456,11 +457,46 @@ where
456457
.expect("index type I must be able to support all points in CAPT during construction")
457458
}
458459

460+
/// Construct a new CAPT containing all the points in `points` with a point radius `r_point`.
461+
///
462+
/// `r_range` is a `(minimum, maximum)` pair containing the lower and upper bound on the
463+
/// radius of the balls which will be queried against the tree.
464+
///
465+
/// # Panics
466+
///
467+
/// This function will panic if there are too many points in the tree to be addressed by `I`, or
468+
/// if any points contain non-finite non-real value. This can even be the case if there are
469+
/// fewer points in `points` than can be addressed by `I` as the CAPT may duplicate points
470+
/// for efficiency.
471+
///
472+
/// # Examples
473+
///
474+
/// ```
475+
/// let points = [[0.0]];
476+
///
477+
/// let capt = capt::Capt::<1>::with_point_radius(&points, (0.0, f32::INFINITY), 0.2);
478+
///
479+
/// assert!(capt.collides(&[1.0], 1.5));
480+
/// assert!(!capt.collides(&[1.0], 0.5));
481+
/// ```
482+
///
483+
/// If there are too many points in `points`, this could cause a panic!
484+
///
485+
/// ```rust,should_panic
486+
/// let points = [[0.0]; 256];
487+
///
488+
/// // note that we are using `u8` as our index type
489+
/// let capt = capt::Capt::<1, 8, f32, u8>::with_point_radius(&points, (0.0, f32::INFINITY), 0.2);
490+
/// ```
491+
pub fn with_point_radius(points: &[[A; K]], r_range: (A, A), r_point: A) -> Self {
492+
Self::try_with_point_radius(points, r_range, r_point)
493+
.expect("index type I must be able to support all points in CAPT during construction")
494+
}
495+
459496
/// Construct a new CAPT containing all the points in `points`, checking for index overflow.
460497
///
461498
/// `r_range` is a `(minimum, maximum)` pair containing the lower and upper bound on the
462499
/// radius of the balls which will be queried against the tree.
463-
/// `rng` is a random number generator.
464500
///
465501
/// # Errors
466502
///
@@ -489,6 +525,48 @@ where
489525
/// assert!(opt.is_err());
490526
/// ```
491527
pub fn try_new(points: &[[A; K]], r_range: (A, A)) -> Result<Self, NewCaptError> {
528+
Self::try_with_point_radius(points, r_range, A::ZERO)
529+
}
530+
531+
/// Construct a new CAPT containing all the points in `points` with a point radius `r_point`,
532+
/// checking for index overflow.
533+
///
534+
/// `r_range` is a `(minimum, maximum)` pair containing the lower and upper bound on the
535+
/// radius of the balls which will be queried against the tree.
536+
///
537+
/// # Errors
538+
///
539+
/// This function will return `Err(NewCaptError::TooManyPoints)` if there are too many points to
540+
/// be indexed by `I`. It will return `Err(NewCaptError::NonFinite)` if any element of
541+
/// `points` is non-finite.
542+
///
543+
/// # Examples
544+
///
545+
/// Unwrapping the output from this function is equivalent to calling
546+
/// [`Capt::with_point_radius`].
547+
///
548+
/// ```
549+
/// let points = [[0.0]];
550+
///
551+
/// let capt = capt::Capt::<1>::try_with_point_radius(&points, (0.0, f32::INFINITY), 0.01).unwrap();
552+
/// ```
553+
///
554+
/// In failure, we get an `Err`.
555+
///
556+
/// ```
557+
/// let points = [[0.0]; 256];
558+
///
559+
/// // note that we are using `u8` as our index type
560+
/// let opt =
561+
/// capt::Capt::<1, 8, f32, u8>::try_with_point_radius(&points, (0.0, f32::INFINITY), 0.01);
562+
///
563+
/// assert!(opt.is_err());
564+
/// ```
565+
pub fn try_with_point_radius(
566+
points: &[[A; K]],
567+
r_range: (A, A),
568+
r_point: A,
569+
) -> Result<Self, NewCaptError> {
492570
let n2 = points.len().next_power_of_two();
493571

494572
if points.iter().any(|p| p.iter().any(|x| !x.is_finite())) {
@@ -534,6 +612,7 @@ where
534612
starts,
535613
afforded: afforded.map(Vec::into_boxed_slice),
536614
aabbs,
615+
r_point,
537616
})
538617
}
539618

@@ -708,7 +787,8 @@ where
708787
/// capt.collides(&[100.0; 3], 100.0)
709788
/// );
710789
/// ```
711-
pub fn collides(&self, center: &[A; K], radius: A) -> bool {
790+
pub fn collides(&self, center: &[A; K], mut radius: A) -> bool {
791+
radius = radius + self.r_point;
712792
// forward pass through the tree
713793
let mut test_idx = 0;
714794
let mut k = 0;
@@ -799,14 +879,15 @@ where
799879
///
800880
/// assert!(tree.collides_simd(&centers, radii));
801881
/// ```
802-
pub fn collides_simd(&self, centers: &[Simd<A, L>; K], radii: Simd<A, L>) -> bool
882+
pub fn collides_simd(&self, centers: &[Simd<A, L>; K], mut radii: Simd<A, L>) -> bool
803883
where
804884
LaneCount<L>: SupportedLaneCount,
805885
Simd<A, L>:
806886
SimdPartialOrd + Sub<Output = Simd<A, L>> + Mul<Output = Simd<A, L>> + AddAssign,
807887
Mask<isize, L>: From<<Simd<A, L> as SimdPartialEq>::Mask>,
808888
A: Axis + AxisSimd<<Simd<A, L> as SimdPartialEq>::Mask>,
809889
{
890+
radii += Simd::splat(self.r_point);
810891
let zs = forward_pass_simd(&self.tests, centers);
811892

812893
let mut inbounds = Mask::splat(true);
@@ -1068,4 +1149,14 @@ mod tests {
10681149
assert!(p0[0] >= median);
10691150
}
10701151
}
1152+
1153+
#[test]
1154+
fn point_radius() {
1155+
let points = [[0.0, 0.0], [0.0, 1.0]];
1156+
let r_range = (0.0, 1.0);
1157+
1158+
let capt: Capt<_, 8, _, u32> = Capt::with_point_radius(&points, r_range, 0.5);
1159+
assert!(capt.collides(&[0.6, 0.0], 0.2));
1160+
assert!(!capt.collides(&[0.6, 0.0], 0.05));
1161+
}
10711162
}

0 commit comments

Comments
 (0)