예제 #1
0
 /// <summary>Constructors</summary>
 public m4x4(v4 x, v4 y, v4 z, v4 w) : this()
 {
     this.x = x;
     this.y = y;
     this.z = z;
     this.w = w;
 }
예제 #2
0
        /// <summary>Create a rotation from an axis and angle</summary>
        public static m3x4 Rotation(v4 axis_norm, v4 axis_sine_angle, float cos_angle)
        {
            Debug.Assert(Math_.FEql(axis_norm.LengthSq, 1f), "'axis_norm' should be normalised");

            var mat = new m3x4();

            v4 trace_vec = axis_norm * (1.0f - cos_angle);

            mat.x.x = trace_vec.x * axis_norm.x + cos_angle;
            mat.y.y = trace_vec.y * axis_norm.y + cos_angle;
            mat.z.z = trace_vec.z * axis_norm.z + cos_angle;

            trace_vec.x *= axis_norm.y;
            trace_vec.z *= axis_norm.x;
            trace_vec.y *= axis_norm.z;

            mat.x.y = trace_vec.x + axis_sine_angle.z;
            mat.x.z = trace_vec.z - axis_sine_angle.y;
            mat.x.w = 0.0f;
            mat.y.x = trace_vec.x - axis_sine_angle.z;
            mat.y.z = trace_vec.y + axis_sine_angle.x;
            mat.y.w = 0.0f;
            mat.z.x = trace_vec.z + axis_sine_angle.y;
            mat.z.y = trace_vec.y - axis_sine_angle.x;
            mat.z.w = 0.0f;

            return(mat);
        }
예제 #3
0
 /// <summary>Returns true if 'point' is within this bounding box (within 'tol'erance)</summary>
 public bool IsWithin(v4 point, float tol)
 {
     return
         (Math.Abs(point.x - Centre.x) <= Radius.x + tol &&
          Math.Abs(point.y - Centre.y) <= Radius.y + tol &&
          Math.Abs(point.z - Centre.z) <= Radius.z + tol);
 }
예제 #4
0
        /// <summary>Returns a bbox that encloses 'lhs' and 'point'</summary>
        public static BBox Union(BBox bbox, v4 point)
        {
            var bb = bbox;

            for (int i = 0; i != 3; ++i)
            {
                if (bb.Radius[i] < 0.0f)
                {
                    bb.Centre[i] = point[i];
                    bb.Radius[i] = 0.0f;
                }
                else
                {
                    var signed_dist = point[i] - bb.Centre[i];
                    var length      = Math.Abs(signed_dist);
                    if (length > bb.Radius[i])
                    {
                        float new_radius = (length + bb.Radius[i]) / 2.0f;
                        bb.Centre[i] += signed_dist * (new_radius - bb.Radius[i]) / length;
                        bb.Radius[i]  = new_radius;
                    }
                }
            }
            return(bb);
        }
예제 #5
0
        /// <summary>Get/Set components by index</summary>
        public                      v4 this[int i]
        {
            get
            {
                switch (i)
                {
                case 0: return(x);

                case 1: return(y);

                case 2: return(z);
                }
                throw new ArgumentException("index out of range", "i");
            }
            set
            {
                switch (i)
                {
                case 0: x = value; return;

                case 1: y = value; return;

                case 2: z = value; return;
                }
                throw new ArgumentException("index out of range", "i");
            }
        }
예제 #6
0
        /// <summary>Returns the squared distance from 'point' to 'bbox'</summary>
        public static float DistanceSq(v4 point, BBox bbox)
        {
            float dist_sq = 0.0f;
            v4    lower   = bbox.Lower();
            v4    upper   = bbox.Upper();

            if (point.x < lower.x)
            {
                dist_sq += Math_.Sqr(lower.x - point.x);
            }
            else if (point.x > upper.x)
            {
                dist_sq += Math_.Sqr(point.x - upper.x);
            }
            if (point.y < lower.y)
            {
                dist_sq += Math_.Sqr(lower.y - point.y);
            }
            else if (point.y > upper.y)
            {
                dist_sq += Math_.Sqr(point.y - upper.y);
            }
            if (point.z < lower.z)
            {
                dist_sq += Math_.Sqr(lower.z - point.z);
            }
            else if (point.z > upper.z)
            {
                dist_sq += Math_.Sqr(point.z - upper.z);
            }
            return(dist_sq);
        }
