Example #1
0
        /// <summary>
        /// Finds the intersection of the line segment defined by <paramref name="linePoint1"/> and
        /// <paramref name="linePoint2"/> with a plane described by it's normal (<paramref name="planeNormal"/>)
        /// and an arbitrary point in the plane (<paramref name="pointInPlane"/>).
        /// </summary>
        /// <param name="planeNormal">The normal vector of an arbitrary plane.</param>
        /// <param name="pointInPlane">A point in space that lies on the plane whose normal is <paramref name="planeNormal"/>.</param>
        /// <param name="linePoint1">The position vector of the start of the line.</param>
        /// <param name="linePoint2">The position vector of the end of the line.</param>
        /// <param name="isLineSegment">Specifies whether <paramref name="linePoint1"/> and <paramref name="linePoint2"/>
        /// define a line segment, or simply 2 points on an infinite line.</param>
        /// <returns>A position vector describing the point of intersection of the line with the plane, or null if the
        /// line and plane do not intersect.</returns>
        public static Vector3D GetLinePlaneIntersection(
            Vector3D planeNormal,
            Vector3D pointInPlane,
            Vector3D linePoint1,
            Vector3D linePoint2,
            bool isLineSegment)
        {
            if (Vector3D.AreEqual(planeNormal, Vector3D.Null))
            {
                return(null);
            }

            Vector3D line             = linePoint2 - linePoint1;
            Vector3D planeToLineStart = pointInPlane - linePoint1;

            float lineDotPlaneNormal = planeNormal.Dot(line);

            if (FloatComparer.AreEqual(0F, lineDotPlaneNormal))
            {
                return(null);
            }

            float ratio = planeNormal.Dot(planeToLineStart) / lineDotPlaneNormal;

            if (isLineSegment && (ratio < 0F || ratio > 1F))
            {
                return(null);
            }

            return(linePoint1 + ratio * line);
        }
Example #2
0
        /// <summary>
        /// Determines whether or not two lines are colinear.
        /// </summary>
        /// <remarks>
        /// Colinearity here is defined as whether or not you can draw a single line through all given points.
        /// This definition is used because it explicitly provides for classification of degenerate cases
        /// where one or both &quot;lines&quot; are actually coincident points.
        /// </remarks>
        /// <param name="p1">One endpoint of one line.</param>
        /// <param name="p2">The other endpoint of one line.</param>
        /// <param name="q1">One endpoint of the other line.</param>
        /// <param name="q2">The other endpoint of the other line.</param>
        /// <returns>True if the lines are colinear; False otherwise.</returns>
        public static bool AreColinear(PointF p1, PointF p2, PointF q1, PointF q2)
        {
            // colinearity test algorithm:
            // 1. compute vectors from one endpoint to each of the other three endpoints
            // 2. if all three vectors are trivial (i.e. zero) then all endpoints are coincident and thus "colinear"
            // 3. otherwise they are colinear iff the non-trivial vector is (anti-)parallel to the other two vectors (as they have a common point)
            // to test for parallel vectors while ignoring direction, compute the dot product between one vector and a perpendicular to the other vector.

            // compute the vectors from P1 to each of P2, Q1 and Q2
            var vector0 = p1 - new SizeF(p2);
            var vector1 = p1 - new SizeF(q1);
            var vector2 = p1 - new SizeF(q2);

            // make sure we have the non-trivial vector in vector0
            if (FloatComparer.AreEqual(PointF.Empty, vector0))
            {
                if (FloatComparer.AreEqual(PointF.Empty, vector1))
                {
                    // if both P1P2 and P1Q1 are trivial, then we have at most two distinct points, through which you can always draw a line!
                    return(true);
                }
                else
                {
                    // vector1 is non-trivial and vector0 is trivial, so swap them
                    var temp = vector0;
                    vector0 = vector1;
                    vector1 = temp;
                }
            }

            // lines are colinear iff the other two vectors are parallel to the non-trivial vector
            return(FloatComparer.AreEqual(vector0.Y * vector1.X - vector0.X * vector1.Y, 0) && FloatComparer.AreEqual(vector0.Y * vector2.X - vector0.X * vector2.Y, 0));
        }
