-1

I have a very simple method which finds the closest point on a line given a test point. Which is achieved by projecting vectorA on vectorB, such that:

Point testPoint

VectorA = testPoint - Origin VectorB

VectorC = (VectorA * VectorB / |VectorB|^2) VectorB

Like in the following image:

enter image description here

The Problem that I am having is that in some cases the Projected point is not on the line. How can I guarantee in my method such behavior?

        /// <summary>
        /// Returns the dot product of two vectors 
        /// This value equals vecA.Magnitude * vecB.Magnitude * cos(theta), where theta is the angle between both vectors.
        /// </summary>
        /// <param name="vecA"></param>
        /// <param name="VecB"></param>
        /// <returns></returns>
        public static double DotProduct(Vec3 vecA, Vec3 VecB)
        {
            return vecA.X * VecB.X + vecA.Y * VecB.Y + vecA.Z * VecB.Z;
        }


        /// <summary>
        /// Projection of vecA on to vecB
        /// </summary>
        /// <param name="vecA"></param>
        /// <param name="vecB"></param>
        /// <returns></returns>
        public static Vec3 Project(Vec3 vecA, Vec3 vecB)
        {
            return DotProduct(vecA, vecB) / vecB.SqrMagnitude * vecB;
        }



   /// <summary>
    /// Finds the closest point on a vector given a test point
    /// </summary>
    /// <param name="testPoint"></param>
    /// <param name="startVertex"></param>
    /// <param name="segment"></param>
    /// <returns></returns>
  public  static Vec3 VectorClosestPoint(Vec3 testPoint, Vec3 startVertex,Vec3 segment)
        {

            Vec3 b = testPoint - startVertex;
            Vec3 proj = Project(b, segment);
            Vec3 onCurve = startVertex + proj;

            return onCurve;


        }