예제 #7
0
 /// <summary>Return the cross product matrix for 'vec'</summary>
 public static m3x4 CPM(v4 vec)
 {
     return(new m3x4(
                new v4(0f, +vec.z, -vec.y, 0f),
                new v4(-vec.z, 0f, +vec.x, 0f),
                new v4(+vec.y, -vec.x, 0f, 0f)));
 }
예제 #8
0
        /// <summary>
        /// Find the closest point on 'spline' to 'pt'
        /// Note: the analytic solution to this problem involves solving a 5th order polynomial
        /// This method uses Newton's method and relies on a "good" initial estimate of the nearest point
        /// Should have quadratic convergence</summary>
        public static float ClosestPoint(Spline spline, v4 pt, float initial_estimate, bool bound01 = true, int iterations = 5)
        {
            // The distance (sqr'd) from 'pt' to the spline is: Dist(t) = |pt - S(t)|^2.    (S(t) = spline at t)
            // At the closest point, Dist'(t) = 0.
            // Dist'(t) = -2(pt - S(t)).S'(t)
            // So we want to find 't' such that Dist'(t) = 0
            // Newton's method of iteration = t_next = t_current - f(x)/f'(x)
            //	f(x) = Dist'(t)
            //	f'(x) = Dist''(t) = 2S'(t).S'(t) - 2(pt - S(t)).S''(t)
            float time = initial_estimate;

            for (int iter = 0; iter != iterations; ++iter)
            {
                v4 S   = spline.Position(time);
                v4 dS  = spline.Velocity(time);
                v4 ddS = spline.Acceleration(time);
                v4 R   = pt - S;
                time += Math_.Dot(R, dS) / (Math_.Dot(dS, dS) - Math_.Dot(R, ddS));
                if (bound01 && time <= 0.0f || time >= 1.0f)
                {
                    return(Math_.Clamp(time, 0.0f, 1.0f));
                }
            }
            return(time);
        }
예제 #9
0
        /// <summary>Create from an angular displacement vector. length = angle(rad), direction = axis</summary>
        public static m3x4 Rotation(v4 angular_displacement)
        {
            Debug.Assert(angular_displacement.w == 0, "'angular_displacement' should be a scaled direction vector");
            var len = angular_displacement.Length;

            return(len > Math_.TinyF
                                ? Rotation(angular_displacement / len, len)
                                : Identity);
        }
예제 #10
0
        /// <summary>Return the acceleration along the spline at 'time'</summary>
        public v4 Acceleration(float time)
        {
            var ddblend = new v4(             // the 2nd derivative of blend
                6.0f * (1.0f - time),
                6.0f * (3.0f * time - 2.0f),
                6.0f * (1.0f - 3.0f * time),
                6.0f * time);

            return((m * ddblend).w0);
        }
예제 #11
0
        /// <summary>
        /// Return the tangent along the spline at 'time'
        /// Notes about velocity:
        /// A spline from (0,0,0) to (1,0,0) with control points at (1/3,0,0) and (2/3,0,0) will
        /// have a constant velocity of (1,0,0) over the full length of the spline.</summary>
        public v4 Velocity(float time)
        {
            var dblend = new v4(             // the derivative of blend
                3.0f * (time - 1.0f) * (1.0f - time),
                3.0f * (1.0f - time) * (1.0f - 3.0f * time),
                3.0f * time * (2.0f - 3.0f * time),
                3.0f * time * time);

            return((m * dblend).w0);
        }
예제 #12
0
        /// <summary>Return the position along the spline at 'time'</summary>
        public v4 Position(float time)
        {
            var blend = new v4(
                (1.0f - time) * (1.0f - time) * (1.0f - time),
                3.0f * time * (1.0f - time) * (1.0f - time),
                3.0f * time * time * (1.0f - time),
                time * time * time);

            return((m * blend).w1);
        }
예제 #13
0
        /// <summary>Create a quaternion from an axis and an angle</summary>
        public quat(v4 axis, float angle)
            : this()
        {
            var s = (float)Math.Sin(0.5 * angle);

            x = s * axis.x;
            y = s * axis.y;
            z = s * axis.z;
            w = (float)Math.Cos(0.5 * angle);
        }
