Skip to content

Commit 20a4a1d

Browse files
committed
- add ray_vs_cylinder + tests, point_sphere_distance + tests
1 parent 7ac46f6 commit 20a4a1d

File tree

2 files changed

+267
-12
lines changed

2 files changed

+267
-12
lines changed

src/lib.rs

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,11 @@ pub fn point_aabb_distance<T: Float, V: VecN<T> + NumberOps<T> + VecFloatOps<T>>
433433
dist(closest_point_on_aabb(p, aabb_min, aabb_max), p)
434434
}
435435

436+
// returns the unsigned distance from point p0 to the sphere (or 2d circle) centred at s0 with radius r
437+
pub fn point_sphere_distance<T: Float, V: VecN<T> + NumberOps<T> + VecFloatOps<T>>(p0: V, s0: V, r: T) -> T {
438+
dist(p0, closest_point_on_sphere(p0, s0, r))
439+
}
440+
436441
/// returns the distance point p is from a triangle defined by t1-t2-t3
437442
pub fn point_triangle_distance<T: Float + FloatOps<T> + NumberOps<T>, V: VecN<T> + NumberOps<T> + VecFloatOps<T>>(p: V, t1: V, t2: V, t3: V) -> T {
438443
let (w23, w31, w12) = barycentric(p, t1, t2, t3);
@@ -1061,6 +1066,79 @@ pub fn ray_vs_capsule<T: Float + FloatOps<T> + NumberOps<T> + SignedNumberOps<T>
10611066
}
10621067
}
10631068

