#include "Geo2D.h" //================================================================================================// /********* ** Line ** *********/ //================================================================================================// Line::Line() { d = 0; } Line::Line(Vec2 start, Vec2 end) { s = start; e = end; r = Normalize(e-s); n = MakeNormalForPoints(s,e); d = Distance(s,e); } scalar Line::Angle(Line l) { return (acos(Dot(Normalize(s-e),Normalize(e-l.e))));//radians } //================================================================================================// /******** ** Box ** ********/ //================================================================================================// Box::Box() { w=h=0; } Box::Box(scalar width, scalar height, Vec2 pos) { w=width*0.5f; h=height*0.5f; p=pos; mins = Vec2(p.x-w,p.y-h); maxs = Vec2(p.x+w,p.y+h); } void Box::Offset(Vec2 newpos) { p=newpos; mins = Vec2(p.x-w,p.y-h); maxs = Vec2(p.x+w,p.y+h); } //================================================================================================// /*********** ** sphere ** ***********/ //================================================================================================// Sphere::Sphere() { r = 0; } Sphere::Sphere(scalar radius, Vec2 pos) { r = radius; p = pos; } //================================================================================================// /************ ** capsule ** ************/ //================================================================================================// Capsule::Capsule() { r=0; } Capsule::Capsule(scalar radius, Vec2 s, Vec2 e) { r = radius; l = Line(s,e); } Capsule::Capsule(scalar radius, Line line) { r = radius; l = line; } //================================================================================================// /******************** ** Collision tests ** ********************/ //================================================================================================// namespace Collision { bool PointBoxOverlap(Vec2 p, Box b) { if(p.xb.maxs.x) return false; if(p.yb.maxs.y) return false; return true; } bool BoxBoxOverlap(Box a, Box b) { if(a.maxs.xb.maxs.x) return false; if(a.mins.y>b.maxs.y) return false; return true; } bool BoxBoxOverlap(Box a, Box b, Vec2& outNormal) { if(a.maxs.xb.maxs.x) return false; if(a.mins.y>b.maxs.y) return false; if(b.mins.x>=a.mins.x) outNormal.x-=1; if(b.maxs.x<=a.maxs.x) outNormal.x+=1; if(b.mins.y>=a.mins.y) outNormal.y-=1; if(b.maxs.y<=a.maxs.y) outNormal.y+=1; return true; } bool PointSphereOverlap(Vec2 p, Sphere s) { if(Distance(p,s.p)<=s.r) return true; return false; } bool SphereSphereOverlap(Sphere a, Sphere b) { scalar d = Distance(a.p,b.p); if(d<=(a.r+b.r)) return true; return false; } bool SphereBoxOverlap(Sphere s, Box b) { Vec2 p,r; r = Normalize(b.p-s.p); p = s.p + r * s.r; if(PointBoxOverlap(p,b)) return true; return false; } Vec2 ClosestPointOnLine(Vec2 p, Line l) { Vec2 out; scalar U; U = (((p.x-l.s.x)*(l.e.x-l.s.x))+((p.y-l.s.y)*(l.e.y-l.s.y))) / (l.d*l.d); if(U<0.0f)U=0.0f; if(U>1.0f)U=1.0f; out.x = l.s.x + U * (l.e.x-l.s.x); out.y = l.s.y + U * (l.e.y-l.s.y); return out; } scalar PointDistanceToLine(Vec2 p, Line l) { Vec2 out = ClosestPointOnLine(p,l); return Distance(out,p); } bool CollisionPrivate::LineQuickReject(Line a, Line b) { scalar minsxa,maxsxa,minsya,maxsya; minsxa= (a.s.xmaxsxb) return false; if(maxsyamaxsyb) return false; return true; } scalar LineDistance(Line a, Line b) { Vec2 s1,e1,s2,e2; scalar Ta,Tb; s1=a.s; s2=b.s; e1=a.e; e2=b.e; scalar denom, numeratorA,numeratorB; denom = (((e2.y-s2.y)*(e1.x-s1.x))-((e2.x-s2.x)*(e1.y-s1.y))); if(denom==0)//parallel { //make a 3rd line using one of the line's normal Line perp(a.s,a.s+a.n); //now we must do an infinite line intersection between the perpendicular line and the second line Vec2 p = LineSegmentIntersection(perp,b); Vec2 p2 = ClosestPointOnLine(p,a); return Distance(p,p2); } numeratorA = (((e2.x-s2.x)*(s1.y-s2.y))-((e2.y-s2.y)*(s1.x-s2.x))); numeratorB = (((e1.x-s1.x)*(s1.y-s2.y))-((e1.y-s1.y)*(s1.x-s2.x))); Ta = numeratorA / denom; Tb = numeratorB / denom; if(Ta>=0 && Ta<=1 && Tb>=0 && Tb<=1)//lines are intersecting inside the segment { return 0;//no need for further testing, we know they intersect within segments } if(Ta<0) Ta=0; else if(Ta>1) Ta=1; Vec2 p,p2; p.x = s1.x + Ta * (e1.x-s1.x); p.y = s1.y + Ta * (e1.y-s1.y); p2 = ClosestPointOnLine(p,b); p = ClosestPointOnLine(p2,a); return Distance(p,p2); } Vec2 LineSegmentIntersection(Line a, Line b) { Vec2 p; Vec2 s1,e1,s2,e2; scalar Ta,Tb; s1=a.s; s2=b.s; e1=a.e; e2=b.e; scalar denom, numeratorA,numeratorB; denom = (((e2.y-s2.y)*(e1.x-s1.x))-((e2.x-s2.x)*(e1.y-s1.y))); if(denom==0)//parallel return p; numeratorA = (((e2.x-s2.x)*(s1.y-s2.y))-((e2.y-s2.y)*(s1.x-s2.x))); numeratorB = (((e1.x-s1.x)*(s1.y-s2.y))-((e1.y-s1.y)*(s1.x-s2.x))); Ta = numeratorA / denom; Tb = numeratorB / denom; if(Tb<0) Tb=0; else if(Tb>1) Tb=1; p.x = s2.x + Tb * (e2.x-s2.x); p.y = s2.y + Tb * (e2.y-s2.y); return p; } bool LineLineOverlap(Line a, Line b) { if(!CollisionPrivate::LineQuickReject(a,b)) return false; Vec2 s1,e1,s2,e2; scalar Ta,Tb; s1=a.s; s2=b.s; e1=a.e; e2=b.e; scalar denom, numeratorA,numeratorB; denom = (((e2.y-s2.y)*(e1.x-s1.x))-((e2.x-s2.x)*(e1.y-s1.y))); if(denom==0)//parallel return false; numeratorA = (((e2.x-s2.x)*(s1.y-s2.y))-((e2.y-s2.y)*(s1.x-s2.x))); numeratorB = (((e1.x-s1.x)*(s1.y-s2.y))-((e1.y-s1.y)*(s1.x-s2.x))); Ta = numeratorA / denom; Tb = numeratorB / denom; if(denom==0)//parallel return false; if((Ta>=0 && Ta<=1) && (Tb>=0 && Tb<=1)) return true; return false; } bool LineLineOverlap(Line a, Line b, Vec2& out) { if(!CollisionPrivate::LineQuickReject(a,b)) return false; Vec2 s1,e1,s2,e2; scalar Ta,Tb; s1=a.s; s2=b.s; e1=a.e; e2=b.e; scalar denom, numeratorA,numeratorB; denom = (((e2.y-s2.y)*(e1.x-s1.x))-((e2.x-s2.x)*(e1.y-s1.y))); if(denom==0)//parallel return false; numeratorA = (((e2.x-s2.x)*(s1.y-s2.y))-((e2.y-s2.y)*(s1.x-s2.x))); numeratorB = (((e1.x-s1.x)*(s1.y-s2.y))-((e1.y-s1.y)*(s1.x-s2.x))); Ta = numeratorA / denom; Tb = numeratorB / denom; if((Ta>=0 && Ta<=1) && (Tb>=0 && Tb<=1)) { out.x = s1.x + Ta * (e1.x-s1.x); out.y = s1.y + Ta * (e1.y-s1.y); return true; } return false; } bool LineBoxOverlap(Line l, Box b) { //Trivial Reject: if any of these pass the line is definetly outside the box if(l.s.xb.maxs.x && l.e.x>b.maxs.x) return false; if(l.s.yb.maxs.y && l.e.y>b.maxs.y) return false; Vec2 p = ClosestPointOnLine(b.p,l); if(!PointBoxOverlap(p,b)) return false; return true; } bool LineBoxOverlap(Line l, Box b, Vec2& entry) { //Trivial Reject: if any of these pass the line is definetly outside the box if(l.s.xb.maxs.x && l.e.x>b.maxs.x) return false; if(l.s.yb.maxs.y && l.e.y>b.maxs.y) return false; //Trivial Reject2: Completely inside the box entry = l.s; if(PointBoxOverlap(l.s,b)) return true; //clipping if(l.s.xb.maxs.x) { Line clip(Vec2(b.maxs.x,-1000),Vec2(b.maxs.x,1000)); LineLineOverlap(l,clip,l.s); } if(l.s.yb.maxs.y) { Line clip(Vec2(-1000,b.maxs.y),Vec2(1000,b.maxs.y)); LineLineOverlap(l,clip,l.s); } entry = l.s; if(!PointBoxOverlap(l.s,b)) return false; return true; } bool LineBoxOverlap(Line l, Box b, Vec2& entry, Vec2& exit) { //Trivial Reject: if any of these pass the line is definetly outside the box if(l.s.xb.maxs.x && l.e.x>b.maxs.x) return false; if(l.s.yb.maxs.y && l.e.y>b.maxs.y) return false; //Trivial Reject2: Completely inside the box entry = l.s; exit = l.e; if(PointBoxOverlap(l.s,b) && PointBoxOverlap(l.e,b)) return true; //clipping if(l.s.xb.maxs.x) { Line clip(Vec2(b.maxs.x,-1000),Vec2(b.maxs.x,1000)); LineLineOverlap(l,clip,l.s); } if(l.s.yb.maxs.y) { Line clip(Vec2(-1000,b.maxs.y),Vec2(1000,b.maxs.y)); LineLineOverlap(l,clip,l.s); } entry = l.s; if(l.e.xb.maxs.x) { Line clip(Vec2(b.maxs.x,-1000),Vec2(b.maxs.x,1000)); LineLineOverlap(l,clip,l.e); } if(l.e.yb.maxs.y) { Line clip(Vec2(-1000,b.maxs.y),Vec2(1000,b.maxs.y)); LineLineOverlap(l,clip,l.e); } exit = l.e; if(!PointBoxOverlap(l.s,b)) return false; return true; } bool LineSphereOverlap(Line l, Sphere s) { if(PointDistanceToLine(s.p,l)<=s.r) return true; return false; } bool LineSphereOverlap(Line l, Sphere s, Vec2& entry) { if(PointDistanceToLine(s.p,l)>s.r) return false; scalar A,B,C; A = ((l.e.x-l.s.x)*(l.e.x-l.s.x)) + ((l.e.y-l.s.y)*(l.e.y-l.s.y)); B = 2*( (l.e.x-l.s.x)*(l.s.x-s.p.x) + (l.e.y-l.s.y)*(l.s.y-s.p.y) ); C = (s.p.x*s.p.x) + (s.p.y*s.p.y) + (l.s.x*l.s.x) + (l.s.y*l.s.y) - 2*( s.p.x*l.s.x + s.p.y*l.s.y ) - (s.r*s.r); scalar result = B*B-4*A*C; scalar U; if(result<0) return false; else if(result==0) U = -B/(2*A); else if(result>0) { if(PointSphereOverlap(l.s,s)) U = (-B + sqrt( (B*B) - 4*A*C )) / (2*A); else U = (-B - sqrt( (B*B) - 4*A*C )) / (2*A); } entry.x = l.s.x + U * (l.e.x-l.s.x); entry.y = l.s.y + U * (l.e.y-l.s.y); return true; } bool LineSphereOverlap(Line l, Sphere s, Vec2& entry, Vec2& exit) { if(PointDistanceToLine(s.p,l)>s.r) return false; scalar A,B,C; A = ((l.e.x-l.s.x)*(l.e.x-l.s.x)) + ((l.e.y-l.s.y)*(l.e.y-l.s.y)); B = 2*( (l.e.x-l.s.x)*(l.s.x-s.p.x) + (l.e.y-l.s.y)*(l.s.y-s.p.y) ); C = (s.p.x*s.p.x) + (s.p.y*s.p.y) + (l.s.x*l.s.x) + (l.s.y*l.s.y) - 2*( s.p.x*l.s.x + s.p.y*l.s.y ) - (s.r*s.r); scalar result = B*B-4*A*C; scalar Ua,Ub; if(result<0) return false; else if(result==0.0f) { if(PointSphereOverlap(l.s,s)) { Ub = -B/(2*A); entry.x = l.s.x; entry.y = l.s.y; exit.x = l.s.x + Ub * (l.e.x-l.s.x); exit.y = l.s.y + Ub * (l.e.y-l.s.y); } else { Ua = -B/(2*A); entry.x = l.e.x; entry.y = l.e.y; exit.x = l.s.x + Ua * (l.e.x-l.s.x); exit.y = l.s.y + Ua * (l.e.y-l.s.y); } } else if(result>0) { Ua = (-B - sqrt( (B*B) - 4*A*C )) / (2*A); Ub = (-B + sqrt( (B*B) - 4*A*C )) / (2*A); if(PointSphereOverlap(l.s,s)) { entry = l.s; exit.x = l.s.x + Ub * (l.e.x-l.s.x); exit.y = l.s.y + Ub * (l.e.y-l.s.y); } else if(PointSphereOverlap(l.e,s)) { entry.x = l.s.x + Ua * (l.e.x-l.s.x); entry.y = l.s.y + Ua * (l.e.y-l.s.y); exit = l.e; } else { entry.x = l.s.x + Ua * (l.e.x-l.s.x); entry.y = l.s.y + Ua * (l.e.y-l.s.y); exit.x = l.s.x + Ub * (l.e.x-l.s.x); exit.y = l.s.y + Ub * (l.e.y-l.s.y); } } return true; } bool BoxCapsuleOverlap(Box b, Capsule c) { Vec2 p,v,r; p = ClosestPointOnLine(b.p,c.l); r = Normalize(b.p-p); v = p + r * c.r; if(PointBoxOverlap(v,b)) return true; return false; } bool SphereCapsuleOverlap(Sphere s, Capsule c) { if(PointDistanceToLine(s.p,c.l)>(s.r+c.r)) return false; return true; } bool LineCapsuleOverlap(Line l, Capsule c) { if(LineDistance(l,c.l)>c.r) return false; return true; } bool LineCapsuleOverlap(Line l, Capsule c, Vec2& entry) { Vec2 ray = l.e - l.s; Vec2 vel = c.l.e - c.l.s;//sphere velocity Vec2 dif = l.s - c.l.s; scalar raydot = Dot(ray,ray); scalar veldot = Dot(vel,vel); scalar raydotvel = Dot(ray,vel); scalar raydotdif = Dot(ray,dif); scalar A = raydot * -veldot + raydotvel * raydotvel; scalar B = raydot * 2.0f * Dot(vel,dif) - 2.0f * raydotvel * raydotdif; scalar C = raydot * (c.r * c.r - Dot(dif,dif)) + raydotdif * raydotdif; scalar t; if(getLowestRoot(A,B,C,1.0f,t)) { scalar u = (raydotvel * t - raydotdif) / raydot; if(u>=0 && u<=1) { entry = l.s + u *(l.e-l.s); return true; } } return false; } bool FindRoot(scalar a, scalar b, scalar c, scalar& r) { float d = b * b - 4.0f * a * c; if (d < 0.0f) return false; float inv2a = 0.5f / a; if (d == 0.0f) r = -b * inv2a; else { float sqrtd = sqrtf(d); scalar r1,r2; r1 = (-b - sqrtd) * inv2a; r2 =(-b + sqrtd) * inv2a; r1>r2? r=r2: r=r1; } return true; } bool getLowestRoot(scalar a, scalar b, scalar c, scalar maxR, scalar& root) { // Check if a solution exists float determinant = b*b - 4.0f*a*c; // If determinant is negative it means no solutions. if (determinant < 0.0f) return false; // calculate the two roots: (if determinant == 0 then // x1==x2 but let’s disregard that slight optimization) float sqrtD = sqrt(determinant); float r1 = (-b - sqrtD) / (2*a); float r2 = (-b + sqrtD) / (2*a); // Sort so x1 <= x2 if (r1 > r2) { float temp = r2; r2 = r1; r1 = temp; } // Get lowest root: if (r1 > 0 && r1 < maxR) { root = r1; return true; } // It is possible that we want x2 - this can happen // if x1 < 0 if (r2 > 0 && r2 < maxR) { root = r2; return true; } // No (valid) solutions return false; } bool PointSideOfLine(Vec2 p, Line l) { Vec2 c = l.s + l.r * (l.d * 0.5f); Vec2 p2 = c - l.n; Vec2 cp1(0,Cross(l.e-l.s, p-l.s)); Vec2 cp2(0,Cross(l.e-l.s, p2-l.s)); scalar d = Dot(cp1,cp2); if(d>=0) return true; return false; } }