예제 #14
0
        /// <summary>
        /// Clip a line segment to a bounding box returning the parametric values of the intersection.
        /// Returns false if the line does not intersect with the bounding box.
        /// Assumes initial values of 't0' and 't1' have been set.</summary>
        public static bool Clip(BBox bbox, v4 point_, v4 direction_, ref float t0, ref float t1)
        {
            // Convert v4's to float arrays for efficient indexing
            var lower     = (float[])bbox.Lower();
            var upper     = (float[])bbox.Upper();
            var point     = (float[])point_;
            var direction = (float[])direction_;

            // For all three slabs
            for (int i = 0; i != 3; ++i)
            {
                if (Math.Abs(direction[i]) < Math_.TinyF)
                {
                    // Ray is parallel to slab. No hit if origin not within slab
                    if (point[i] < lower[i] || point[i] > upper[i])
                    {
                        return(false);
                    }
                }
                else
                {
                    // Compute intersection t value of ray with near and far plane of slab
                    float u0 = (lower[i] - point[i]) / direction[i];
                    float u1 = (upper[i] - point[i]) / direction[i];

                    // Make u0 be intersection with near plane, u1 with far plane
                    if (u0 > u1)
                    {
                        Math_.Swap(ref u0, ref u1);
                    }

                    // Record the tightest bounds on the line segment
                    if (u0 > t0)
                    {
                        t0 = u0;
                    }
                    if (u1 < t1)
                    {
                        t1 = u1;
                    }

                    // Exit with no collision as soon as slab intersection becomes empty
                    if (t0 > t1)
                    {
                        return(false);
                    }
                }
            }

            // Ray intersects all 3 slabs
            return(true);
        }
예제 #15
0
        // Orientation matrix to "look" at a point
        public static m4x4 LookAt(v4 eye, v4 at, v4 up)
        {
            Debug.Assert(eye.w == 1.0f && at.w == 1.0f && up.w == 0.0f, "Invalid position/direction vectors passed to LookAt");
            Debug.Assert(eye - at != v4.Zero, "LookAt 'eye' and 'at' positions are coincident");
            Debug.Assert(!Math_.Parallel(eye - at, up), "LookAt 'forward' and 'up' axes are aligned");
            var mat = new m4x4 {
            };

            mat.z   = Math_.Normalise(eye - at);
            mat.x   = Math_.Normalise(Math_.Cross(up, mat.z));
            mat.y   = Math_.Cross(mat.z, mat.x);
            mat.pos = eye;
            return(mat);
        }
예제 #16
0
        /// <summary>Return the closest point between two line segments</summary>
        public static void ClosestPoint(v4 s0, v4 e0, v4 s1, v4 e1, out float t0, out float t1)
        {
            Debug.Assert(s0.w == 1f && e0.w == 1f && s1.w == 1f && e1.w == 1f);

            v4    line0           = e0 - s0;
            v4    line1           = e1 - s1;
            v4    separation      = s0 - s1;
            float f               = Math_.Dot(line1, separation);
            float c               = Math_.Dot(line0, separation);
            float line0_length_sq = line0.LengthSq;
            float line1_length_sq = line1.LengthSq;

            // Check if either or both segments are degenerate
            if (Math_.FEql(line0_length_sq, 0f) && Math_.FEql(line1_length_sq, 0f))
            {
                t0 = 0.0f; t1 = 0.0f; return;
            }
            if (Math_.FEql(line0_length_sq, 0f))
            {
                t0 = 0.0f; t1 = Math_.Clamp(f / line1_length_sq, 0.0f, 1.0f); return;
            }
            if (Math_.FEql(line1_length_sq, 0f))
            {
                t1 = 0.0f; t0 = Math_.Clamp(-c / line0_length_sq, 0.0f, 1.0f); return;
            }

            // The general nondegenerate case starts here
            float b     = Math_.Dot(line0, line1);
            float denom = line0_length_sq * line1_length_sq - b * b;             // Always non-negative

            // If segments not parallel, calculate closest point on infinite line 'line0'
            // to infinite line 'line1', and clamp to segment 1. Otherwise pick arbitrary t0
            t0 = denom != 0.0f ? Math_.Clamp((b * f - c * line1_length_sq) / denom, 0.0f, 1.0f) : 0.0f;

            // Calculate point on infinite line 'line1' closest to segment 'line0' at t0
            // using t1 = Dot3(pt0 - s1, line1) / line1_length_sq = (b*t0 + f) / line1_length_sq
            t1 = (b * t0 + f) / line1_length_sq;

            // If t1 in [0,1] then done. Otherwise, clamp t1, recompute t0 for the new value
            // of t1 using t0 = Dot3(pt1 - s0, line0) / line0_length_sq = (b*t1 - c) / line0_length_sq
            // and clamped t0 to [0, 1]
            if (t1 < 0.0f)
            {
                t1 = 0.0f; t0 = Math_.Clamp((-c) / line0_length_sq, 0.0f, 1.0f);
            }
            else if (t1 > 1.0f)
            {
                t1 = 1.0f; t0 = Math_.Clamp((b - c) / line0_length_sq, 0.0f, 1.0f);
            }
        }
