1 /*
2  * Kuklomenos
3  * Copyright (C) 2008-2009 Martin Bays <mbays@sdf.lonestar.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/.
17  */
18 
19 #include <cmath>
20 
21 #include "collision.h"
22 #include "coords.h"
23 
pointHitsCircle(float x,float y,float vx,float vy,float rad,float et)24 float pointHitsCircle(float x, float y, float vx, float vy, float rad, float et)
25 {
26     if (et >= 0)
27     {
28 	if ((x < -rad && x+et*vx < -rad) || (x > rad && x+et*vx > rad) ||
29 		(y < -rad && y+et*vy < -rad) || (y > rad && y+et*vy > rad))
30 	    return -1;
31     }
32     else
33 	if ((x < -rad && vx < 0) || (x > rad && vx > 0) ||
34 		(y < -rad && vy < 0) || (y > rad && vy > 0))
35 	    return -1;
36 
37     if (x*x + y*y <= rad*rad)
38 	return 0;
39 
40     float a = vx*vx+vy*vy;
41     float b = vx*x+vy*y;
42     float c = x*x+y*y-rad*rad;
43 
44     /* answer is the lesser real solution to at^2+2bt+c=0, if it has any; we
45      * just use the quadratic formula! */
46     float discriminantOverFour = b*b-a*c;
47     if (discriminantOverFour < 0)
48 	return -1;
49     float t = (-b - sqrt(discriminantOverFour))/a;
50     if (t < 0 || (et >= 0 && t > et))
51 	return -1;
52     return t;
53 }
54 
pointHitsPolygon(RelCartCoord * points,int n,RelCartCoord p,RelCartCoord v,float et)55 float pointHitsPolygon(RelCartCoord* points, int n, RelCartCoord p,
56 	RelCartCoord v, float et )
57 {
58     double maxIn=0;
59     double minOut=-1;
60     for (int i=0; i < n; i++)
61     {
62 	RelCartCoord next;
63 	if (i+1 < n)
64 	    next = points[i+1];
65 	else
66 	    next = points[0];
67 	RelCartCoord dir = next - points[i];
68 	RelCartCoord r = p - points[i];
69 	// ddy*(rdx+t.vx) - ddx(rdy+t.vy) = 0
70 	// t(ddy.vx - ddx.vy) = ddx.rdy-ddy.rdx
71 	double vel = dir.dy*v.dx - dir.dx*v.dy;
72 	double d = dir.dy*r.dx - dir.dx*r.dy;
73 	if (vel == 0)
74 	{
75 	    if (d > 0)
76 		return -1;
77 	    else
78 		continue;
79 	}
80 
81 	double t = d/-vel;
82 	if (d > 0)
83 	{
84 	    // enter halfplane at time t
85 	    if (t < 0 || (et >= 0 && t > et))
86 		return -1;
87 	    if (t > maxIn)
88 		maxIn = t;
89 	}
90 	else
91 	{
92 	    // leave halfplane at time t
93 	    if (t < 0 || (et >= 0 && t > et))
94 		continue;
95 	    if (minOut == -1 || t < minOut)
96 		minOut = t;
97 	}
98     }
99     if (minOut == -1 || maxIn < minOut)
100 	return float(maxIn);
101     else
102 	return -1;
103 }
104 
pointIn(float x,float y) const105 bool CollisionObject::pointIn(float x, float y) const
106 {
107     return (pointHits(x, y, 0, 0, 0) == 0);
108 }
pointHits(float x,float y,float vx,float vy,float et) const109 float CollisionObject::pointHits(float x, float y, float vx, float vy, float et) const
110 {
111     return pointHits(CartCoord(x,y), RelCartCoord(vx,vy), et);
112 }
pointIn(CartCoord p) const113 bool CollisionObject::pointIn(CartCoord p) const
114 {
115     return pointIn(p.x, p.y);
116 }
117 
objectCollides(const CollisionCircle & other) const118 bool CollisionObject::objectCollides(const CollisionCircle& other) const
119 {
120     return circleIntersects(other.startPos, other.radius);
121 }
objectCollides(const CollisionPolygon & other) const122 bool CollisionObject::objectCollides(const CollisionPolygon& other) const
123 {
124     // UNIMPLEMENTED
125     return false;
126 }
127 
pointHits(CartCoord p,RelCartCoord v,float et) const128 float CollisionCircle::pointHits(CartCoord p, RelCartCoord v, float et) const
129 {
130     return pointHitsCircle(p.x - startPos.x, p.y - startPos.y,
131 	    v.dx - velocity.dx, v.dy - velocity.dy, radius, et);
132 }
133 
circleIntersects(CartCoord c,float rad) const134 bool CollisionCircle::circleIntersects(CartCoord c, float rad) const
135 {
136     return (pointHitsCircle(startPos.x - c.x, startPos.y - c.y, 0, 0,
137 		radius + rad, 0) == 0);
138 }
139 
pointHits(CartCoord p,RelCartCoord v,float et) const140 float CollisionPolygon::pointHits(CartCoord p, RelCartCoord v, float et) const
141 {
142     RelCartCoord rp = (p - startPos).rotated(-angle);
143     RelCartCoord rv = (v - velocity).rotated(-angle);
144     return pointHitsPolygon(points, numPoints, rp, rv, et);
145 }
146 
circleIntersects(CartCoord c,float rad) const147 bool CollisionPolygon::circleIntersects(CartCoord c, float rad) const
148 {
149     // TODO properly
150     return pointIn(c);
151 }
152