Angel 3.2
A 2D Game Prototyping Engine
MathUtil.cpp
1 
2 // Copyright (C) 2008-2013, Shane Liesegang
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of the copyright holder nor the names of any
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "stdafx.h"
31 #include "../Util/MathUtil.h"
32 
33 #include "../Infrastructure/World.h"
34 #include "../Infrastructure/Camera.h"
35 
36 #include <math.h>
37 #include <time.h>
38 
39 const float MathUtil::E = 2.718282f;
40 const float MathUtil::Log10E = 0.4342945f;
41 const float MathUtil::Log2E = 1.442695f;
42 const float MathUtil::Pi = 3.141593f;
43 const float MathUtil::PiOver2 = 1.570796f;
44 const float MathUtil::PiOver4 = 0.7853982f;
45 const float MathUtil::TwoPi = 6.283185f;
46 const float MathUtil::MaxFloat = 3.402823E+38f;
47 const float MathUtil::MinFloat = -3.402823E+38f;
48 const float MathUtil::Epsilon = 0.000001f;
49 
50 
51 float MathUtil::ToDegrees(float radians)
52 {
53  return (radians * 57.29578f);
54 }
55 
56 float MathUtil::ToRadians(float degrees)
57 {
58  return (degrees * 0.01745329f);
59 }
60 
61 Vector2 MathUtil::VectorFromAngle(float angle_in_degrees)
62 {
63  return Vector2(cos(ToRadians(angle_in_degrees)), sin(ToRadians(angle_in_degrees)));
64 }
65 
67 {
68  Vector2 localVector = v1;
69 
70  localVector.Normalize();
71  return atan2( localVector.Y, localVector.X );
72 }
73 
74 float MathUtil::AngleFromVectors(const Vector2& v1, const Vector2& v2)
75 {
76  return atan2(v1.Y, v1.X) - atan2(v2.Y, v2.X);
77 }
78 
79 
80 int MathUtil::RoundToInt(double x)
81 {
82  return ((int)(floor(x+0.5)));
83 }
84 
85 int MathUtil::RandomInt(int maximum)
86 {
87  static bool firstTime = true;
88  if (firstTime)
89  {
90  firstTime = false;
91  srand((int)time(NULL));
92  }
93  if (maximum <= 0)
94  {
95  return 0;
96  }
97  return (rand() % maximum);
98 }
99 
100 int MathUtil::RandomIntInRange(int min, int max)
101 {
102  return RandomInt(max-min) + min;
103 }
104 
105 int MathUtil::RandomIntWithError(int target, int error)
106 {
107  return RandomIntInRange(target-error, target+error);
108 }
109 
110 float MathUtil::RandomFloat(float maximum)
111 {
112  const float bigNumber = 10000.0f;
113  float randFloat = (float)RandomInt((int)bigNumber);
114  randFloat = randFloat/bigNumber;
115  return randFloat*maximum;
116 }
117 
118 float MathUtil::RandomFloatInRange(float min, float max)
119 {
120  return RandomFloat(max-min) + min;
121 }
122 
123 float MathUtil::RandomFloatWithError(float target, float error)
124 {
125  return RandomFloatInRange(target-error, target+error);
126 }
127 
129 {
130  return MathUtil::RandomInt(2) > 0;
131 }
132 
134 {
135  return RandomVector(Vector2(1.0f));//.Normalize();
136 }
137 
139 {
140  return RandomVector(Vector2::Zero, maxValues);
141 }
142 
143 Vector2 MathUtil::RandomVector(const Vector2& minValues, const Vector2& maxValues)
144 {
145  return Vector2(RandomFloatInRange(minValues.X, maxValues.X), RandomFloatInRange(minValues.Y, maxValues.Y));
146 }
147 
148 bool __poissonFieldCheck(Vector2List list, Vector2 point, float minDist)
149 {
150  for (int i=0; i < list.size(); i++) {
151  if (Vector2::DistanceSquared(list[i], point) < minDist)
152  {
153  return true;
154  }
155  }
156  return false;
157 }
158 
159 Vector2List MathUtil::RandomPointField(int numPoints, const Vector2& min, const Vector2& max, float minDistance)
160 {
161  Vector2List forReturn;
162 
163  int tryLimit = 100;
164  float reductionFactor = 0.75f;
165 
166  int tries;
167  Vector2 newPoint;
168 
169  while (forReturn.size() < numPoints)
170  {
171  tries = 0;
172 
173  do
174  {
175  newPoint = RandomVector(min, max);
176  tries++;
177  }
178  while (__poissonFieldCheck(forReturn, newPoint, minDistance) && tries < tryLimit);
179 
180  if (tries < tryLimit)
181  {
182  forReturn.push_back(newPoint);
183  }
184  else
185  {
186  minDistance *= reductionFactor;
187  }
188  }
189 
190  return forReturn;
191 }
192 
193 bool MathUtil::FuzzyEquals(float value1, float value2, float epsilon)
194 {
195  float a = value1 - value2;
196  if (fabs(a) < epsilon)
197  {
198  return true;
199  }
200  return false;
201 }
202 
203 
204 bool MathUtil::FuzzyEquals(const Vector2& v1, const Vector2& v2, float epsilon)
205 {
206  if ( (MathUtil::FuzzyEquals(v1.X, v2.X, epsilon)) && (MathUtil::FuzzyEquals(v1.Y, v2.Y, epsilon)) )
207  {
208  return true;
209  }
210  return false;
211 }
212 
213 Vector2 MathUtil::ScreenToWorld(const Vec2i& screenCoordinates)
214 {
215  return MathUtil::ScreenToWorld(screenCoordinates.X, screenCoordinates.Y);
216 }
217 
219 {
220  if (theWorld.IsHighResScreen())
221  {
222  x *= 2;
223  y *= 2;
224  }
225 
226  Vector2 worldDimensions = GetWorldDimensions();
227 
228  float worldX = ( ((float)x / (float)theCamera.GetWindowWidth()) - 0.5f ) * worldDimensions.X;
229  float worldY = ( 0.5f - ((float)y / (float)theCamera.GetWindowHeight()) ) * worldDimensions.Y;
230 
231  Vector2 camPos = theCamera.GetPosition();
232  Vector2 forReturn(worldX + camPos.X, worldY + camPos.Y);
233 
234  return Vector2::Rotate(forReturn, MathUtil::ToRadians(-theCamera.GetRotation()));
235 }
236 
237 Vector2 MathUtil::WorldToScreen(const Vector2& worldCoordinates)
238 {
239  Vector2 startingWorldCoords(worldCoordinates.X, worldCoordinates.Y);
240  Vector2 camPos = theCamera.GetPosition();
241  startingWorldCoords.X -= camPos.X;
242  startingWorldCoords.Y -= camPos.Y;
243  startingWorldCoords = Vector2::Rotate(startingWorldCoords, ToRadians(theCamera.GetRotation()));
244 
245  Vector2 worldDimensions = GetWorldDimensions();
246 
247  float screenX = theCamera.GetWindowWidth() * ( (startingWorldCoords.X / worldDimensions.X) + 0.5f );
248  float screenY = theCamera.GetWindowHeight() - (theCamera.GetWindowHeight() * ( 0.5f + (startingWorldCoords.Y / worldDimensions.Y) ));
249 
250  return Vector2(screenX, screenY);
251 }
252 
254 {
255  return WorldToScreen(Vector2(x, y));
256 }
257 
259 {
260  float worldWidth, worldHeight;
261  int screenWidth = theCamera.GetWindowWidth();
262  int screenHeight = theCamera.GetWindowHeight();
263  float aspect = (float)screenWidth / (float)screenHeight;
264  if (screenWidth > screenHeight)
265  {
266  //window is wider than it is tall; radius goes with height
267  worldHeight = theCamera.GetViewRadius() * 2.0f;
268  worldWidth = worldHeight * aspect;
269  }
270  else
271  {
272  //window is taller than it is wide; radius goes with width
273  worldWidth = theCamera.GetViewRadius() * 2.0f;
274  worldHeight = worldWidth / aspect;
275  }
276 
277  return Vector2(worldWidth, worldHeight);
278 }
279 
280 float MathUtil::PixelsToWorldUnits(float pixels)
281 {
282  float ratio = theCamera.GetWindowWidth() / GetWorldDimensions().X;
283 
284  return pixels / ratio;
285 }
286 
287 float MathUtil::WorldUnitsToPixels(float worldUnits)
288 {
289  float ratio = theCamera.GetWindowWidth() / GetWorldDimensions().X;
290 
291  return worldUnits * ratio;
292 }
293 
294 MathUtil::AABBSplittingAxis MathUtil::GetMajorAxis(const BoundingBox& source)
295 {
296  AABBSplittingAxis retVal = AA_X;
297  float maxAxis = source.Max.X - source.Min.X;
298 
299  float yAxis = source.Max.Y - source.Min.Y;
300  if (yAxis > maxAxis)
301  {
302  retVal = AA_Y;
303  maxAxis = yAxis;
304  }
305 
306  return retVal;
307 }
308 
309 void MathUtil::SplitBoundingBox(const BoundingBox& source, AABBSplittingAxis axis, BoundingBox& LHS, BoundingBox& RHS)
310 {
311  LHS = source;
312  RHS = source;
313 
314  switch (axis)
315  {
316  case AA_X:
317  LHS.Max.X = Lerp(LHS.Min.X, LHS.Max.X, 0.5f);
318  RHS.Min.X = LHS.Max.X;
319  break;
320  case AA_Y:
321  LHS.Max.Y = Lerp(LHS.Min.Y, LHS.Max.Y, 0.5f);
322  RHS.Min.Y = LHS.Max.Y;
323  break;
324  }
325 }
326 
327 float MathUtil::DeltaAngle(float A1, float A2)
328 {
329  // Find the difference
330  float Delta = A2 - A1;
331 
332  // If change is larger than PI
333  if(Delta > Pi)
334  {
335  // Flip to negative equivalent
336  Delta = Delta - (TwoPi);
337  }
338  else if(Delta < -Pi)
339  {
340  // Otherwise, if change is smaller than -PI
341  // Flip to positive equivalent
342  Delta = Delta + (TwoPi);
343  }
344 
345  // Return delta in [-PI,PI] range
346  return Delta;
347 }
348 
349 float MathUtil::VectorDeltaAngle(const Vector2& v1, const Vector2& v2)
350 {
351  return acos( Vector2::Dot(v1, v2) );
352 }
353