예제 #17
0
        /// <summary>
        /// Make an orientation matrix from a direction. Note the rotation around the direction
        /// vector is not defined. 'axis' is the axis that 'direction' will become.</summary>
        public static m3x4 OriFromDir(AxisId axis, v4 dir, v4?up = null)
        {
            // Get the preferred up direction (handling parallel cases)
            var up_ = up == null || Parallel(up.Value, dir) ? Perpendicular(dir) : up.Value;

            var ori = m3x4.Identity;

            ori.z = Normalise(Sign(axis) * dir);
            ori.x = Normalise(Cross(up_, ori.z));
            ori.y = Cross(ori.z, ori.x);

            // Permute the column vectors so +Z becomes 'axis'
            return(PermuteRotation(ori, Math.Abs(axis)));
        }
예제 #18
0
        /// <summary>Construct a quaternion representing a rotation from 'from' to 'to'</summary>
        public quat(v4 from, v4 to)
            : this()
        {
            var d    = Math_.Dot(from.xyz, to.xyz);
            var s    = (float)Math.Sqrt(from.xyz.LengthSq * to.xyz.LengthSq) + d;
            var axis = Math_.Cross(from, to);

            // vectors are 180 degrees apart
            if (Math_.FEql(s, 0))
            {
                axis = Math_.Perpendicular(to);
                s    = 0.0f;
            }

            xyzw = Math_.Normalise(new v4(axis.x, axis.y, axis.z, s));
        }
예제 #19
0
        /// <summary>
        /// This overload attempts to find the nearest point robustly
        /// by testing 3 starting points and returning minimum.</summary>
        public static float ClosestPoint(Spline spline, v4 pt, bool bound01 = true)
        {
            float t0 = ClosestPoint(spline, pt, 0.0f, bound01, 5);
            float t1 = ClosestPoint(spline, pt, 0.5f, bound01, 5);
            float t2 = ClosestPoint(spline, pt, 1.0f, bound01, 5);
            float d0 = (pt - spline.Position(t0)).LengthSq;
            float d1 = (pt - spline.Position(t1)).LengthSq;
            float d2 = (pt - spline.Position(t2)).LengthSq;

            if (d0 < d1 && d0 < d2)
            {
                return(t0);
            }
            if (d1 < d0 && d1 < d2)
            {
                return(t1);
            }
            return(t2);
        }
예제 #20
0
        /// <summary>Create a quadratic from 3 points</summary>
        public static Quadratic FromPoints(v2 a, v2 b, v2 c)
        {
            //' Aa.x2 + Ba.x + C = a.y
            //' Ab.x2 + Bb.x + C = a.y
            //' Ac.x2 + Bc.x + C = a.y
            //' => Ax = y
            //' A = |a.x² a.x 1| x = |A| y = |a.y|
            //'     |b.x² b.x 1|     |B|     |b.y|
            //'     |c.x² c.x 1|     |C|     |c.y|
            var M = Math_.Transpose(new m3x4(
                                        new v4(a.x * a.x, a.x, 1, 0),
                                        new v4(b.x * b.x, b.x, 1, 0),
                                        new v4(c.x * c.x, c.x, 1, 0)));

            var y = new v4(a.y, b.y, c.y, 0);
            var x = Math_.Invert(M) * y;

            return(new Quadratic(x.x, x.y, x.z));
        }