1069+
// returns true if there is an intersection between ray wih origin r0 and direction rv against the cylinder with line c0 - c1 and radius cr
1070+
// the intersection point is stored in ip if one exists
1071+
pub fn ray_vs_cylinder<T: Float + FloatOps<T> + NumberOps<T> + SignedNumberOps<T>>(r0: Vec3<T>, rv: Vec3<T>, c0: Vec3<T>, c1: Vec3<T>, cr: T) -> Option<Vec3<T>> {
1072+
// intesection of ray and infinite cylinder about axis
1073+
// https://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection
1074+
let a = c0;
1075+
let b = c1;
1076+
let v = rv;
1077+
let r = cr;
1078+
1079+
let ab = b - a;
1080+
let ao = r0 - a;
1081+
let aoxab = cross(ao, ab);
1082+
let vxab = cross(v, ab);
1083+
let ab2 = dot(ab, ab);
1084+
1085+
let aa = dot(vxab, vxab);
1086+
let bb = T::two() * dot(vxab, aoxab);
1087+
let cc = dot(aoxab, aoxab) - (r*r * ab2);
1088+
let dd = bb * bb - T::four() * aa * cc;
1089+
1090+
if dd >= T::zero() {
1091+
let t = (-bb - sqrt(dd)) / (T::two() * aa);
1092+
if t >= T::zero() {
1093+
// intersection point
1094+
let ipc = r0 + rv * t;
1095+
// clamps to finite cylinder extents
1096+
let ipd = distance_on_line(ipc, a, b);
1097+
if ipd >= T::zero() && ipd <= dist(a, b) {
1098+
return Some(ipc);
1099+
}
1100+
}
1101+
}
1102+
1103+
// intersect with the top and bottom circles
1104+
let ip_top = ray_vs_plane(r0, rv, c0, normalize(c0 - c1));
1105+
let ip_bottom = ray_vs_plane(r0, rv, c1, normalize(c1 - c0));
1106+
let r2 = r*r;
1107+
1108+
let mut btop = false;
1109+
if let Some(ip_top) = ip_top {
1110+
if dist2(ip_top, c0) < r2 {
1111+
btop = true;
1112+
}
1113+
}
1114+
1115+
if let Some(ip_bottom) = ip_bottom {
1116+
if dist2(ip_bottom, c1) < r2 {
1117+
if btop {
1118+
if let Some(ip_top) = ip_top {
1119+
let d1 = distance_on_line(ip_top, r0, r0 + rv);
1120+
let d2 = distance_on_line(ip_bottom, r0, r0 + rv);
1121+
if d2 < d1 {
1122+
return Some(ip_bottom);
1123+
}
1124+
}
1125+
}
1126+
else {
1127+
return Some(ip_bottom);
1128+
}
1129+
}
1130+
}
1131+
1132+
if btop {
1133+
if let Some(ip_top) = ip_top {
1134+
return Some(ip_top);
1135+
}
1136+
}
1137+
1138+
1139+
None
1140+
}
1141+
10641142
/// returns true if the sphere with centre s and radius r is inside the frustum defined by 6 planes packed as vec4's .xyz = normal, .w = plane distance
10651143
pub fn sphere_vs_frustum<T: Number>(s: Vec3<T>, r: T, planes: &[Vec4<T>; 6]) -> bool {
10661144
for p in planes.iter().take(6) {
@@ -1562,17 +1640,18 @@ pub fn map_to_range<T: Float, X: Base<T>>(v: X, in_start: X, in_end: X, out_star
15621640
out_start + slope * (v - in_start)
15631641
}
15641642

1565-
// TODO:
1566-
// fix ray vs capsule intersection based the c++ lib
1643+
// TODO: tests
15671644
// line_segment_between_line_segment (test)
15681645
// sphere_vs_capsule (test)
1569-
// ray_vs_capsule (test)
1570-
// missing fail cases
1646+
// ray_vs_line_segment (test)
1647+
// shortest_line_segment_between_line_segments (test)
1648+
// shortest_line_segment_between_lines (test)
1649+
15711650
// projection, ndc
15721651
// projection, sc
15731652
// unprojection, ndc,
15741653
// unprojection sc
1575-
// projection matrices
1654+
// projection matrices ;D
15761655
// quilez functions
15771656
// quat tests
15781657
// mat from quat

tests/tests.rs

Lines changed: 183 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,10 +3397,186 @@ fn ray_vs_capsule_test() {
33973397
}
33983398
}
33993399

3400-
// TODO:
3401-
// ray_vs_cylinder
3402-
// sphere_vs_capsule
3403-
// point_sphere_distance
3404-
// ray_vs_line_segment
3405-
// shortest_line_segment_between_line_segments
3406-
// shortest_line_segment_between_lines
3400+
#[test]
3401+
fn ray_vs_cylinder_test() {
3402+
{
3403+
let r0 = vec3f(-15.203737, 6.055606, -4.583255);
3404+
let rv = vec3f(0.554472, -0.831708, -0.028680);
3405+
let cy0 = vec3f(-3.880000, -14.260001, 1.690000);
3406+
let cy1 = vec3f(-3.880000, 0.320001, 1.690000);
3407+
let cyr = 6.730000;
3408+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3409+
assert_eq!(i.is_some(), false);
3410+
}
3411+
{
3412+
let r0 = vec3f(21.496956, 1.133426, -4.004637);
3413+
let rv = vec3f(-0.831786, 0.025206, 0.554524);
3414+
let cy0 = vec3f(8.260000, -3.040000, 2.670000);
3415+
let cy1 = vec3f(8.260000, 13.280000, 2.670000);
3416+
let cyr = 8.160000;
3417+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3418+
assert_eq!(i.is_some(), true);
3419+
assert_eq!(approx(i.unwrap(), vec3f(15.876670, 1.303737, -0.257780), 0.001), true);
3420+
}
3421+
{
3422+
let r0 = vec3f(15.056831, 17.546116, -11.426116);
3423+
let rv = vec3f(-0.401653, -0.647563, 0.647563);
3424+
let cy0 = vec3f(3.360000, -4.820000, 7.450001);
3425+
let cy1 = vec3f(3.360000, 14.520000, 7.450001);
3426+
let cyr = 9.670000;
3427+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3428+
assert_eq!(i.is_some(), true);
3429+
assert_eq!(approx(i.unwrap(), vec3f(8.448961, 6.892612, -0.772611), 0.001), true);
3430+
}
3431+
{
3432+
let r0 = vec3f(10.119667, -22.706263, -0.770485);
3433+
let rv = vec3f(-0.452623, 0.786135, -0.420860);
3434+
let cy0 = vec3f(4.900000, -18.200001, -8.040000);
3435+
let cy1 = vec3f(4.900000, 0.680000, -8.040000);
3436+
let cyr = 2.920000;
3437+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3438+
assert_eq!(i.is_some(), true);
3439+
assert_eq!(approx(i.unwrap(), vec3f(5.396209, -14.502363, -5.162472), 0.001), true);
3440+
}
3441+
{
3442+
let r0 = vec3f(-0.795774, 7.815088, -6.405851);
3443+
let rv = vec3f(0.351940, -0.507207, 0.786689);
3444+
let cy0 = vec3f(4.080000, -15.730000, -0.670000);
3445+
let cy1 = vec3f(4.080000, 0.290000, -0.670000);
3446+
let cyr = 8.010000;
3447+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3448+
assert_eq!(i.is_some(), true);
3449+
assert_eq!(approx(i.unwrap(), vec3f(4.425715, 0.290000, 5.265714), 0.001), true);
3450+
}
3451+
{
3452+
let r0 = vec3f(6.690001, -23.490000, -9.060000);
3453+
let rv = vec3f(0.000000, 1.000000, 0.000000);
3454+
let cy0 = vec3f(6.690001, -13.490000, -9.060000);
3455+
let cy1 = vec3f(6.690001, -4.210001, -9.060000);
3456+
let cyr = 0.630000;
3457+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3458+
assert_eq!(i.is_some(), true);
3459+
assert_eq!(approx(i.unwrap(), vec3f(6.690001, -13.490000, -9.060000), 0.001), true);
3460+
}
3461+
{
3462+
let r0 = vec3f(18.464821, -4.349220, -17.132975);
3463+
let rv = vec3f(-0.702914, -0.036995, 0.710313);
3464+
let cy0 = vec3f(1.230000, -1.970000, -1.180000);
3465+
let cy1 = vec3f(1.230000, 12.070000, -1.180000);
3466+
let cyr = 4.510000;
3467+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3468+
assert_eq!(i.is_some(), false);
3469+
}
3470+
{
3471+
let r0 = vec3f(5.099485, -7.040759, 6.173443);
3472+
let rv = vec3f(-0.437602, 0.733279, -0.520391);
3473+
let cy0 = vec3f(8.280001, -3.640000, 3.580000);
3474+
let cy1 = vec3f(8.280001, 6.380000, 3.580000);
3475+
let cyr = 5.010000;
3476+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3477+
assert_eq!(i.is_some(), false);
3478+
}
3479+
{
3480+
let r0 = vec3f(2.376292, -19.823158, -17.518549);
3481+
let rv = vec3f(-0.035004, 0.796348, 0.603825);
3482+
let cy0 = vec3f(-7.020000, -16.830000, -6.010000);
3483+
let cy1 = vec3f(-7.020000, -0.330000, -6.010000);
3484+
let cyr = 7.450001;
3485+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3486+
assert_eq!(i.is_some(), false);
3487+
}
3488+
{
3489+
let r0 = vec3f(2.137793, 13.814747, -21.165298);
3490+
let rv = vec3f(0.420438, -0.586862, 0.691972);
3491+
let cy0 = vec3f(9.010000, -11.050001, -1.690000);
3492+
let cy1 = vec3f(9.010000, 2.850001, -1.690000);
3493+
let cyr = 6.950001;
3494+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3495+
assert_eq!(i.is_some(), true);
3496+
assert_eq!(approx(i.unwrap(), vec3f(9.993134, 2.850000, -8.236716), 0.001), true);
3497+
}
3498+
{
3499+
let r0 = vec3f(-2.701339, -15.851871, 9.038837);
3500+
let rv = vec3f(0.010470, 0.952736, -0.303619);
3501+
let cy0 = vec3f(-3.930000, -7.100000, 3.220000);
3502+
let cy1 = vec3f(-3.930000, 2.560000, 3.220000);
3503+
let cyr = 4.470000;
3504+
let i = ray_vs_cylinder(r0, rv, cy0, cy1, cyr);
3505+
assert_eq!(i.is_some(), true);
3506+
assert_eq!(approx(i.unwrap(), vec3f(-2.605165, -7.100000, 6.249780), 0.001), true);
3507+
}
3508+
}
3509+
3510+
#[test]
3511+
fn point_sphere_distance_test() {
3512+
{
3513+
let sp = vec3f(-1.600000, -3.880000, -6.970000);
3514+
let sr = 4.970000;
3515+
let p = vec3f(-5.080000, 0.420000, 9.870001);
3516+
let dd = point_sphere_distance(p, sp, sr);
3517+
assert_eq!(approx(dd, 12.755294, 0.001), true);
3518+
}
3519+
{
3520+
let sp = vec3f(-7.220000, 8.160000, 3.350000);
3521+
let sr = 5.600000;
3522+
let p = vec3f(1.690000, 7.090000, 1.570000);
3523+
let dd = point_sphere_distance(p, sp, sr);
3524+
assert_eq!(approx(dd, 3.548846, 0.001), true);
3525+
}
3526+
{
3527+
let sp = vec3f(-0.210000, 1.490000, -4.210000);
3528+
let sr = 2.670000;
3529+
let p = vec3f(0.970000, 8.260000, 5.120000);
3530+
let dd = point_sphere_distance(p, sp, sr);
3531+
assert_eq!(approx(dd, 8.917675, 0.001), true);
3532+
}
3533+
{
3534+
let sp = vec3f(7.450001, 2.280000, -9.090000);
3535+
let sr = 3.930000;
3536+
let p = vec3f(-1.790000, 9.670000, -3.280000);
3537+
let dd = point_sphere_distance(p, sp, sr);
3538+
assert_eq!(approx(dd, 9.251267, 0.001), true);
3539+
}
3540+
{
3541+
let sp = vec3f(6.680000, 4.900000, -8.760000);
3542+
let sr = 1.530000;
3543+
let p = vec3f(-8.059999, -6.430000, 0.010000);
3544+
let dd = point_sphere_distance(p, sp, sr);
3545+
assert_eq!(approx(dd, 19.026007, 0.001), true);
3546+
}
3547+
{
3548+
let sp = vec3f(-9.760000, 8.010000, -1.470000);
3549+
let sr = 7.219999;
3550+
let p = vec3f(-8.040000, 5.300000, -0.970000);
3551+
let dd = point_sphere_distance(p, sp, sr);
3552+
assert_eq!(approx(dd, 3.971538, 0.001), true);
3553+
}
3554+
{
3555+
let sp = vec3f(-6.010000, 9.680000, -5.880000);
3556+
let sr = 6.350000;
3557+
let p = vec3f(-7.690000, 7.450001, -8.250000);
3558+
let dd = point_sphere_distance(p, sp, sr);
3559+
assert_eq!(approx(dd, 2.687733, 0.001), true);
3560+
}
3561+
{
3562+
let sp = vec3f(3.190000, 4.540000, 2.230000);
3563+
let sr = 9.719999;
3564+
let p = vec3f(-3.210000, 3.350000, 0.060000);
3565+
let dd = point_sphere_distance(p, sp, sr);
3566+
assert_eq!(approx(dd, 2.858149, 0.001), true);
3567+
}
3568+
{
3569+
let sp = vec3f(3.170000, 5.250000, -8.000000);
3570+
let sr = 7.400000;
3571+
let p = vec3f(9.290001, 2.230000, -9.460000);
3572+
let dd = point_sphere_distance(p, sp, sr);
3573+
assert_eq!(approx(dd, 0.421002, 0.001), true);
3574+
}
3575+
{
3576+
let sp = vec3f(3.220000, -6.950000, 1.760000);
3577+
let sr = 5.530000;
3578+
let p = vec3f(1.290000, -4.470000, 4.830000);
3579+
let dd = point_sphere_distance(p, sp, sr);
3580+
assert_eq!(approx(dd, 1.136801, 0.001), true);
3581+
}
3582+
}

0 commit comments

Comments
 (0)