@@ -490,7 +490,7 @@ pub fn closest_point_on_line_segment<T: Float, V: VecFloatOps<T> + VecN<T>>(p: V
490490
491491/// returns the closest point on the plane to point p wher the plane is defined by point on plane x and normal n
492492pub fn closest_point_on_plane < T : SignedNumber , V : VecN < T > + SignedVecN < T > > ( p : V , x : V , n : V ) -> V {
493- p - n * ( V :: dot ( p, n) - V :: dot ( x, n) )
493+ p + n * - ( V :: dot ( p, n) - V :: dot ( x, n) )
494494}
495495
496496/// returns the closest point on the aabb defined by aabb_min and aabb_max to point p, if the point is inside the aabb it will return p
@@ -569,36 +569,22 @@ pub fn closest_point_on_triangle<T: Float + FloatOps<T> + NumberOps<T>, V: VecN<
569569
570570/// returns the closest point to p on the cone defined by cp position, with direction cv height h an radius r
571571pub fn closest_point_on_cone < T : Float , V : VecN < T > + VecFloatOps < T > > ( p : V , cp : V , cv : V , h : T , r : T ) -> V {
572+ // centre point of the cones base (where radius is largest)
572573 let l2 = cp + cv * h;
573- let dh = distance_on_line ( p, cp, l2) / h;
574- let x0 = closest_point_on_line_segment ( p, cp, l2) ;
575- let d = dist ( x0, p) ;
576- if dh >= T :: one ( ) {
577- // clamp to the tip
578- l2
579- }
580- else if dh <= T :: zero ( ) {
581- // clamp to the base
582- // base plane
583- let pp = closest_point_on_plane ( p, cp, cv) ;
584- let vv = pp - x0;
585- let m = mag ( pp - x0) ;
586- if m < r {
587- pp
588- }
589- else {
590- let v = vv / m;
591- x0 + v * r
592- }
593- }
594- else if d < dh * r {
595- // inside the code
596- p
574+
575+ // find point onbase plane and clamp to the extent
576+ let cplane = closest_point_on_plane ( p, l2, cv) ;
577+ let extent = l2 + normalize ( cplane - l2) * r;
578+
579+ // test closest point on line with the axis along the side and bottom of the cone
580+ let e1 = closest_point_on_line_segment ( p, cp, extent) ;
581+ let e2 = closest_point_on_line_segment ( p, l2, extent) ;
582+
583+ if dist2 ( p, e1) < dist2 ( p, e2) {
584+ e1
597585 }
598586 else {
599- let v = normalize ( p - x0) ;
600- // clamp to the radius
601- x0 + ( v * dh * r)
587+ e2
602588 }
603589}
604590
@@ -705,6 +691,21 @@ pub fn point_inside_frustum<T: Number>(p: Vec3<T>, planes: &[Vec4<T>; 6]) -> boo
705691 }
706692 true
707693}
694+
695+ /// returns the classification of point p vs the plane defined by point on plane x and normal n
696+ pub fn point_vs_plane < T : SignedNumber + SignedNumberOps < T > > ( p : Vec3 < T > , x : Vec3 < T > , n : Vec3 < T > ) -> Classification {
697+ let pd = plane_distance ( x, n) ;
698+ let d = dot ( n, p) + pd;
699+ if d < T :: zero ( ) {
700+ Classification :: Behind
701+ }
702+ else if d > T :: zero ( ) {
703+ Classification :: Infront
704+ }
705+ else {
706+ Classification :: Intersects
707+ }
708+ }
708709
709710/// returns the classification of the 3D aabb defined as aabb_min to aabb_max vs the plane defined by point on plane x and normal n
710711pub fn aabb_vs_plane < T : SignedNumber + SignedNumberOps < T > > ( aabb_min : Vec3 < T > , aabb_max : Vec3 < T > , x : Vec3 < T > , n : Vec3 < T > ) -> Classification {
@@ -777,16 +778,16 @@ pub fn capsule_vs_plane<T: Float + FloatOps<T> + SignedNumber + SignedNumberOps<
777778
778779/// return the classification of cone defined by position cp, direction cv with height h and radius at the base of r. vs the plane defined by point x and normal n
779780pub fn cone_vs_plane < T : Float + SignedNumberOps < T > , V : VecN < T > + Cross < T > + Dot < T > + SignedVecN < T > + VecFloatOps < T > > ( cp : V , cv : V , h : T , r : T , x : V , n : V ) -> Classification {
780- let tip = cp + cv * h;
781+ let l2 = cp + cv * h;
781782 let pd = plane_distance ( x, n) ;
782783 // check if the tip and cones extent are on different sides of the plane
783- let d1 = dot ( n, tip ) + pd;
784+ let d1 = dot ( n, cp ) + pd;
784785 // extent from the tip is at the base centre point perp of cv at the radius edge... we need to choose the side toward the plane
785- let perp = normalize ( cross ( cross ( n, - cv) , - cv) ) ;
786- let extent = cp + perp * r;
787- let extent2 = cp + perp * -r;
788- let d2 = dot ( n, extent) ;
789- let d3 = dot ( n, extent2) ;
786+ let perp = normalize ( cross ( cross ( n, cv) , cv) ) ;
787+ let extent = l2 + perp * r;
788+ let extent2 = l2 + perp * -r;
789+ let d2 = dot ( n, extent) + pd ;
790+ let d3 = dot ( n, extent2) + pd ;
790791 if d1 < T :: zero ( ) && d2 < T :: zero ( ) && d3 < T :: zero ( ) {
791792 Classification :: Behind
792793 }
@@ -974,11 +975,85 @@ pub fn ray_vs_triangle<T: Float>(r0: Vec3<T>, rv: Vec3<T>, t0: Vec3<T>, t1: Vec3
974975}
975976
976977/// returns the intersection point of ray wih origin r0 and direction rv against the capsule with line c0 - c1 and radius cr
977- pub fn ray_vs_capsule < T : Float + FloatOps < T > + NumberOps < T > + SignedNumberOps < T > , V : VecN < T > + VecFloatOps < T > + SignedNumberOps < T > + FloatOps < T > > ( r0 : V , rv : V , c0 : V , c1 : V , cr : T ) -> Option < V > {
978- let sl = shortest_line_segment_between_lines ( r0, rv, c0, c1) ;
979- if let Some ( line) = sl {
980- if dist2 ( line. 0 , line. 1 ) < sqr ( cr) {
981- Some ( line. 0 )
978+ pub fn ray_vs_capsule < T : Float + FloatOps < T > + NumberOps < T > + SignedNumberOps < T > , V : VecN < T > + Cross < T > + VecFloatOps < T > + SignedNumberOps < T > + FloatOps < T > > ( r0 : V , rv : V , c0 : V , c1 : V , cr : T ) -> Option < V > {
979+ // shortest line seg within radius will indicate we intersect an infinite cylinder about an axis
980+ let seg = shortest_line_segment_between_line_and_line_segment ( r0, r0 + rv, c0, c1) ;
981+ if let Some ( ( l0, l1) ) = seg {
982+ // check we intesect the cylinder
983+ let mut ipc = V :: max_value ( ) ;
984+ let mut bc = false ;
985+ if dist2 ( l0, l1) < sqr ( cr) {
986+ // intesection of ray and infinite cylinder about axis
987+ // https://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection
988+ let a = c0;
989+ let b = c1;
990+ let v = rv;
991+ let r = cr;
992+
993+ let ab = b - a;
994+ let ao = r0 - a;
995+ let aoxab = cross ( ao, ab) ;
996+ let vxab = cross ( v, ab) ;
997+ let ab2 = dot ( ab, ab) ;
998+
999+ let aa = dot ( vxab, vxab) ;
1000+ let bb = T :: two ( ) * dot ( vxab, aoxab) ;
1001+ let cc = dot ( aoxab, aoxab) - ( r* r * ab2) ;
1002+ let dd = bb * bb - T :: four ( ) * aa * cc;
1003+
1004+ if dd >= T :: zero ( ) {
1005+ let t = ( -bb - sqrt ( dd) ) / ( T :: two ( ) * aa) ;
1006+ if t >= T :: zero ( ) {
1007+ // intersection point
1008+ ipc = r0 + rv * t;
1009+ // clamps to finite cylinder extents
1010+ let ipd = distance_on_line ( ipc, a, b) ;
1011+ if ipd >= T :: zero ( ) && ipd <= dist ( a, b) {
1012+ bc = true ;
1013+ }
1014+ }
1015+ }
1016+ }
1017+
1018+ // if our line doesnt intersect the cylinder, we might still intersect the top / bottom sphere
1019+ // test intersections with the end spheres
1020+ let bs1 = ray_vs_sphere ( r0, rv, c0, cr) ;
1021+ let bs2 = ray_vs_sphere ( r0, rv, c1, cr) ;
1022+
1023+ // get optional intersection points
1024+ let ips1 = if let Some ( ip) = bs1 {
1025+ ip
1026+ }
1027+ else {
1028+ V :: max_value ( )
1029+ } ;
1030+
1031+ let ips2 = if let Some ( ip) = bs2 {
1032+ ip
1033+ }
1034+ else {
1035+ V :: max_value ( )
1036+ } ;
1037+
1038+ // we need to choose the closes intersection if we have multiple
1039+ let ips : [ V ; 3 ] = [ ips1, ips2, ipc] ;
1040+ let bips : [ bool ; 3 ] = [ bs1. is_some ( ) , bs2. is_some ( ) , bc] ;
1041+
1042+ let mut iclosest = -1 ;
1043+ let mut dclosest = T :: max_value ( ) ;
1044+ for i in 0 ..3 {
1045+ if bips[ i] {
1046+ let dd = distance_on_line ( ips[ i] , r0, r0 + rv) ;
1047+ if dd < dclosest {
1048+ iclosest = i as i32 ;
1049+ dclosest = dd;
1050+ }
1051+ }
1052+ }
1053+
1054+ // if we have a valid closest point
1055+ if iclosest != -1 {
1056+ Some ( ips[ iclosest as usize ] )
9821057 }
9831058 else {
9841059 None
@@ -1160,6 +1235,43 @@ pub fn shortest_line_segment_between_lines<T: Float + SignedNumberOps<T> + Float
11601235 ) )
11611236}
11621237
1238+ /// returns the shortest line segment between 2 line segments (p1-p2) and (p3-p4) as an option tuple where .0 is the point on line segment 1 and .1 is the point on line segment 2
1239+ pub fn shortest_line_segment_between_line_and_line_segment < T : Float + SignedNumberOps < T > + FloatOps < T > , V : VecN < T > + SignedNumberOps < T > + FloatOps < T > + Dot < T > > ( p1 : V , p2 : V , p3 : V , p4 : V ) -> Option < ( V , V ) > {
1240+ // https://web.archive.org/web/20120404121511/http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/lineline.c
1241+
1242+ let p13 = p1 - p3;
1243+ let p43 = p4 - p3;
1244+
1245+ if approx ( abs ( p43) , V :: zero ( ) , T :: small_epsilon ( ) ) {
1246+ return None ;
1247+ }
1248+
1249+ let p21 = p2 - p1;
1250+ if approx ( abs ( p21) , V :: zero ( ) , T :: small_epsilon ( ) ) {
1251+ return None ;
1252+ }
1253+
1254+ let d1343 = dot ( p13, p43) ;
1255+ let d4321 = dot ( p43, p21) ;
1256+ let d1321 = dot ( p13, p21) ;
1257+ let d4343 = dot ( p43, p43) ;
1258+ let d2121 = dot ( p21, p21) ;
1259+
1260+ let denom = d2121 * d4343 - d4321 * d4321;
1261+ if abs ( denom) < T :: small_epsilon ( ) {
1262+ return None ;
1263+ }
1264+
1265+ let numer = d1343 * d4321 - d1321 * d4343;
1266+ let mua = numer / denom;
1267+ let mub = saturate ( ( d1343 + d4321 * mua) / d4343) ;
1268+
1269+ Some ( (
1270+ p1 + ( p21 * mua) ,
1271+ p3 + ( p43 * mub)
1272+ ) )
1273+ }
1274+
11631275/// returns soft clipping (in a cubic fashion) of x; let m be the threshold (anything above m stays unchanged), and n the value things will take when the signal is zero
11641276pub fn almost_identity < T : Number + Float > ( x : T , m : T , n : T ) -> T {
11651277 // <https://iquilezles.org/articles/functions/>
0 commit comments