Example #3
0
        /// <summary>Used by <see cref="CountWindings(PointF,PointF*,int)"/>.</summary>
        /// <remarks>
        /// Algorithm as given on <a href="http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm">http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm</a>.
        /// </remarks>
        private static int IsLeft(PointF point0, PointF point1, PointF testPoint)
        {
            // this is a dot product of the vector test to p0, with a normal to the vector p0 to p1.
            float result = (point1.X - point0.X) * (testPoint.Y - point0.Y) - (testPoint.X - point0.X) * (point1.Y - point0.Y);

            return(FloatComparer.Compare(result, 0, 1));
        }
Example #4
0
        /// <summary>
        /// Determines whether or not this vector is orthogonal to <paramref name="other"/> within a certain <paramref name="angleTolerance"/>.
        /// </summary>
        public bool IsOrthogonalTo(Vector3D other, float angleToleranceRadians)
        {
            angleToleranceRadians = Math.Abs(angleToleranceRadians);
            float       angle  = GetAngleBetween(other);
            const float halfPi = (float)Math.PI / 2;

            return(FloatComparer.AreEqual(angle, halfPi, angleToleranceRadians));
        }
Example #5
0
        /// <summary>
        /// Determines whether or not this vector is parallel to <paramref name="other"/> within a certain <paramref name="angleTolerance"/>.
        /// </summary>
        public bool IsParallelTo(Vector3D other, float angleToleranceRadians)
        {
            angleToleranceRadians = Math.Abs(angleToleranceRadians);
            float angle = GetAngleBetween(other);

            return(FloatComparer.AreEqual(angle, 0, angleToleranceRadians) ||
                   FloatComparer.AreEqual(angle, (float)Math.PI, angleToleranceRadians));
        }
Example #6
0
 //TODO (CR February 2011) - Low: Not used except in unit tests.  Deprecate and recommend using the Vector3D class.
 /// <summary>
 /// Computes the unit vector of the vector defined by <paramref name="vector"/>.
 /// </summary>
 /// <param name="vector">The vector given as a point relative to the origin.</param>
 /// <returns>The unit vector in the same direction as the given vector.</returns>
 /// <exception cref="ArgumentException">If <paramref name="vector"/> is equal to the origin.</exception>
 public static PointF CreateUnitVector(PointF vector)
 {
     if (FloatComparer.AreEqual(PointF.Empty, vector))
     {
         throw new ArgumentException("Argument must specify a valid vector.", "vector");
     }
     return(CreateUnitVector(PointF.Empty, vector));
 }
Example #7
0
        /// <summary>
        /// Gets whether or not <paramref name="left"/> is equal to <paramref name="right"/>, within a small tolerance (per vector component).
        /// </summary>
        public static bool AreEqual(Vector3D left, Vector3D right)
        {
            if (left == null || right == null)
            {
                return(ReferenceEquals(left, right));
            }

            return(FloatComparer.AreEqual(left.X, right.X) &&
                   FloatComparer.AreEqual(left.Y, right.Y) &&
                   FloatComparer.AreEqual(left.Z, right.Z));
        }
