/// <summary> /// Returns the inverse of the transform, under the assumption that /// the transformation is composed of rotation, scaling, and translation. /// </summary> /// <seealso cref="Inverse"/> /// <returns>The inverse transformation matrix.</returns> public Transform2D AffineInverse() { real_t det = BasisDeterminant(); if (det == 0) { throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); } Transform2D inv = this; real_t temp = inv[0, 0]; inv[0, 0] = inv[1, 1]; inv[1, 1] = temp; real_t detInv = 1.0f / det; inv[0] *= new Vector2(detInv, -detInv); inv[1] *= new Vector2(-detInv, detInv); inv[2] = inv.BasisXform(-inv[2]); return(inv); }
/// <summary> /// Constructs a new <see cref="Vector4"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> /// <param name="y">The vector's Y component.</param> /// <param name="z">The vector's Z component.</param> /// <param name="w">The vector's W component.</param> public Vector4(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; }
public static Projection CreateForHmd(int eye, real_t aspect, real_t intraocularDist, real_t displayWidth, real_t displayToLens, real_t oversample, real_t zNear, real_t zFar) { real_t f1 = (intraocularDist * (real_t)0.5) / displayToLens; real_t f2 = ((displayWidth - intraocularDist) * (real_t)0.5) / displayToLens; real_t f3 = (displayWidth / (real_t)4.0) / displayToLens; real_t add = ((f1 + f2) * (oversample - (real_t)1.0)) / (real_t)2.0; f1 += add; f2 += add; f3 *= oversample; f3 /= aspect; switch (eye) { case 1: return(CreateFrustum(-f2 * zNear, f1 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar)); case 2: return(CreateFrustum(-f1 * zNear, f2 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar)); default: return(Zero); } }
/// <summary> /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. /// </summary> /// <param name="transform">The other transform.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated transform.</returns> public Transform3D InterpolateWith(Transform3D transform, real_t weight) { Basis retBasis = basis.Lerp(transform.basis, weight); Vector3 retOrigin = origin.Lerp(transform.origin, weight); return(new Transform3D(retBasis, retOrigin)); }
/// <summary> /// Returns an AABB transformed (multiplied) by the transformation matrix. /// </summary> /// <param name="transform">The transformation to apply.</param> /// <param name="aabb">An AABB to transform.</param> /// <returns>The transformed AABB.</returns> public static AABB operator *(Transform3D transform, AABB aabb) { Vector3 min = aabb.Position; Vector3 max = aabb.Position + aabb.Size; Vector3 tmin = transform.origin; Vector3 tmax = transform.origin; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { real_t e = transform.basis[i][j] * min[j]; real_t f = transform.basis[i][j] * max[j]; if (e < f) { tmin[i] += e; tmax[i] += f; } else { tmin[i] += f; tmax[i] += e; } } } return(new AABB(tmin, tmax - tmin)); }
/// <summary> /// Constructs a <see cref="Quaternion"/> that will rotate around the given axis /// by the specified angle. The axis must be a normalized vector. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> /// <param name="angle">The angle to rotate, in radians.</param> public Quaternion(Vector3 axis, real_t angle) { #if DEBUG if (!axis.IsNormalized()) { throw new ArgumentException("Argument is not normalized.", nameof(axis)); } #endif real_t d = axis.Length(); if (d == 0f) { x = 0f; y = 0f; z = 0f; w = 0f; } else { real_t sinAngle = Mathf.Sin(angle * 0.5f); real_t cosAngle = Mathf.Cos(angle * 0.5f); real_t s = sinAngle / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; w = cosAngle; } }
/// <summary> /// Constructs a <see cref="Quaternion"/> defined by the given values. /// </summary> /// <param name="x">X component of the quaternion (imaginary <c>i</c> axis part).</param> /// <param name="y">Y component of the quaternion (imaginary <c>j</c> axis part).</param> /// <param name="z">Z component of the quaternion (imaginary <c>k</c> axis part).</param> /// <param name="w">W component of the quaternion (real part).</param> public Quaternion(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; }
/// <summary> /// Returns the result of the spherical linear interpolation between /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>, but without /// checking if the rotation path is not bigger than 90 degrees. /// </summary> /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting quaternion of the interpolation.</returns> public Quaternion Slerpni(Quaternion to, real_t weight) { #if DEBUG if (!IsNormalized()) { throw new InvalidOperationException("Quaternion is not normalized"); } if (!to.IsNormalized()) { throw new ArgumentException("Argument is not normalized", nameof(to)); } #endif real_t dot = Dot(to); if (Mathf.Abs(dot) > 0.9999f) { return(this); } real_t theta = Mathf.Acos(dot); real_t sinT = 1.0f / Mathf.Sin(theta); real_t newFactor = Mathf.Sin(weight * theta) * sinT; real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT; return(new Quaternion ( (invFactor * x) + (newFactor * to.x), (invFactor * y) + (newFactor * to.y), (invFactor * z) + (newFactor * to.z), (invFactor * w) + (newFactor * to.w) )); }
public static Projection CreateFrustum(real_t left, real_t right, real_t bottom, real_t top, real_t near, real_t far) { if (right <= left) { throw new ArgumentException("right is less or equal to left."); } if (top <= bottom) { throw new ArgumentException("top is less or equal to bottom."); } if (far <= near) { throw new ArgumentException("far is less or equal to near."); } real_t x = 2 * near / (right - left); real_t y = 2 * near / (top - bottom); real_t a = (right + left) / (right - left); real_t b = (top + bottom) / (top - bottom); real_t c = -(far + near) / (far - near); real_t d = -2 * far * near / (far - near); return(new Projection( new Vector4(x, 0, 0, 0), new Vector4(0, y, 0, 0), new Vector4(a, b, c, -1), new Vector4(0, 0, d, 0) )); }
/// <summary> /// Constructs a transformation matrix from a <paramref name="rotation"/> value and /// <paramref name="origin"/> vector. /// </summary> /// <param name="rotation">The rotation of the new transform, in radians.</param> /// <param name="origin">The origin vector, or column index 2.</param> public Transform2D(real_t rotation, Vector2 origin) { x.x = y.y = Mathf.Cos(rotation); x.y = y.x = Mathf.Sin(rotation); y.x *= -1; this.origin = origin; }
/// <summary> /// Linearly interpolates between two angles (in radians) by a normalized value. /// /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, /// but interpolates correctly when the angles wrap around <see cref="Tau"/>. /// </summary> /// <param name="from">The start angle for interpolation.</param> /// <param name="to">The destination angle for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting angle of the interpolation.</returns> public static real_t LerpAngle(real_t from, real_t to, real_t weight) { real_t difference = (to - from) % Mathf.Tau; real_t distance = ((2 * difference) % Mathf.Tau) - difference; return(from + (distance * weight)); }
/// <summary> /// Returns the position of the first non-zero digit, after the /// decimal point. Note that the maximum return value is 10, /// which is a design decision in the implementation. /// </summary> /// <param name="step">The input value.</param> /// <returns>The position of the first non-zero digit.</returns> public static int StepDecimals(real_t step) { double[] sd = new double[] { 0.9999, 0.09999, 0.009999, 0.0009999, 0.00009999, 0.000009999, 0.0000009999, 0.00000009999, 0.000000009999, }; double abs = Abs(step); double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < sd.Length; i++) { if (decs >= sd[i]) { return(i); } } return(0); }
/// <summary> /// Easing function, based on exponent. The <paramref name="curve"/> values are: /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. /// Negative values are in-out/out-in. /// </summary> /// <param name="s">The value to ease.</param> /// <param name="curve"> /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. /// </param> /// <returns>The eased value.</returns> public static real_t Ease(real_t s, real_t curve) { if (s < 0f) { s = 0f; } else if (s > 1.0f) { s = 1.0f; } if (curve > 0f) { if (curve < 1.0f) { return(1.0f - Pow(1.0f - s, 1.0f / curve)); } return(Pow(s, curve)); } if (curve < 0f) { if (s < 0.5f) { return(Pow(s * 2.0f, -curve) * 0.5f); } return(((1.0f - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f); } return(0f); }
/// <summary> /// Helper method for deconstruction into a tuple. /// </summary> public void Deconstruct(out real_t x, out real_t y, out real_t z, out real_t w) { x = this.x; y = this.y; z = this.z; w = this.w; }
public static Projection CreatePerspective(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov) { if (flipFov) { fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect); } real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0); real_t deltaZ = zFar - zNear; real_t sine = Mathf.Sin(radians); if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) { return(Zero); } real_t cotangent = Mathf.Cos(radians) / sine; Projection proj = Projection.Identity; proj.x.x = cotangent / aspect; proj.y.y = cotangent; proj.z.z = -(zFar + zNear) / deltaZ; proj.z.w = -1; proj.w.z = -2 * zNear * zFar / deltaZ; proj.w.w = 0; return(proj); }
/// <summary> /// Access vector components using their index. /// </summary> /// <exception cref="IndexOutOfRangeException"> /// Thrown when the given the <paramref name="index"/> is not 0 or 1. /// </exception> /// <value> /// <c>[0]</c> is equivalent to <see cref="x"/>, /// <c>[1]</c> is equivalent to <see cref="y"/>. /// </value> public real_t this[int index] { get { switch (index) { case 0: return(x); case 1: return(y); default: throw new ArgumentOutOfRangeException(nameof(index)); } } set { switch (index) { case 0: x = value; return; case 1: y = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); } } }
/// <summary> /// Returns the squared length (squared magnitude) of this vector. /// This method runs faster than <see cref="Length"/>, so prefer it if /// you need to compare vectors or need the squared length for some formula. /// </summary> /// <returns>The squared length of this vector.</returns> public real_t LengthSquared() { real_t x2 = x * x; real_t y2 = y * y; real_t z2 = z * z; return(x2 + y2 + z2); }
/// <summary> /// Returns the length (magnitude) of this vector. /// </summary> /// <seealso cref="LengthSquared"/> /// <returns>The length of this vector.</returns> public real_t Length() { real_t x2 = x * x; real_t y2 = y * y; real_t z2 = z * z; return(Mathf.Sqrt(x2 + y2 + z2)); }
/// <summary> /// Returns the result of the linear interpolation between /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting vector of the interpolation.</returns> public Vector2 Lerp(Vector2 to, real_t weight) { return(new Vector2 ( Mathf.Lerp(x, to.x, weight), Mathf.Lerp(y, to.y, weight) )); }
/// <summary> /// Returns the signed angle to the given vector, in radians. /// The sign of the angle is positive in a counter-clockwise /// direction and negative in a clockwise direction when viewed /// from the side specified by the <paramref name="axis"/>. /// </summary> /// <param name="to">The other vector to compare this vector to.</param> /// <param name="axis">The reference axis to use for the angle sign.</param> /// <returns>The signed angle between the two vectors, in radians.</returns> public real_t SignedAngleTo(Vector3 to, Vector3 axis) { Vector3 crossTo = Cross(to); real_t unsignedAngle = Mathf.Atan2(crossTo.Length(), Dot(to)); real_t sign = crossTo.Dot(axis); return((sign < 0) ? -unsignedAngle : unsignedAngle); }
public static Projection CreateFrustumAspect(real_t size, real_t aspect, Vector2 offset, real_t near, real_t far, bool flipFov) { if (!flipFov) { size *= aspect; } return(CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far)); }
/// <summary> /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> /// <returns> /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>. /// </returns> public Vector2 PosMod(real_t mod) { Vector2 v; v.x = Mathf.PosMod(x, mod); v.y = Mathf.PosMod(y, mod); return(v); }
/// <summary> /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> /// with pre and post values. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> /// <param name="pre">The value which before "from" value for interpolation.</param> /// <param name="post">The value which after "to" value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> public static real_t CubicInterpolate(real_t from, real_t to, real_t pre, real_t post, real_t weight) { return(0.5f * ((from * 2.0f) + (-pre + to) * weight + (2.0f * pre - 5.0f * from + 4.0f * to - post) * (weight * weight) + (-pre + 3.0f * from - 3.0f * to + post) * (weight * weight * weight))); }
public static Projection CreateOrthogonalAspect(real_t size, real_t aspect, real_t zNear, real_t zFar, bool flipFov) { if (!flipFov) { size *= aspect; } return(CreateOrthogonal(-size / 2, +size / 2, -size / aspect / 2, +size / aspect / 2, zNear, zFar)); }
/// <summary> /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector, /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>. /// </summary> /// <param name="b">The destination vector.</param> /// <param name="preA">A vector before this vector.</param> /// <param name="postB">A vector after <paramref name="b"/>.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated vector.</returns> public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight) { return(new Vector2 ( Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight), Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight) )); }
/// <summary> /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>. /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>. /// </summary> /// <param name="s">The input number.</param> /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public static int Sign(real_t s) { if (s == 0) { return(0); } return(s < 0 ? -1 : 1); }
/// <summary> /// Snaps float value <paramref name="s"/> to a given <paramref name="step"/>. /// This can also be used to round a floating point number to an arbitrary number of decimals. /// </summary> /// <param name="s">The value to snap.</param> /// <param name="step">The step size to snap to.</param> /// <returns>The snapped value.</returns> public static real_t Snapped(real_t s, real_t step) { if (step != 0f) { return(Floor((s / step) + 0.5f) * step); } return(s); }
/// <summary> /// Returns the result of the linear interpolation between /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting vector of the interpolation.</returns> public Vector3 Lerp(Vector3 to, real_t weight) { return(new Vector3 ( Mathf.Lerp(x, to.x, weight), Mathf.Lerp(y, to.y, weight), Mathf.Lerp(z, to.z, weight) )); }
/// <summary> /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value. /// /// Use a negative <paramref name="delta"/> value to move away. /// </summary> /// <param name="from">The start value.</param> /// <param name="to">The value to move towards.</param> /// <param name="delta">The amount to move by.</param> /// <returns>The value after moving.</returns> public static real_t MoveToward(real_t from, real_t to, real_t delta) { if (Abs(to - from) <= delta) { return(to); } return(from + (Sign(to - from) * delta)); }
/// <summary> /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> /// <returns> /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>. /// </returns> public Vector3 PosMod(real_t mod) { Vector3 v; v.x = Mathf.PosMod(x, mod); v.y = Mathf.PosMod(y, mod); v.z = Mathf.PosMod(z, mod); return(v); }