Line data Source code
1 : /**
2 : * ============================================================================
3 : * MIT License
4 : *
5 : * Copyright (c) 2016 Eric Phillips
6 : *
7 : * Permission is hereby granted, free of charge, to any person obtaining a
8 : * copy of this software and associated documentation files (the "Software"),
9 : * to deal in the Software without restriction, including without limitation
10 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 : * and/or sell copies of the Software, and to permit persons to whom the
12 : * Software is furnished to do so, subject to the following conditions:
13 : *
14 : * The above copyright notice and this permission notice shall be included in
15 : * all copies or substantial portions of the Software.
16 : *
17 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 : * DEALINGS IN THE SOFTWARE.
24 : * ============================================================================
25 : *
26 : *
27 : * This file implements a series of math functions for manipulating a
28 : * 2D vector.
29 : *
30 : * Created by Eric Phillips on October 15, 2016.
31 : */
32 :
33 : #pragma once
34 :
35 : #define _USE_MATH_DEFINES
36 : #include <math.h>
37 :
38 :
39 : struct Vector2
40 : {
41 : union
42 : {
43 : struct
44 : {
45 : double X;
46 : double Y;
47 : };
48 : double data[2];
49 : };
50 :
51 :
52 : /**
53 : * Constructors.
54 : */
55 : inline Vector2();
56 : inline Vector2(double data[]);
57 : inline Vector2(double value);
58 : inline Vector2(double x, double y);
59 :
60 :
61 : /**
62 : * Constants for common vectors.
63 : */
64 : static inline Vector2 Zero();
65 : static inline Vector2 One();
66 : static inline Vector2 Right();
67 : static inline Vector2 Left();
68 : static inline Vector2 Up();
69 : static inline Vector2 Down();
70 :
71 :
72 : /**
73 : * Returns the angle between two vectors in radians.
74 : * @param a: The first vector.
75 : * @param b: The second vector.
76 : * @return: A scalar value.
77 : */
78 : static inline double Angle(Vector2 a, Vector2 b);
79 :
80 : /**
81 : * Returns a vector with its magnitude clamped to maxLength.
82 : * @param vector: The target vector.
83 : * @param maxLength: The maximum length of the return vector.
84 : * @return: A new vector.
85 : */
86 : static inline Vector2 ClampMagnitude(Vector2 vector, double maxLength);
87 :
88 : /**
89 : * Returns the component of a in the direction of b (scalar projection).
90 : * @param a: The target vector.
91 : * @param b: The vector being compared against.
92 : * @return: A scalar value.
93 : */
94 : static inline double Component(Vector2 a, Vector2 b);
95 :
96 : /**
97 : * Returns the distance between a and b.
98 : * @param a: The first point.
99 : * @param b: The second point.
100 : * @return: A scalar value.
101 : */
102 : static inline double Distance(Vector2 a, Vector2 b);
103 :
104 : /**
105 : * Returns the dot product of two vectors.
106 : * @param lhs: The left side of the multiplication.
107 : * @param rhs: The right side of the multiplication.
108 : * @return: A scalar value.
109 : */
110 : static inline double Dot(Vector2 lhs, Vector2 rhs);
111 :
112 : /**
113 : * Converts a polar representation of a vector into cartesian
114 : * coordinates.
115 : * @param rad: The magnitude of the vector.
116 : * @param theta: The angle from the X axis.
117 : * @return: A new vector.
118 : */
119 : static inline Vector2 FromPolar(double rad, double theta);
120 :
121 : /**
122 : * Returns a vector linearly interpolated between a and b, moving along
123 : * a straight line. The vector is clamped to never go beyond the end points.
124 : * @param a: The starting point.
125 : * @param b: The ending point.
126 : * @param t: The interpolation value [0-1].
127 : * @return: A new vector.
128 : */
129 : static inline Vector2 Lerp(Vector2 a, Vector2 b, double t);
130 :
131 : /**
132 : * Returns a vector linearly interpolated between a and b, moving along
133 : * a straight line.
134 : * @param a: The starting point.
135 : * @param b: The ending point.
136 : * @param t: The interpolation value [0-1] (no actual bounds).
137 : * @return: A new vector.
138 : */
139 : static inline Vector2 LerpUnclamped(Vector2 a, Vector2 b, double t);
140 :
141 : /**
142 : * Returns the magnitude of a vector.
143 : * @param v: The vector in question.
144 : * @return: A scalar value.
145 : */
146 : static inline double Magnitude(Vector2 v);
147 :
148 : /**
149 : * Returns a vector made from the largest components of two other vectors.
150 : * @param a: The first vector.
151 : * @param b: The second vector.
152 : * @return: A new vector.
153 : */
154 : static inline Vector2 Max(Vector2 a, Vector2 b);
155 :
156 : /**
157 : * Returns a vector made from the smallest components of two other vectors.
158 : * @param a: The first vector.
159 : * @param b: The second vector.
160 : * @return: A new vector.
161 : */
162 : static inline Vector2 Min(Vector2 a, Vector2 b);
163 :
164 : /**
165 : * Returns a vector "maxDistanceDelta" units closer to the target. This
166 : * interpolation is in a straight line, and will not overshoot.
167 : * @param current: The current position.
168 : * @param target: The destination position.
169 : * @param maxDistanceDelta: The maximum distance to move.
170 : * @return: A new vector.
171 : */
172 : static inline Vector2 MoveTowards(Vector2 current, Vector2 target,
173 : double maxDistanceDelta);
174 :
175 : /**
176 : * Returns a new vector with magnitude of one.
177 : * @param v: The vector in question.
178 : * @return: A new vector.
179 : */
180 : static inline Vector2 Normalized(Vector2 v);
181 :
182 : /**
183 : * Creates a new coordinate system out of the two vectors.
184 : * Normalizes "normal" and normalizes "tangent" and makes it orthogonal to
185 : * "normal"..
186 : * @param normal: A reference to the first axis vector.
187 : * @param tangent: A reference to the second axis vector.
188 : */
189 : static inline void OrthoNormalize(Vector2 &normal, Vector2 &tangent);
190 :
191 : /**
192 : * Returns the vector projection of a onto b.
193 : * @param a: The target vector.
194 : * @param b: The vector being projected onto.
195 : * @return: A new vector.
196 : */
197 : static inline Vector2 Project(Vector2 a, Vector2 b);
198 :
199 : /**
200 : * Returns a vector reflected about the provided line.
201 : * This behaves as if there is a plane with the line as its normal, and the
202 : * vector comes in and bounces off this plane.
203 : * @param vector: The vector traveling inward at the imaginary plane.
204 : * @param line: The line about which to reflect.
205 : * @return: A new vector pointing outward from the imaginary plane.
206 : */
207 : static inline Vector2 Reflect(Vector2 vector, Vector2 line);
208 :
209 : /**
210 : * Returns the vector rejection of a on b.
211 : * @param a: The target vector.
212 : * @param b: The vector being projected onto.
213 : * @return: A new vector.
214 : */
215 : static inline Vector2 Reject(Vector2 a, Vector2 b);
216 :
217 : /**
218 : * Rotates vector "current" towards vector "target" by "maxRadiansDelta".
219 : * This treats the vectors as directions and will linearly interpolate
220 : * between their magnitudes by "maxMagnitudeDelta". This function does not
221 : * overshoot. If a negative delta is supplied, it will rotate away from
222 : * "target" until it is pointing the opposite direction, but will not
223 : * overshoot that either.
224 : * @param current: The starting direction.
225 : * @param target: The destination direction.
226 : * @param maxRadiansDelta: The maximum number of radians to rotate.
227 : * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation.
228 : * @return: A new vector.
229 : */
230 : static inline Vector2 RotateTowards(Vector2 current, Vector2 target,
231 : double maxRadiansDelta,
232 : double maxMagnitudeDelta);
233 :
234 : /**
235 : * Multiplies two vectors component-wise.
236 : * @param a: The lhs of the multiplication.
237 : * @param b: The rhs of the multiplication.
238 : * @return: A new vector.
239 : */
240 : static inline Vector2 Scale(Vector2 a, Vector2 b);
241 :
242 : /**
243 : * Returns a vector rotated towards b from a by the percent t.
244 : * Since interpolation is done spherically, the vector moves at a constant
245 : * angular velocity. This rotation is clamped to 0 <= t <= 1.
246 : * @param a: The starting direction.
247 : * @param b: The ending direction.
248 : * @param t: The interpolation value [0-1].
249 : */
250 : static inline Vector2 Slerp(Vector2 a, Vector2 b, double t);
251 :
252 : /**
253 : * Returns a vector rotated towards b from a by the percent t.
254 : * Since interpolation is done spherically, the vector moves at a constant
255 : * angular velocity. This rotation is unclamped.
256 : * @param a: The starting direction.
257 : * @param b: The ending direction.
258 : * @param t: The interpolation value [0-1].
259 : */
260 : static inline Vector2 SlerpUnclamped(Vector2 a, Vector2 b, double t);
261 :
262 : /**
263 : * Returns the squared magnitude of a vector.
264 : * This is useful when comparing relative lengths, where the exact length
265 : * is not important, and much time can be saved by not calculating the
266 : * square root.
267 : * @param v: The vector in question.
268 : * @return: A scalar value.
269 : */
270 : static inline double SqrMagnitude(Vector2 v);
271 :
272 : /**
273 : * Calculates the polar coordinate space representation of a vector.
274 : * @param vector: The vector to convert.
275 : * @param rad: The magnitude of the vector.
276 : * @param theta: The angle from the X axis.
277 : */
278 : static inline void ToPolar(Vector2 vector, double &rad, double &theta);
279 :
280 :
281 : /**
282 : * Operator overloading.
283 : */
284 : inline struct Vector2& operator+=(const double rhs);
285 : inline struct Vector2& operator-=(const double rhs);
286 : inline struct Vector2& operator*=(const double rhs);
287 : inline struct Vector2& operator/=(const double rhs);
288 : inline struct Vector2& operator+=(const Vector2 rhs);
289 : inline struct Vector2& operator-=(const Vector2 rhs);
290 : };
291 :
292 : inline Vector2 operator-(Vector2 rhs);
293 : inline Vector2 operator+(Vector2 lhs, const double rhs);
294 : inline Vector2 operator-(Vector2 lhs, const double rhs);
295 : inline Vector2 operator*(Vector2 lhs, const double rhs);
296 : inline Vector2 operator/(Vector2 lhs, const double rhs);
297 : inline Vector2 operator+(const double lhs, Vector2 rhs);
298 : inline Vector2 operator-(const double lhs, Vector2 rhs);
299 : inline Vector2 operator*(const double lhs, Vector2 rhs);
300 : inline Vector2 operator/(const double lhs, Vector2 rhs);
301 : inline Vector2 operator+(Vector2 lhs, const Vector2 rhs);
302 : inline Vector2 operator-(Vector2 lhs, const Vector2 rhs);
303 : inline bool operator==(const Vector2 lhs, const Vector2 rhs);
304 : inline bool operator!=(const Vector2 lhs, const Vector2 rhs);
305 :
306 :
307 :
308 : /*******************************************************************************
309 : * Implementation
310 : */
311 :
312 0 : Vector2::Vector2() : X(0), Y(0) {}
313 : Vector2::Vector2(double data[]) : X(data[0]), Y(data[1]) {}
314 : Vector2::Vector2(double value) : X(value), Y(value) {}
315 267647 : Vector2::Vector2(double x, double y) : X(x), Y(y) {}
316 :
317 :
318 : Vector2 Vector2::Zero() { return Vector2(0, 0); }
319 : Vector2 Vector2::One() { return Vector2(1, 1); }
320 : Vector2 Vector2::Right() { return Vector2(1, 0); }
321 : Vector2 Vector2::Left() { return Vector2(-1, 0); }
322 : Vector2 Vector2::Up() { return Vector2(0, 1); }
323 : Vector2 Vector2::Down() { return Vector2(0, -1); }
324 :
325 :
326 0 : double Vector2::Angle(Vector2 a, Vector2 b)
327 : {
328 0 : double v = Dot(a, b) / (Magnitude(a) * Magnitude(b));
329 0 : v = fmax(v, -1.0);
330 0 : v = fmin(v, 1.0);
331 0 : return acos(v);
332 : }
333 :
334 : Vector2 Vector2::ClampMagnitude(Vector2 vector, double maxLength)
335 : {
336 : double length = Magnitude(vector);
337 : if (length > maxLength)
338 : vector *= maxLength / length;
339 : return vector;
340 : }
341 :
342 : double Vector2::Component(Vector2 a, Vector2 b)
343 : {
344 : return Dot(a, b) / Magnitude(b);
345 : }
346 :
347 0 : double Vector2::Distance(Vector2 a, Vector2 b)
348 : {
349 0 : return Vector2::Magnitude(a - b);
350 : }
351 :
352 0 : double Vector2::Dot(Vector2 lhs, Vector2 rhs)
353 : {
354 0 : return lhs.X * rhs.X + lhs.Y * rhs.Y;
355 : }
356 :
357 : Vector2 Vector2::FromPolar(double rad, double theta)
358 : {
359 : Vector2 v;
360 : v.X = rad * cos(theta);
361 : v.Y = rad * sin(theta);
362 : return v;
363 : }
364 :
365 : Vector2 Vector2::Lerp(Vector2 a, Vector2 b, double t)
366 : {
367 : if (t < 0) return a;
368 : else if (t > 1) return b;
369 : return LerpUnclamped(a, b, t);
370 : }
371 :
372 : Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, double t)
373 : {
374 : return (b - a) * t + a;
375 : }
376 :
377 340928 : double Vector2::Magnitude(Vector2 v)
378 : {
379 340928 : return sqrt(SqrMagnitude(v));
380 : }
381 :
382 : Vector2 Vector2::Max(Vector2 a, Vector2 b)
383 : {
384 : double x = a.X > b.X ? a.X : b.X;
385 : double y = a.Y > b.Y ? a.Y : b.Y;
386 : return Vector2(x, y);
387 : }
388 :
389 : Vector2 Vector2::Min(Vector2 a, Vector2 b)
390 : {
391 : double x = a.X > b.X ? b.X : a.X;
392 : double y = a.Y > b.Y ? b.Y : a.Y;
393 : return Vector2(x, y);
394 : }
395 :
396 : Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target,
397 : double maxDistanceDelta)
398 : {
399 : Vector2 d = target - current;
400 : double m = Magnitude(d);
401 : if (m < maxDistanceDelta || m == 0)
402 : return target;
403 : return current + (d * maxDistanceDelta / m);
404 : }
405 :
406 : Vector2 Vector2::Normalized(Vector2 v)
407 : {
408 : double mag = Magnitude(v);
409 : if (mag == 0)
410 : return Vector2::Zero();
411 : return v / mag;
412 : }
413 :
414 : void Vector2::OrthoNormalize(Vector2 &normal, Vector2 &tangent)
415 : {
416 : normal = Normalized(normal);
417 : tangent = Reject(tangent, normal);
418 : tangent = Normalized(tangent);
419 : }
420 :
421 : Vector2 Vector2::Project(Vector2 a, Vector2 b)
422 : {
423 : double m = Magnitude(b);
424 : return Dot(a, b) / (m * m) * b;
425 : }
426 :
427 : Vector2 Vector2::Reflect(Vector2 vector, Vector2 planeNormal)
428 : {
429 : return vector - 2 * Project(vector, planeNormal);
430 : }
431 :
432 : Vector2 Vector2::Reject(Vector2 a, Vector2 b)
433 : {
434 : return a - Project(a, b);
435 : }
436 :
437 : Vector2 Vector2::RotateTowards(Vector2 current, Vector2 target,
438 : double maxRadiansDelta,
439 : double maxMagnitudeDelta)
440 : {
441 : double magCur = Magnitude(current);
442 : double magTar = Magnitude(target);
443 : double newMag = magCur + maxMagnitudeDelta *
444 : ((magTar > magCur) - (magCur > magTar));
445 : newMag = fmin(newMag, fmax(magCur, magTar));
446 : newMag = fmax(newMag, fmin(magCur, magTar));
447 :
448 : double totalAngle = Angle(current, target) - maxRadiansDelta;
449 : if (totalAngle <= 0)
450 : return Normalized(target) * newMag;
451 : else if (totalAngle >= M_PI)
452 : return Normalized(-target) * newMag;
453 :
454 : double axis = current.X * target.Y - current.Y * target.X;
455 : axis = axis / fabs(axis);
456 : if (!(1 - fabs(axis) < 0.00001))
457 : axis = 1;
458 : current = Normalized(current);
459 : Vector2 newVector = current * cos(maxRadiansDelta) +
460 : Vector2(-current.Y, current.X) * sin(maxRadiansDelta) * axis;
461 : return newVector * newMag;
462 : }
463 :
464 : Vector2 Vector2::Scale(Vector2 a, Vector2 b)
465 : {
466 : return Vector2(a.X * b.X, a.Y * b.Y);
467 : }
468 :
469 : Vector2 Vector2::Slerp(Vector2 a, Vector2 b, double t)
470 : {
471 : if (t < 0) return a;
472 : else if (t > 1) return b;
473 : return SlerpUnclamped(a, b, t);
474 : }
475 :
476 : Vector2 Vector2::SlerpUnclamped(Vector2 a, Vector2 b, double t)
477 : {
478 : double magA = Magnitude(a);
479 : double magB = Magnitude(b);
480 : a /= magA;
481 : b /= magB;
482 : double dot = Dot(a, b);
483 : dot = fmax(dot, -1.0);
484 : dot = fmin(dot, 1.0);
485 : double theta = acos(dot) * t;
486 : Vector2 relativeVec = Normalized(b - a * dot);
487 : Vector2 newVec = a * cos(theta) + relativeVec * sin(theta);
488 : return newVec * (magA + (magB - magA) * t);
489 : }
490 :
491 340928 : double Vector2::SqrMagnitude(Vector2 v)
492 : {
493 340928 : return v.X * v.X + v.Y * v.Y;
494 : }
495 :
496 340928 : void Vector2::ToPolar(Vector2 vector, double &rad, double &theta)
497 : {
498 340928 : rad = Magnitude(vector);
499 340928 : theta = atan2(vector.Y, vector.X);
500 340928 : }
501 :
502 :
503 : struct Vector2& Vector2::operator+=(const double rhs)
504 : {
505 : X += rhs;
506 : Y += rhs;
507 : return *this;
508 : }
509 :
510 : struct Vector2& Vector2::operator-=(const double rhs)
511 : {
512 : X -= rhs;
513 : Y -= rhs;
514 : return *this;
515 : }
516 :
517 0 : struct Vector2& Vector2::operator*=(const double rhs)
518 : {
519 0 : X *= rhs;
520 0 : Y *= rhs;
521 0 : return *this;
522 : }
523 :
524 0 : struct Vector2& Vector2::operator/=(const double rhs)
525 : {
526 0 : X /= rhs;
527 0 : Y /= rhs;
528 0 : return *this;
529 : }
530 :
531 0 : struct Vector2& Vector2::operator+=(const Vector2 rhs)
532 : {
533 0 : X += rhs.X;
534 0 : Y += rhs.Y;
535 0 : return *this;
536 : }
537 :
538 0 : struct Vector2& Vector2::operator-=(const Vector2 rhs)
539 : {
540 0 : X -= rhs.X;
541 0 : Y -= rhs.Y;
542 0 : return *this;
543 : }
544 :
545 : Vector2 operator-(Vector2 rhs) { return rhs * -1; }
546 : Vector2 operator+(Vector2 lhs, const double rhs) { return lhs += rhs; }
547 : Vector2 operator-(Vector2 lhs, const double rhs) { return lhs -= rhs; }
548 0 : Vector2 operator*(Vector2 lhs, const double rhs) { return lhs *= rhs; }
549 0 : Vector2 operator/(Vector2 lhs, const double rhs) { return lhs /= rhs; }
550 : Vector2 operator+(const double lhs, Vector2 rhs) { return rhs += lhs; }
551 : Vector2 operator-(const double lhs, Vector2 rhs) { return rhs -= lhs; }
552 : Vector2 operator*(const double lhs, Vector2 rhs) { return rhs *= lhs; }
553 : Vector2 operator/(const double lhs, Vector2 rhs) { return rhs /= lhs; }
554 0 : Vector2 operator+(Vector2 lhs, const Vector2 rhs) { return lhs += rhs; }
555 0 : Vector2 operator-(Vector2 lhs, const Vector2 rhs) { return lhs -= rhs; }
556 :
557 : bool operator==(const Vector2 lhs, const Vector2 rhs)
558 : {
559 : return lhs.X == rhs.X && lhs.Y == rhs.Y;
560 : }
561 :
562 : bool operator!=(const Vector2 lhs, const Vector2 rhs)
563 : {
564 : return !(lhs == rhs);
565 : }
|