Example #8
0
        /// <summary>
        /// Computes the unit vector of the vector defined by <paramref name="startingPoint"/> to <paramref name="endingPoint"/>.
        /// </summary>
        /// <param name="startingPoint">The starting point of the vector.</param>
        /// <param name="endingPoint">The ending point of the vector.</param>
        /// <returns>The unit vector in the same direction as the given vector.</returns>
        /// <exception cref="ArgumentException">If <paramref name="startingPoint"/> is equal to <paramref name="endingPoint"/>.</exception>
        public static PointF CreateUnitVector(PointF startingPoint, PointF endingPoint)
        {
            if (FloatComparer.AreEqual(startingPoint, endingPoint))
            {
                throw new ArgumentException("Arguments must specify a valid vector.", "endingPoint");
            }
            float  deltaX    = endingPoint.X - startingPoint.X;
            float  deltaY    = endingPoint.Y - startingPoint.Y;
            double magnitude = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

            return(new PointF((float)(deltaX / magnitude), (float)(deltaY / magnitude)));
        }
 /// <summary>
 /// Returns a value indicating whether the specified rectangle is normalized.
 /// </summary>
 /// <param name="rectangle"></param>
 /// <returns></returns>
 public static bool IsRectangleNormalized(RectangleF rectangle)
 {
     return(!(FloatComparer.IsLessThan(rectangle.Left, 0.0f) ||
              FloatComparer.IsGreaterThan(rectangle.Left, 1.0f) ||
              FloatComparer.IsLessThan(rectangle.Right, 0.0f) ||
              FloatComparer.IsGreaterThan(rectangle.Right, 1.0f) ||
              FloatComparer.IsLessThan(rectangle.Top, 0.0f) ||
              FloatComparer.IsGreaterThan(rectangle.Top, 1.0f) ||
              FloatComparer.IsLessThan(rectangle.Bottom, 0.0f) ||
              FloatComparer.IsGreaterThan(rectangle.Bottom, 1.0f) ||
              FloatComparer.IsGreaterThan(rectangle.Left, rectangle.Right) ||
              FloatComparer.IsGreaterThan(rectangle.Top, rectangle.Bottom)));
 }
Example #10
0
        /// <summary>
        /// Computes the intersection between two line segments, if a solution exists.
        /// </summary>
        /// <param name="p1">One endpoint of one line segment.</param>
        /// <param name="p2">The other endpoint of one line segment.</param>
        /// <param name="q1">One endpoint of the other line segment.</param>
        /// <param name="q2">The other endpoint of the other line segment.</param>
        /// <param name="intersection">The intersection between the two line segments, if a solution exists.</param>
        /// <returns>True if the intersection exists; False otherwise.</returns>

        //TODO (CR February 2011) - High (SDK release): Name? GetLineSegmentIntersection : Nullable<Point>
        public static bool IntersectLineSegments(PointF p1, PointF p2, PointF q1, PointF q2, out PointF intersection)
        {
            // find the solution to the line equations in matrix form
            // P1 + s(P2-P1) = Q1 + t(Q2-Q1)
            // => P1 + s(P2-P1) = Q1 - t(Q1-Q2)
            // => [P2-P1 Q1-Q2] * [s t]^T = Q1-P1
            // => [s t]^T = [P2-P1 Q1-Q2]^-1 * [Q1-P1]

            // use double precision floating point variables to minimize precision loss

            // compute elements of the matrix M
            double m11 = p2.X - p1.X;             // M[R1C1]
            double m12 = q1.X - q2.X;             // M[R1C2]
            double m21 = p2.Y - p1.Y;             // M[R2C1]
            double m22 = q1.Y - q2.Y;             // M[R2C2]

            // compute determinant of the matrix M
            double determinant = m11 * m22 - m12 * m21;         // det(M)

            if (!FloatComparer.AreEqual(determinant, 0))
            {
                // compute elements of the inverted matrix M^-1
                double v11 = m22 / determinant;
                double v12 = -m12 / determinant;
                double v21 = -m21 / determinant;
                double v22 = m11 / determinant;

                // compute elements of the RHS vector
                double r1 = q1.X - p1.X;
                double r2 = q1.Y - p1.Y;

                // left-multiply inverted matrix with RHS to get solution of {s,t}
                double s = v11 * r1 + v12 * r2;
                double t = v21 * r1 + v22 * r2;

                //TODO (CR February 2011) - Medium (SDK release): tolerance seems arbitrary.  Should add an overload that accepts the tolerance.

                // the solution {s,t} represents the intersection of the lines
                // for line segments, we must therefore further restrict the valid range of {s,t} to [0,1]
                const int tolerance = 100000;                 // allow additional tolerance due to amount of floating-point computation
                if (FloatComparer.Compare(s, 0, tolerance) >= 0 && FloatComparer.Compare(s, 1, tolerance) <= 0 &&
                    FloatComparer.Compare(t, 0, tolerance) >= 0 && FloatComparer.Compare(t, 1, tolerance) <= 0)
                {
                    intersection = new PointF((float)(p1.X + s * m11), (float)(p1.Y + s * m21));
                    return(true);
                }
            }

            intersection = PointF.Empty;
            return(false);
        }