Any hints would be very helpful

  • What do you mean by the `not on the line`? According to the projection definition that is impossible. So, it is most probably that there is an issue with your projecting function. It would be helpful if you provide inputs for which your method does not work. – qqqqqqq Feb 01 '20 at 23:05
  • From my experience there is no need to decompose the projection in this kind of problem. It is a lot easier to use the equation of a line. Given that you would have a system of two equations. Using the system it is possible to infer a formula which would compute the coordinates of the closest point on line inline (without any decomposed method). Also you would have to use a condition that the two lines are perpendicular. – qqqqqqq Feb 01 '20 at 23:10
  • Useful links: [this](https://www.mathsisfun.com/algebra/line-equation-2points.html) and [this](https://www.mathplanet.com/education/algebra-1/formulating-linear-equations/parallel-and-perpendicular-lines). – qqqqqqq Feb 01 '20 at 23:13
  • A projection is not always on a line, this is true only if the line does not have an infinite domain. So In the image above,if I create a Vector : Proj Point - Test Point, it will be clearly orthogonal to Vector C or to VectorB if it was infinite. But Since VectorB is not infinite, the projected point is not on the line. This is what I mean – Nicholas Rawitscher Feb 01 '20 at 23:21
  • Could you, please, add the definitions of a line and a projection those which are referred to in your question? – qqqqqqq Feb 02 '20 at 13:46
  • @qqqqqqq I solved my own question, when I have time I will post the answer. I just had to specify the domain of the lines, and then test if the projected point was in that domain. – Nicholas Rawitscher Feb 03 '20 at 08:52
  • Your figure is unclear. –  Feb 03 '20 at 15:53
  • 1
    A "line" is infinite. A "line segment" is finite ("does not have an infinite domain" in your speak). If people are confused it could be because your terminology is a little different from what we're used to. If you want shortest distance between a point and a line segment, see this answer: https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment – Wyck Feb 03 '20 at 16:16

2 Answers2

2

Assume that the point is P and the segment is AB.

Subtract A from B and P to translate the segment to the origin. Rotate around the origin to bring OB' on the x axis, giving OB". P' goes to P".

Now, if P''x lies between 0 and B''x, the distance is |P''y|. Otherwise, if P''x<0, the distance is √P''x²+P''y², and if P''x>B''x, the distance is √(P''x-B''x)²+P''y².

enter image description here

0

It was very easy to solve... I just had to specify the domain of the "line segment" and test for containment of the projected point in the domain. This solution will work in either 2D or 3D.

Here is the completed code:

        /// <summary>
        /// Finds the closest point on a vector given a test point
        /// </summary>
        /// <param name="testPoint"></param>
        /// <param name="startVertex"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        public  static Vec3 VectorClosestPoint(Vec3 testPoint, Vec3 startVertex,Vec3 segment)
        {


            Vec3 onCurve = new Vec3();

            Vec3 b = testPoint - startVertex;
            Vec3 proj = Project(b, segment, out double dotProduct);
            Vec3 onCurveTemp = startVertex + proj;
            Vec3 endVertex = startVertex + segment;

            // Specify the domain of the function. 
            // This part constraints the domain if the function is not infinite. 
            Domain domainX = new Domain(startVertex.X, endVertex.X);
            Domain domainY = new Domain(startVertex.Y, endVertex.Y);
            Domain domainZ = new Domain(startVertex.Z, endVertex.Z);


            // Constraints projected points to be in the line, given the specified
            // domain of the function
            bool onLine = OnLine(domainX, domainY, domainZ, onCurveTemp);

            if (dotProduct < 0) onCurve = startVertex;

            if (dotProduct > 0 && onLine == false) onCurve = endVertex;

            if (dotProduct > 0 && onLine == true) onCurve = onCurveTemp;


            return onCurve;


        }



        /// <summary>
        /// Returns the dot product of two vectors 
        /// This value equals vecA.Magnitude * vecB.Magnitude * cos(theta), where theta is the angle between both vectors.
        /// </summary>
        /// <param name="vecA"></param>
        /// <param name="VecB"></param>
        /// <returns></returns>
        public static double DotProduct(Vec3 vecA, Vec3 VecB)
        {
            return vecA.X * VecB.X + vecA.Y * VecB.Y + vecA.Z * VecB.Z;
        }



        /// <summary>
        /// Tests whether a Point lies on a vector
        /// </summary>
        /// <param name="domainX">X- Domain</param>
        /// <param name="domainY"> Y - Domain </param>
        /// <param name="domainZ">Z - Domain</param>
        /// <param name="pt">Point to test for inclusion </param>
        /// <returns>Returns true if point is on the line, false otherwise</returns>
        public static bool OnLine(Domain domainX, Domain domainY, Domain domainZ, Vec3 pt)

        {

            if (Domain.InDomain(domainX.Min, domainX.Max, pt.X) && Domain.InDomain(domainY.Min, domainY.Max, pt.Y) &&
              Domain.InDomain(domainZ.Min, domainZ.Max, pt.Z))
            {
                return true;
            }


            else return false;
        }


        /// <summary>
        /// Projection of vecA on to vecB
        /// </summary>
        /// <param name="vecA"></param>
        /// <param name="vecB"></param>
        /// <returns></returns>
        public static Vec3 Project(Vec3 vecA, Vec3 vecB, out double dotProduct)
        {
            dotProduct = DotProduct(vecA, vecB);
            return dotProduct / vecB.SqrMagnitude * vecB;
            // or to save Sqr operation return dotProduct / DotProduct(vecB, vecB) * vecB;
        }





/// <summary>
/// Tests whether a number is inside a domain
/// </summary>
/// <param name="minVal">Minimum value</param>
/// <param name="maxVal">Maximum value</param>
/// <param name="numToTest">Number to test </param>
/// <returns>True if in domain, false otherwise </returns>
public static bool InDomain(double minVal, double maxVal, double numToTest)
{
    double min = 0;
    double max = 0;
    if (minVal > maxVal)
    {
        min = maxVal;
        max = minVal;
    }

    if (minVal < maxVal)
    {
        min = minVal;
        max = maxVal;
    }


    if (numToTest >= min && numToTest <= max) return true;

    else return false;

}