/* * Farseer Physics Engine: * Copyright (c) 2012 Ian Qvist * * Original source Box2D: * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ using System; using System.Diagnostics; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; namespace FarseerPhysics.Common { public static class MathUtils { public static float Cross(ref Vector2 a, ref Vector2 b) { return a.X * b.Y - a.Y * b.X; } public static float Cross(Vector2 a, Vector2 b) { return Cross(ref a, ref b); } /// Perform the cross product on two vectors. public static Vector3 Cross(Vector3 a, Vector3 b) { return new Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } public static Vector2 Cross(Vector2 a, float s) { return new Vector2(s * a.Y, -s * a.X); } public static Vector2 Cross(float s, Vector2 a) { return new Vector2(-s * a.Y, s * a.X); } public static Vector2 Abs(Vector2 v) { return new Vector2(Math.Abs(v.X), Math.Abs(v.Y)); } public static Vector2 Mul(ref Mat22 A, Vector2 v) { return Mul(ref A, ref v); } public static Vector2 Mul(ref Mat22 A, ref Vector2 v) { return new Vector2(A.ex.X * v.X + A.ey.X * v.Y, A.ex.Y * v.X + A.ey.Y * v.Y); } public static Vector2 Mul(ref Transform T, Vector2 v) { return Mul(ref T, ref v); } public static Vector2 Mul(ref Transform T, ref Vector2 v) { return new Vector2( (T.q.c * v.X - T.q.s * v.Y) + T.p.X, (T.q.s * v.X + T.q.c * v.Y) + T.p.Y); } public static Vector2 MulT(ref Mat22 A, Vector2 v) { return MulT(ref A, ref v); } public static Vector2 MulT(ref Mat22 A, ref Vector2 v) { return new Vector2(v.X * A.ex.X + v.Y * A.ex.Y, v.X * A.ey.X + v.Y * A.ey.Y); } public static Vector2 MulT(ref Transform T, Vector2 v) { return MulT(ref T, ref v); } public static Vector2 MulT(ref Transform T, ref Vector2 v) { float px = v.X - T.p.X; float py = v.Y - T.p.Y; return new Vector2(T.q.c * px + T.q.s * py, -T.q.s * px + T.q.c * py); } // A^T * B public static void MulT(ref Mat22 A, ref Mat22 B, out Mat22 C) { C = new Mat22(); C.ex.X = A.ex.X * B.ex.X + A.ex.Y * B.ex.Y; C.ex.Y = A.ey.X * B.ex.X + A.ey.Y * B.ex.Y; C.ey.X = A.ex.X * B.ey.X + A.ex.Y * B.ey.Y; C.ey.Y = A.ey.X * B.ey.X + A.ey.Y * B.ey.Y; } /// Multiply a matrix times a vector. public static Vector3 Mul(Mat33 A, Vector3 v) { return v.X * A.ex + v.Y * A.ey + v.Z * A.ez; } // v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p // = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p public static Transform Mul(Transform A, Transform B) { Transform C = new Transform(); C.q = Mul(A.q, B.q); C.p = Mul(A.q, B.p) + A.p; return C; } // v2 = A.q' * (B.q * v1 + B.p - A.p) // = A.q' * B.q * v1 + A.q' * (B.p - A.p) public static void MulT(ref Transform A, ref Transform B, out Transform C) { C = new Transform(); C.q = MulT(A.q, B.q); C.p = MulT(A.q, B.p - A.p); } public static void Swap(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } /// Multiply a matrix times a vector. public static Vector2 Mul22(Mat33 A, Vector2 v) { return new Vector2(A.ex.X * v.X + A.ey.X * v.Y, A.ex.Y * v.X + A.ey.Y * v.Y); } /// Multiply two rotations: q * r public static Rot Mul(Rot q, Rot r) { // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc] // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc] // s = qs * rc + qc * rs // c = qc * rc - qs * rs Rot qr; qr.s = q.s * r.c + q.c * r.s; qr.c = q.c * r.c - q.s * r.s; return qr; } public static Vector2 MulT(Transform T, Vector2 v) { float px = v.X - T.p.X; float py = v.Y - T.p.Y; float x = (T.q.c * px + T.q.s * py); float y = (-T.q.s * px + T.q.c * py); return new Vector2(x, y); } /// Transpose multiply two rotations: qT * r public static Rot MulT(Rot q, Rot r) { // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc] // [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc] // s = qc * rs - qs * rc // c = qc * rc + qs * rs Rot qr; qr.s = q.c * r.s - q.s * r.c; qr.c = q.c * r.c + q.s * r.s; return qr; } // v2 = A.q' * (B.q * v1 + B.p - A.p) // = A.q' * B.q * v1 + A.q' * (B.p - A.p) public static Transform MulT(Transform A, Transform B) { Transform C = new Transform(); C.q = MulT(A.q, B.q); C.p = MulT(A.q, B.p - A.p); return C; } /// Rotate a vector public static Vector2 Mul(Rot q, Vector2 v) { return new Vector2(q.c * v.X - q.s * v.Y, q.s * v.X + q.c * v.Y); } /// Inverse rotate a vector public static Vector2 MulT(Rot q, Vector2 v) { return new Vector2(q.c * v.X + q.s * v.Y, -q.s * v.X + q.c * v.Y); } /// Get the skew vector such that dot(skew_vec, other) == cross(vec, other) public static Vector2 Skew(Vector2 input) { return new Vector2(-input.Y, input.X); } /// /// This function is used to ensure that a floating point number is /// not a NaN or infinity. /// /// The x. /// /// true if the specified x is valid; otherwise, false. /// public static bool IsValid(float x) { if (float.IsNaN(x)) { // NaN. return false; } return !float.IsInfinity(x); } public static bool IsValid(this Vector2 x) { return IsValid(x.X) && IsValid(x.Y); } /// /// This is a approximate yet fast inverse square-root. /// /// The x. /// public static float InvSqrt(float x) { FloatConverter convert = new FloatConverter(); convert.x = x; float xhalf = 0.5f * x; convert.i = 0x5f3759df - (convert.i >> 1); x = convert.x; x = x * (1.5f - xhalf * x * x); return x; } public static int Clamp(int a, int low, int high) { return Math.Max(low, Math.Min(a, high)); } public static float Clamp(float a, float low, float high) { return Math.Max(low, Math.Min(a, high)); } public static Vector2 Clamp(Vector2 a, Vector2 low, Vector2 high) { return Vector2.Max(low, Vector2.Min(a, high)); } public static void Cross(ref Vector2 a, ref Vector2 b, out float c) { c = a.X * b.Y - a.Y * b.X; } /// /// Return the angle between two vectors on a plane /// The angle is from vector 1 to vector 2, positive anticlockwise /// The result is between -pi -> pi /// public static double VectorAngle(ref Vector2 p1, ref Vector2 p2) { double theta1 = Math.Atan2(p1.Y, p1.X); double theta2 = Math.Atan2(p2.Y, p2.X); double dtheta = theta2 - theta1; while (dtheta > Math.PI) dtheta -= (2 * Math.PI); while (dtheta < -Math.PI) dtheta += (2 * Math.PI); return (dtheta); } /// Perform the dot product on two vectors. public static float Dot(Vector3 a, Vector3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } public static double VectorAngle(Vector2 p1, Vector2 p2) { return VectorAngle(ref p1, ref p2); } /// /// Returns a positive number if c is to the left of the line going from a to b. /// /// Positive number if point is left, negative if point is right, /// and 0 if points are collinear. public static float Area(Vector2 a, Vector2 b, Vector2 c) { return Area(ref a, ref b, ref c); } /// /// Returns a positive number if c is to the left of the line going from a to b. /// /// Positive number if point is left, negative if point is right, /// and 0 if points are collinear. public static float Area(ref Vector2 a, ref Vector2 b, ref Vector2 c) { return a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y); } /// /// Determines if three vertices are collinear (ie. on a straight line) /// /// First vertex /// Second vertex /// Third vertex /// The tolerance /// public static bool IsCollinear(ref Vector2 a, ref Vector2 b, ref Vector2 c, float tolerance = 0) { return FloatInRange(Area(ref a, ref b, ref c), -tolerance, tolerance); } public static void Cross(float s, ref Vector2 a, out Vector2 b) { b = new Vector2(-s * a.Y, s * a.X); } public static bool FloatEquals(float value1, float value2) { return Math.Abs(value1 - value2) <= Settings.Epsilon; } /// /// Checks if a floating point Value is equal to another, /// within a certain tolerance. /// /// The first floating point Value. /// The second floating point Value. /// The floating point tolerance. /// True if the values are "equal", false otherwise. public static bool FloatEquals(float value1, float value2, float delta) { return FloatInRange(value1, value2 - delta, value2 + delta); } /// /// Checks if a floating point Value is within a specified /// range of values (inclusive). /// /// The Value to check. /// The minimum Value. /// The maximum Value. /// True if the Value is within the range specified, /// false otherwise. public static bool FloatInRange(float value, float min, float max) { return (value >= min && value <= max); } #region Nested type: FloatConverter [StructLayout(LayoutKind.Explicit)] private struct FloatConverter { [FieldOffset(0)] public float x; [FieldOffset(0)] public int i; } #endregion public static Vector2 Mul(ref Rot rot, Vector2 axis) { return Mul(rot, axis); } public static Vector2 MulT(ref Rot rot, Vector2 axis) { return MulT(rot, axis); } } /// /// A 2-by-2 matrix. Stored in column-major order. /// public struct Mat22 { public Vector2 ex, ey; /// /// Construct this matrix using columns. /// /// The c1. /// The c2. public Mat22(Vector2 c1, Vector2 c2) { ex = c1; ey = c2; } /// /// Construct this matrix using scalars. /// /// The a11. /// The a12. /// The a21. /// The a22. public Mat22(float a11, float a12, float a21, float a22) { ex = new Vector2(a11, a21); ey = new Vector2(a12, a22); } public Mat22 Inverse { get { float a = ex.X, b = ey.X, c = ex.Y, d = ey.Y; float det = a * d - b * c; if (det != 0.0f) { det = 1.0f / det; } Mat22 result = new Mat22(); result.ex.X = det * d; result.ex.Y = -det * c; result.ey.X = -det * b; result.ey.Y = det * a; return result; } } /// /// Initialize this matrix using columns. /// /// The c1. /// The c2. public void Set(Vector2 c1, Vector2 c2) { ex = c1; ey = c2; } /// /// Set this to the identity matrix. /// public void SetIdentity() { ex.X = 1.0f; ey.X = 0.0f; ex.Y = 0.0f; ey.Y = 1.0f; } /// /// Set this matrix to all zeros. /// public void SetZero() { ex.X = 0.0f; ey.X = 0.0f; ex.Y = 0.0f; ey.Y = 0.0f; } /// /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. /// /// The b. /// public Vector2 Solve(Vector2 b) { float a11 = ex.X, a12 = ey.X, a21 = ex.Y, a22 = ey.Y; float det = a11 * a22 - a12 * a21; if (det != 0.0f) { det = 1.0f / det; } return new Vector2(det * (a22 * b.X - a12 * b.Y), det * (a11 * b.Y - a21 * b.X)); } public static void Add(ref Mat22 A, ref Mat22 B, out Mat22 R) { R.ex = A.ex + B.ex; R.ey = A.ey + B.ey; } } /// /// A 3-by-3 matrix. Stored in column-major order. /// public struct Mat33 { public Vector3 ex, ey, ez; /// /// Construct this matrix using columns. /// /// The c1. /// The c2. /// The c3. public Mat33(Vector3 c1, Vector3 c2, Vector3 c3) { ex = c1; ey = c2; ez = c3; } /// /// Set this matrix to all zeros. /// public void SetZero() { ex = Vector3.Zero; ey = Vector3.Zero; ez = Vector3.Zero; } /// /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. /// /// The b. /// public Vector3 Solve33(Vector3 b) { float det = Vector3.Dot(ex, Vector3.Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } return new Vector3(det * Vector3.Dot(b, Vector3.Cross(ey, ez)), det * Vector3.Dot(ex, Vector3.Cross(b, ez)), det * Vector3.Dot(ex, Vector3.Cross(ey, b))); } /// /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. Solve only the upper /// 2-by-2 matrix equation. /// /// The b. /// public Vector2 Solve22(Vector2 b) { float a11 = ex.X, a12 = ey.X, a21 = ex.Y, a22 = ey.Y; float det = a11 * a22 - a12 * a21; if (det != 0.0f) { det = 1.0f / det; } return new Vector2(det * (a22 * b.X - a12 * b.Y), det * (a11 * b.Y - a21 * b.X)); } /// Get the inverse of this matrix as a 2-by-2. /// Returns the zero matrix if singular. public void GetInverse22(ref Mat33 M) { float a = ex.X, b = ey.X, c = ex.Y, d = ey.Y; float det = a * d - b * c; if (det != 0.0f) { det = 1.0f / det; } M.ex.X = det * d; M.ey.X = -det * b; M.ex.Z = 0.0f; M.ex.Y = -det * c; M.ey.Y = det * a; M.ey.Z = 0.0f; M.ez.X = 0.0f; M.ez.Y = 0.0f; M.ez.Z = 0.0f; } /// Get the symmetric inverse of this matrix as a 3-by-3. /// Returns the zero matrix if singular. public void GetSymInverse33(ref Mat33 M) { float det = MathUtils.Dot(ex, MathUtils.Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } float a11 = ex.X, a12 = ey.X, a13 = ez.X; float a22 = ey.Y, a23 = ez.Y; float a33 = ez.Z; M.ex.X = det * (a22 * a33 - a23 * a23); M.ex.Y = det * (a13 * a23 - a12 * a33); M.ex.Z = det * (a12 * a23 - a13 * a22); M.ey.X = M.ex.Y; M.ey.Y = det * (a11 * a33 - a13 * a13); M.ey.Z = det * (a13 * a12 - a11 * a23); M.ez.X = M.ex.Z; M.ez.Y = M.ey.Z; M.ez.Z = det * (a11 * a22 - a12 * a12); } } /// /// Rotation /// public struct Rot { /// Sine and cosine public float s, c; /// /// Initialize from an angle in radians /// /// Angle in radians public Rot(float angle) { // TODO_ERIN optimize s = (float)Math.Sin(angle); c = (float)Math.Cos(angle); } /// /// Set using an angle in radians. /// /// public void Set(float angle) { // TODO_ERIN optimize s = (float)Math.Sin(angle); c = (float)Math.Cos(angle); } /// /// Set to the identity rotation /// public void SetIdentity() { s = 0.0f; c = 1.0f; } /// /// Get the angle in radians /// public float GetAngle() { return (float)Math.Atan2(s, c); } /// /// Get the x-axis /// public Vector2 GetXAxis() { return new Vector2(c, s); } /// /// Get the y-axis /// public Vector2 GetYAxis() { return new Vector2(-s, c); } } /// /// A transform contains translation and rotation. It is used to represent /// the position and orientation of rigid frames. /// public struct Transform { public Vector2 p; public Rot q; /// /// Initialize using a position vector and a rotation matrix. /// /// The position. /// The r. public Transform(ref Vector2 position, ref Rot rotation) { p = position; q = rotation; } /// /// Set this to the identity transform. /// public void SetIdentity() { p = Vector2.Zero; q.SetIdentity(); } /// /// Set this based on the position and angle. /// /// The position. /// The angle. public void Set(Vector2 position, float angle) { p = position; q.Set(angle); } } /// /// This describes the motion of a body/shape for TOI computation. /// Shapes are defined with respect to the body origin, which may /// no coincide with the center of mass. However, to support dynamics /// we must interpolate the center of mass position. /// public struct Sweep { /// /// World angles /// public float A; public float A0; /// /// Fraction of the current time step in the range [0,1] /// c0 and a0 are the positions at alpha0. /// public float Alpha0; /// /// Center world positions /// public Vector2 C; public Vector2 C0; /// /// Local center of mass position /// public Vector2 LocalCenter; /// /// Get the interpolated transform at a specific time. /// /// The transform. /// beta is a factor in [0,1], where 0 indicates alpha0. public void GetTransform(out Transform xfb, float beta) { xfb = new Transform(); xfb.p.X = (1.0f - beta) * C0.X + beta * C.X; xfb.p.Y = (1.0f - beta) * C0.Y + beta * C.Y; float angle = (1.0f - beta) * A0 + beta * A; xfb.q.Set(angle); // Shift to origin xfb.p -= MathUtils.Mul(xfb.q, LocalCenter); } /// /// Advance the sweep forward, yielding a new initial state. /// /// new initial time.. public void Advance(float alpha) { Debug.Assert(Alpha0 < 1.0f); float beta = (alpha - Alpha0) / (1.0f - Alpha0); C0 += beta * (C - C0); A0 += beta * (A - A0); Alpha0 = alpha; } /// /// Normalize the angles. /// public void Normalize() { float d = MathHelper.TwoPi * (float)Math.Floor(A0 / MathHelper.TwoPi); A0 -= d; A -= d; } } }