Example #11
0
		/// <summary>
		/// Gets a value indicating whether or not the elements of <paramref name="left"/> are equal to <paramref name="right"/> within a small tolerance.
		/// </summary>
		/// <exception cref="ArgumentException">If the matrices do not have the same dimensions.</exception>
		public static bool AreEqual(Matrix left, Matrix right)
		{
			Platform.CheckTrue(left.Columns == right.Columns && left.Rows == right.Rows, "Matrix Same Dimensions");

			for (int row = 0; row < left.Rows; ++row)
			{
				for (int column = 0; column < left.Columns; ++column)
				{
					if (!FloatComparer.AreEqual(left[row, column], right[row, column]))
						return false;
				}
			}

			return true;
		}
Example #12
0
        /// <summary>
        /// Gets a value indicating whether or not the elements of <paramref name="left"/> are equal to <paramref name="right"/> within the given absolute tolerance.
        /// </summary>
        /// <exception cref="ArgumentException">If the matrices do not have the same dimensions.</exception>
        public static bool AreEqual(Matrix3D left, Matrix3D right, float tolerance)
        {
            for (int row = 0; row < 3; ++row)
            {
                for (int column = 0; column < 3; ++column)
                {
                    if (!FloatComparer.AreEqual(left[row, column], right[row, column], tolerance))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
Example #13
0
        /// <summary>
        /// Computes the intersection between two lines, if a solution exists.
        /// </summary>
        /// <param name="p1">One endpoint of one line.</param>
        /// <param name="p2">The other endpoint of one line.</param>
        /// <param name="q1">One endpoint of the other line.</param>
        /// <param name="q2">The other endpoint of the other line.</param>
        /// <param name="intersection">The intersection between the two lines, if a solution exists.</param>
        /// <returns>True if the intersection exists and is distinct; False otherwise.</returns>

        //TODO (CR February 2011) - High (SDK release): Name? GetLineIntersection : Nullable<Point>
        public static bool IntersectLines(PointF p1, PointF p2, PointF q1, PointF q2, out PointF intersection)
        {
            //TODO (CR February 2011) - Medium (SDK release): I remember talking about it, but looking at these 2 very similar
            //methods, I think it makes sense to combine them.

            // find the solution to the line equations in matrix form
            // P1 + s(P2-P1) = Q1 + t(Q2-Q1)
            // => P1 + s(P2-P1) = Q1 - t(Q1-Q2)
            // => [P2-P1 Q1-Q2] * [s t]^T = Q1-P1
            // => [s t]^T = [P2-P1 Q1-Q2]^-1 * [Q1-P1]

            // use double precision floating point variables to minimize precision loss

            // compute elements of the matrix M
            double m11 = p2.X - p1.X;             // M[R1C1]
            double m12 = q1.X - q2.X;             // M[R1C2]
            double m21 = p2.Y - p1.Y;             // M[R2C1]
            double m22 = q1.Y - q2.Y;             // M[R2C2]

            // compute determinant of the matrix M
            double determinant = m11 * m22 - m12 * m21;         // det(M)

            if (!FloatComparer.AreEqual(determinant, 0))
            {
                // compute elements of the inverted matrix M^-1
                double v11 = m22 / determinant;
                double v12 = -m12 / determinant;
                // double v21 = -m21/determinant;
                // double v22 = m11/determinant;

                // compute elements of the RHS vector
                double r1 = q1.X - p1.X;
                double r2 = q1.Y - p1.Y;

                // left-multiply inverted matrix with RHS to get solution of {s,t}
                double s = v11 * r1 + v12 * r2;
                // double t = v21*r1 + v22*r2;

                // the solution {s,t} represents the intersection of the lines
                intersection = new PointF((float)(p1.X + s * m11), (float)(p1.Y + s * m21));
                return(true);
            }

            intersection = PointF.Empty;
            return(false);
        }
Example #14
0
        /// <summary>
        /// Determines whether or not two lines are parallel.
        /// </summary>
        /// <param name="p1">One endpoint of one line.</param>
        /// <param name="p2">The other endpoint of one line.</param>
        /// <param name="q1">One endpoint of the other line.</param>
        /// <param name="q2">The other endpoint of the other line.</param>
        /// <returns>True if the lines are parallel; False otherwise.</returns>
        public static bool AreParallel(PointF p1, PointF p2, PointF q1, PointF q2)
        {
            //TODO (CR February 2011) - Medium (SDK release): same determinant computed in 3 places; should make it a method (ComputeDeterminant?)
            // find the solution to the line equations in matrix form
            // P1 + s(P2-P1) = Q1 + t(Q2-Q1)
            // => P1 + s(P2-P1) = Q1 - t(Q1-Q2)
            // => [P2-P1 Q1-Q2] * [s t]^T = Q1-P1
            // => [s t]^T = [P2-P1 Q1-Q2]^-1 * [Q1-P1]

            // use double precision floating point variables to minimize precision loss

            // compute elements of the matrix M
            double m11 = p2.X - p1.X;             // M[R1C1]
            double m12 = q1.X - q2.X;             // M[R1C2]
            double m21 = p2.Y - p1.Y;             // M[R2C1]
            double m22 = q1.Y - q2.Y;             // M[R2C2]

            // compute determinant of the matrix M
            double determinant = m11 * m22 - m12 * m21;         // det(M)

            // if a distinct solution does not exist then the lines must be parallel
            return(FloatComparer.AreEqual(determinant, 0));
        }
Example #15
0
        /// <summary>
        /// Calculates the angle subtended by two line segments that meet at a vertex.
        /// </summary>
        /// <param name="start">The end of one of the line segments.</param>
        /// <param name="vertex">The vertex of the angle formed by the two line segments.</param>
        /// <param name="end">The end of the other line segment.</param>
        /// <returns>The angle subtended by the two line segments in degrees.</returns>
        public static double SubtendedAngle(PointF start, PointF vertex, PointF end)
        {
            Vector3D vertexPositionVector = new Vector3D(vertex.X, vertex.Y, 0);
            Vector3D a = new Vector3D(start.X, start.Y, 0) - vertexPositionVector;
            Vector3D b = new Vector3D(end.X, end.Y, 0) - vertexPositionVector;

            float dotProduct = a.Dot(b);

            Vector3D crossProduct = a.Cross(b);

            float magA = a.Magnitude;
            float magB = b.Magnitude;

            if (FloatComparer.AreEqual(magA, 0F) || FloatComparer.AreEqual(magB, 0F))
            {
                return(0);
            }

            double cosTheta = dotProduct / magA / magB;

            // Make sure cosTheta is within bounds so we don't
            // get any errors when we take the acos.
            if (cosTheta > 1.0f)
            {
                cosTheta = 1.0f;
            }

            if (cosTheta < -1.0f)
            {
                cosTheta = -1.0f;
            }

            double theta          = Math.Acos(cosTheta) * (crossProduct.Z == 0 ? 1 : -Math.Sign(crossProduct.Z));
            double thetaInDegrees = theta / Math.PI * 180;

            return(thetaInDegrees);
        }