Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Geometry.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "Geometry.h"
21 
22 #include "maths/FixedVector2D.h"
23 
24 using namespace Geometry;
25 
26 // TODO: all of these things could be optimised quite easily
27 
29 {
30  fixed du = point.Dot(u);
31  if (-halfSize.X <= du && du <= halfSize.X)
32  {
33  fixed dv = point.Dot(v);
34  if (-halfSize.Y <= dv && dv <= halfSize.Y)
35  return true;
36  }
37  return false;
38 }
39 
41 {
42  return CFixedVector2D(
43  u.X.Multiply(halfSize.X).Absolute() + v.X.Multiply(halfSize.Y).Absolute(),
44  u.Y.Multiply(halfSize.X).Absolute() + v.Y.Multiply(halfSize.Y).Absolute()
45  );
46 }
47 
48 float Geometry::ChordToCentralAngle(const float chordLength, const float radius)
49 {
50  return acosf(1.f - SQR(chordLength)/(2.f*SQR(radius))); // cfr. law of cosines
51 }
52 
54 {
55  /*
56  * Relative to its own coordinate system, we have a square like:
57  *
58  * A : B : C
59  * : :
60  * - - ########### - -
61  * # #
62  * # I #
63  * D # 0 # E v
64  * # # ^
65  * # # |
66  * - - ########### - - -->u
67  * : :
68  * F : G : H
69  *
70  * where 0 is the center, u and v are unit axes,
71  * and the square is hw*2 by hh*2 units in size.
72  *
73  * Points in the BIG regions should check distance to horizontal edges.
74  * Points in the DIE regions should check distance to vertical edges.
75  * Points in the ACFH regions should check distance to the corresponding corner.
76  *
77  * So we just need to check all of the regions to work out which calculations to apply.
78  *
79  */
80 
81  // du, dv are the location of the point in the square's coordinate system
82  fixed du = point.Dot(u);
83  fixed dv = point.Dot(v);
84 
85  fixed hw = halfSize.X;
86  fixed hh = halfSize.Y;
87 
88  // TODO: I haven't actually tested this
89 
90  if (-hw < du && du < hw) // regions B, I, G
91  {
92  fixed closest = (dv.Absolute() - hh).Absolute(); // horizontal edges
93 
94  if (-hh < dv && dv < hh) // region I
95  closest = std::min(closest, (du.Absolute() - hw).Absolute()); // vertical edges
96 
97  return closest;
98  }
99  else if (-hh < dv && dv < hh) // regions D, E
100  {
101  return (du.Absolute() - hw).Absolute(); // vertical edges
102  }
103  else // regions A, C, F, H
104  {
105  CFixedVector2D corner;
106  if (du < fixed::Zero()) // A, F
107  corner -= u.Multiply(hw);
108  else // C, H
109  corner += u.Multiply(hw);
110  if (dv < fixed::Zero()) // F, H
111  corner -= v.Multiply(hh);
112  else // A, C
113  corner += v.Multiply(hh);
114 
115  return (corner - point).Length();
116  }
117 }
118 
120 {
121  /*
122  * Relative to its own coordinate system, we have a square like:
123  *
124  * A : : C
125  * : :
126  * - - #### B #### - -
127  * #\ /#
128  * # \ / #
129  * D --0-- E v
130  * # / \ # ^
131  * #/ \# |
132  * - - #### G #### - - -->u
133  * : :
134  * F : : H
135  *
136  * where 0 is the center, u and v are unit axes,
137  * and the square is hw*2 by hh*2 units in size.
138  *
139  * Points in the BDEG regions are nearest to the corresponding edge.
140  * Points in the ACFH regions are nearest to the corresponding corner.
141  *
142  * So we just need to check all of the regions to work out which calculations to apply.
143  *
144  */
145 
146  // du, dv are the location of the point in the square's coordinate system
147  fixed du = point.Dot(u);
148  fixed dv = point.Dot(v);
149 
150  fixed hw = halfSize.X;
151  fixed hh = halfSize.Y;
152 
153  if (-hw < du && du < hw) // regions B, G; or regions D, E inside the square
154  {
155  if (-hh < dv && dv < hh && (du.Absolute() - hw).Absolute() < (dv.Absolute() - hh).Absolute()) // regions D, E
156  {
157  if (du >= fixed::Zero()) // E
158  return u.Multiply(hw) + v.Multiply(dv);
159  else // D
160  return -u.Multiply(hw) + v.Multiply(dv);
161  }
162  else // B, G
163  {
164  if (dv >= fixed::Zero()) // B
165  return v.Multiply(hh) + u.Multiply(du);
166  else // G
167  return -v.Multiply(hh) + u.Multiply(du);
168  }
169  }
170  else if (-hh < dv && dv < hh) // regions D, E outside the square
171  {
172  if (du >= fixed::Zero()) // E
173  return u.Multiply(hw) + v.Multiply(dv);
174  else // D
175  return -u.Multiply(hw) + v.Multiply(dv);
176  }
177  else // regions A, C, F, H
178  {
179  CFixedVector2D corner;
180  if (du < fixed::Zero()) // A, F
181  corner -= u.Multiply(hw);
182  else // C, H
183  corner += u.Multiply(hw);
184  if (dv < fixed::Zero()) // F, H
185  corner -= v.Multiply(hh);
186  else // A, C
187  corner += v.Multiply(hh);
188 
189  return corner;
190  }
191 }
192 
194 {
195  /*
196  * We only consider collisions to be when the ray goes from outside to inside the shape (and possibly out again).
197  * Various cases to consider:
198  * 'a' inside, 'b' inside -> no collision
199  * 'a' inside, 'b' outside -> no collision
200  * 'a' outside, 'b' inside -> collision
201  * 'a' outside, 'b' outside -> depends; use separating axis theorem:
202  * if the ray's bounding box is outside the square -> no collision
203  * if the whole square is on the same side of the ray -> no collision
204  * otherwise -> collision
205  * (Points on the edge are considered 'inside'.)
206  */
207 
208  fixed hw = halfSize.X;
209  fixed hh = halfSize.Y;
210 
211  fixed au = a.Dot(u);
212  fixed av = a.Dot(v);
213 
214  if (-hw <= au && au <= hw && -hh <= av && av <= hh)
215  return false; // a is inside
216 
217  fixed bu = b.Dot(u);
218  fixed bv = b.Dot(v);
219 
220  if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks?
221  return true; // a is outside, b is inside
222 
223  if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh))
224  return false; // ab is entirely above/below/side the square
225 
226  CFixedVector2D abp = (b - a).Perpendicular();
227  fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a);
228  fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a);
229  fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a);
230  fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a);
231  if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero())
232  return true; // ray intersects the corner
233 
234  bool sign = (s0 < fixed::Zero());
235  if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign)
236  return true; // ray cuts through the square
237 
238  return false;
239 }
240 
242 {
243  // Exactly like TestRaySquare with u=(1,0), v=(0,1)
244 
245  // Assume the compiler is clever enough to inline and simplify all this
246  // (TODO: stop assuming that)
249 
250  fixed hw = halfSize.X;
251  fixed hh = halfSize.Y;
252 
253  fixed au = a.Dot(u);
254  fixed av = a.Dot(v);
255 
256  if (-hw <= au && au <= hw && -hh <= av && av <= hh)
257  return false; // a is inside
258 
259  fixed bu = b.Dot(u);
260  fixed bv = b.Dot(v);
261 
262  if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks?
263  return true; // a is outside, b is inside
264 
265  if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh))
266  return false; // ab is entirely above/below/side the square
267 
268  CFixedVector2D abp = (b - a).Perpendicular();
269  fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a);
270  fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a);
271  fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a);
272  fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a);
273  if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero())
274  return true; // ray intersects the corner
275 
276  bool sign = (s0 < fixed::Zero());
277  if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign)
278  return true; // ray cuts through the square
279 
280  return false;
281 }
282 
283 /**
284  * Separating axis test; returns true if the square defined by u/v/halfSize at the origin
285  * is not entirely on the clockwise side of a line in direction 'axis' passing through 'a'
286  */
288 {
289  fixed hw = halfSize.X;
290  fixed hh = halfSize.Y;
291 
292  CFixedVector2D p = axis.Perpendicular();
293  if (p.Dot((u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
294  return true;
295  if (p.Dot((u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
296  return true;
297  if (p.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
298  return true;
299  if (p.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
300  return true;
301 
302  return false;
303 }
304 
308 {
309  // TODO: need to test this carefully
310 
311  CFixedVector2D corner0a = c0 + u0.Multiply(halfSize0.X) + v0.Multiply(halfSize0.Y);
312  CFixedVector2D corner0b = c0 - u0.Multiply(halfSize0.X) - v0.Multiply(halfSize0.Y);
313  CFixedVector2D corner1a = c1 + u1.Multiply(halfSize1.X) + v1.Multiply(halfSize1.Y);
314  CFixedVector2D corner1b = c1 - u1.Multiply(halfSize1.X) - v1.Multiply(halfSize1.Y);
315 
316  // Do a SAT test for each square vs each edge of the other square
317  if (!SquareSAT(corner0a - c1, -u0, u1, v1, halfSize1))
318  return false;
319  if (!SquareSAT(corner0a - c1, v0, u1, v1, halfSize1))
320  return false;
321  if (!SquareSAT(corner0b - c1, u0, u1, v1, halfSize1))
322  return false;
323  if (!SquareSAT(corner0b - c1, -v0, u1, v1, halfSize1))
324  return false;
325  if (!SquareSAT(corner1a - c0, -u1, u0, v0, halfSize0))
326  return false;
327  if (!SquareSAT(corner1a - c0, v1, u0, v0, halfSize0))
328  return false;
329  if (!SquareSAT(corner1b - c0, u1, u0, v0, halfSize0))
330  return false;
331  if (!SquareSAT(corner1b - c0, -v1, u0, v0, halfSize0))
332  return false;
333 
334  return true;
335 }
A simple fixed-point number class.
Definition: Fixed.h:115
static CFixed Zero()
Definition: Fixed.h:127
CFixed Absolute() const
Definition: Fixed.h:284
#define SQR(x)
Definition: MathUtil.h:23
fixed Dot(const CFixedVector2D &v)
Compute the dot product of this vector with another.
CFixedVector2D NearestPointOnSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Find point closest to the given point on the edge of the given square or rectangle.
Definition: Geometry.cpp:119
CFixedVector2D Perpendicular()
CFixed Multiply(CFixed n) const
Multiply by a CFixed.
Definition: Fixed.h:290
static bool SquareSAT(CFixedVector2D a, CFixedVector2D axis, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Separating axis test; returns true if the square defined by u/v/halfSize at the origin is not entirel...
Definition: Geometry.cpp:287
bool TestRayAASquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D halfSize)
Definition: Geometry.cpp:241
bool TestRaySquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:193
static CFixed FromInt(int n)
Definition: Fixed.h:136
CFixedVector2D Multiply(fixed n) const
Multiply by a CFixed.
Definition: FixedVector2D.h:86
bool TestSquareSquare(CFixedVector2D c0, CFixedVector2D u0, CFixedVector2D v0, CFixedVector2D halfSize0, CFixedVector2D c1, CFixedVector2D u1, CFixedVector2D v1, CFixedVector2D halfSize1)
Definition: Geometry.cpp:305
fixed Length() const
Returns the length of the vector.
Definition: FixedVector2D.h:95
bool IsZero() const
Returns true if the number is precisely 0.
Definition: Fixed.h:199
fixed DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:53
float ChordToCentralAngle(const float chordLength, const float radius)
Given a circle of radius radius, and a chord of length chordLength on this circle, computes the central angle formed by connecting the chord&#39;s endpoints to the center of the circle.
Definition: Geometry.cpp:48
bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Checks if a point is inside the given rotated square or rectangle.
Definition: Geometry.cpp:28
Helper functions related to geometry algorithms.
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:40