renamed Rectangle to Rect because of ยง%$
[supertux.git] / src / collision.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 // 
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "collision.h"
23
24 #include <algorithm>
25 #include <iostream>
26 #include <stdio.h>
27 #include <float.h>
28 #include <math.h>
29 #include "math/vector.h"
30 #include "math/aatriangle.h"
31 #include "math/rect.h"
32 #include "collision_hit.h"
33
34 static const float DELTA = .0001;
35
36 bool
37 Collision::rectangle_rectangle(CollisionHit& hit, const Rect& r1,
38     const Vector& movement, const Rect& r2)
39 {
40   if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
41     return false;
42   if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
43     return false;
44
45   if(movement.x > DELTA) {
46     hit.depth = r1.p2.x - r2.p1.x;
47     hit.time = hit.depth / movement.x;
48     hit.normal.x = -1;
49     hit.normal.y = 0;
50   } else if(movement.x < -DELTA) {
51     hit.depth = r2.p2.x - r1.p1.x;
52     hit.time = hit.depth / -movement.x;
53     hit.normal.x = 1;
54     hit.normal.y = 0;
55   } else {
56     if(movement.y > -DELTA && movement.y < DELTA) {
57       hit.time = 0;
58       hit.depth = 0;
59       hit.normal.x = 1;
60       hit.normal.y = 0;
61       return true;
62     }
63     hit.time = FLT_MAX;
64   }
65
66   if(movement.y > DELTA) {
67     float ydepth = r1.p2.y - r2.p1.y;
68     float yt = ydepth / movement.y;
69     if(yt < hit.time) {
70       hit.depth = ydepth;
71       hit.time = yt;
72       hit.normal.x = 0;
73       hit.normal.y = -1;
74     }
75   } else if(movement.y < -DELTA) {
76     float ydepth = r2.p2.y - r1.p1.y;
77     float yt = ydepth / -movement.y;
78     if(yt < hit.time) {
79       hit.depth = ydepth;
80       hit.time = yt;
81       hit.normal.x = 0;
82       hit.normal.y = 1;
83     }
84   }
85
86   return true;
87 }
88
89 //---------------------------------------------------------------------------
90
91 static void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
92 {
93   n = Vector(p2.y-p1.y, p1.x-p2.x);
94   c = -(p2 * n);
95   float nval = n.norm();             
96   n /= nval;
97   c /= nval;
98 }
99
100 bool
101 Collision::rectangle_aatriangle(CollisionHit& hit, const Rect& rect,
102     const Vector& movement, const AATriangle& triangle)
103 {
104   if(!rectangle_rectangle(hit, rect, movement, (const Rect&) triangle))
105     return false;
106
107   Vector normal;
108   float c;
109   Vector p1;
110   Vector tp1, tp2;
111   switch(triangle.dir & AATriangle::DEFORM_MASK) {
112     case 0:
113       tp1 = triangle.p1;
114       tp2 = triangle.p2;
115       break;
116     case AATriangle::DEFORM1:
117       tp1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
118       tp2 = triangle.p2;
119       break;
120     case AATriangle::DEFORM2:
121       tp1 = triangle.p1;
122       tp2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
123       break;
124     case AATriangle::DEFORM3:
125       tp1 = triangle.p1;
126       tp2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
127       break;
128     case AATriangle::DEFORM4:
129       tp1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
130       tp2 = triangle.p2;
131       break;
132     default:
133       assert(false);
134   } 
135   
136   switch(triangle.dir & AATriangle::DIRECTION_MASK) {
137     case AATriangle::SOUTHWEST:
138       p1 = Vector(rect.p1.x, rect.p2.y);
139       makePlane(tp1, tp2, normal, c);
140       break;
141     case AATriangle::NORTHEAST:
142       p1 = Vector(rect.p2.x, rect.p1.y);
143       makePlane(tp2, tp1, normal, c);
144       break;
145     case AATriangle::SOUTHEAST:
146       p1 = rect.p2;
147       makePlane(Vector(tp1.x, tp2.y),
148           Vector(tp2.x, tp1.y), normal, c);
149       break;
150     case AATriangle::NORTHWEST:
151       p1 = rect.p1;
152       makePlane(Vector(tp2.x, tp1.y),
153           Vector(tp1.x, tp2.y), normal, c);
154       break;
155     default:
156       assert(false);
157   }
158
159   float n_p1 = -(normal * p1);
160   float depth = n_p1 - c;
161   if(depth < 0)
162     return false;
163   float time = depth / -(normal * movement);
164   if(time < hit.time) {
165     hit.depth = depth;
166     hit.time = time;
167     hit.normal = normal;
168   }
169
170   return true;
171 }
172