예제 #21
0
        // Create a cubic from 4 points
        public static Cubic FromPoints(v2 a, v2 b, v2 c, v2 d)
        {
            //' Aa.x³ + Ba.x² + Ca.x + D = a.y
            //' Ab.x³ + Bb.x² + Cb.x + D = a.y
            //' Ac.x³ + Bc.x² + Cc.x + D = a.y
            //' Ad.x³ + Bd.x² + Cd.x + D = a.y
            //' => Ax = y
            //' A = |a.x² a.x 1| x = |A| y = |a.y|
            //'     |b.x² b.x 1|     |B|     |b.y|
            //'     |c.x² c.x 1|     |C|     |c.y|
            var M = Math_.Transpose(new m4x4(
                                        new v4(a.x * a.x * a.x, a.x * a.x, a.x, 1),
                                        new v4(b.x * b.x * b.x, b.x * b.x, b.x, 1),
                                        new v4(c.x * c.x * c.x, c.x * c.x, c.x, 1),
                                        new v4(d.x * d.x * d.x, d.x * d.x, d.x, 1)));

            var y = new v4(a.y, b.y, c.y, d.y);
            var x = Math_.Invert(M) * y;

            return(new Cubic(x.x, x.y, x.z, x.w));
        }
예제 #22
0
        /// <summary>Create a transform representing the rotation from one vector to another. 'from' and 'to' do not have to be normalised</summary>
        public static m3x4 Rotation(v4 from, v4 to)
        {
            Debug.Assert(!Math_.FEql(from.Length, 0));
            Debug.Assert(!Math_.FEql(to.Length, 0));
            var len = from.Length * to.Length;

            // Find the cosine of the angle between the vectors
            var cos_angle = Math_.Dot(from, to) / len;

            if (cos_angle >= 1f - Math_.TinyF)
            {
                return(Identity);
            }
            if (cos_angle <= Math_.TinyF - 1f)
            {
                return(Rotation(Math_.Normalise(Math_.Perpendicular(from - to)), (float)Math_.TauBy2));
            }

            // Axis multiplied by sine of the angle
            var axis_sine_angle = Math_.Cross(from, to) / len;
            var axis_norm       = Math_.Normalise(axis_sine_angle);

            return(Rotation(axis_norm, axis_sine_angle, cos_angle));
        }
예제 #23
0
 public static v4 DegreesToRadians(v4 degrees)
 {
     return(new v4(DegreesToRadians(degrees.x), DegreesToRadians(degrees.y), DegreesToRadians(degrees.z), DegreesToRadians(degrees.w)));
 }
예제 #24
0
 public quat(v4 vec)
     : this()
 {
     this.xyzw = vec;
 }
예제 #25
0
 /// <summary>Reinterpret a vector as a quaternion</summary>
 public static quat From(v4 vec)
 {
     return(new quat(vec.x, vec.y, vec.z, vec.w));
 }
예제 #26
0
 /// <summary>Create a random 3D rotation matrix</summary>
 public static m3x4 Random(v4 axis, float min_angle, float max_angle, Random r)
 {
     return(Rotation(axis, r.Float(min_angle, max_angle)));
 }
예제 #27
0
 public m3x4(v4 x, v4 y, v4 z) : this()
 {
     this.x = x;
     this.y = y;
     this.z = z;
 }
예제 #28
0
 /// <summary>Create from an axis and angle. 'axis' should be normalised</summary>
 public static m3x4 Rotation(v4 axis_norm, float angle)
 {
     Debug.Assert(Math_.FEql(axis_norm.LengthSq, 1f), "'axis_norm' should be normalised");
     return(Rotation(axis_norm, axis_norm * (float)Math.Sin(angle), (float)Math.Cos(angle)));
 }
예제 #29
0
 /// <summary>
 /// Return the cross product matrix for 'vec'. This matrix can be used to take the cross
 /// product of another vector: e.g. Cross(v1, v2) == CrossProductMatrix4x4(v1) * v2</summary>
 public static m4x4 CPM(v4 vec, v4 pos)
 {
     return(new m4x4(CPM(vec), pos));
 }
예제 #30
0
 public static v4 RadiansToDegrees(v4 degrees)
 {
     return(new v4(RadiansToDegrees(degrees.x), RadiansToDegrees(degrees.y), RadiansToDegrees(degrees.z), RadiansToDegrees(degrees.w)));
 }