Looks like just about everyone else on StackOverflow has contributed an answer (23 answers so far), so here's my contribution for C#. This is mostly based on the answer by M. Katz, which in turn is based on the answer by Grumdrig.
public struct MyVector { private readonly double _x, _y; // Constructor public MyVector(double x, double y) { _x = x; _y = y; } // Distance from this point to another point, squared private double DistanceSquared(MyVector otherPoint) { double dx = otherPoint._x - this._x; double dy = otherPoint._y - this._y; return dx * dx + dy * dy; } // Find the distance from this point to a line segment (which is not the same as from this // point to anywhere on an infinite line). Also returns the closest point. public double DistanceToLineSegment(MyVector lineSegmentPoint1, MyVector lineSegmentPoint2, out MyVector closestPoint) { return Math.Sqrt(DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, out closestPoint)); } // Same as above, but avoid using Sqrt(), saves a new nanoseconds in cases where you only want // to compare several distances to find the smallest or largest, but don't need the distance public double DistanceToLineSegmentSquared(MyVector lineSegmentPoint1, MyVector lineSegmentPoint2, out MyVector closestPoint) { // Compute length of line segment (squared) and handle special case of coincident points double segmentLengthSquared = lineSegmentPoint1.DistanceSquared(lineSegmentPoint2); if (segmentLengthSquared < 1E-7f) // Arbitrary "close enough for government work" value { closestPoint = lineSegmentPoint1; return this.DistanceSquared(closestPoint); } // Use the magic formula to compute the "projection" of this point on the infinite line MyVector lineSegment = lineSegmentPoint2 - lineSegmentPoint1; double t = (this - lineSegmentPoint1).DotProduct(lineSegment) / segmentLengthSquared; // Handle the two cases where the projection is not on the line segment, and the case where // the projection is on the segment if (t <= 0) closestPoint = lineSegmentPoint1; else if (t >= 1) closestPoint = lineSegmentPoint2; else closestPoint = lineSegmentPoint1 + (lineSegment * t); return this.DistanceSquared(closestPoint); } public double DotProduct(MyVector otherVector) { return this._x * otherVector._x + this._y * otherVector._y; } public static MyVector operator +(MyVector leftVector, MyVector rightVector) { return new MyVector(leftVector._x + rightVector._x, leftVector._y + rightVector._y); } public static MyVector operator -(MyVector leftVector, MyVector rightVector) { return new MyVector(leftVector._x - rightVector._x, leftVector._y - rightVector._y); } public static MyVector operator *(MyVector aVector, double aScalar) { return new MyVector(aVector._x * aScalar, aVector._y * aScalar); } // Added using ReSharper due to CodeAnalysis nagging public bool Equals(MyVector other) { return _x.Equals(other._x) && _y.Equals(other._y); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; return obj is MyVector && Equals((MyVector) obj); } public override int GetHashCode() { unchecked { return (_x.GetHashCode()*397) ^ _y.GetHashCode(); } } public static bool operator ==(MyVector left, MyVector right) { return left.Equals(right); } public static bool operator !=(MyVector left, MyVector right) { return !left.Equals(right); } }
And here's a little test program.
public static class JustTesting { public static void Main() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 10000000; i++) { TestIt(1, 0, 0, 0, 1, 1, 0.70710678118654757); TestIt(5, 4, 0, 0, 20, 10, 1.3416407864998738); TestIt(30, 15, 0, 0, 20, 10, 11.180339887498949); TestIt(-30, 15, 0, 0, 20, 10, 33.541019662496844); TestIt(5, 1, 0, 0, 10, 0, 1.0); TestIt(1, 5, 0, 0, 0, 10, 1.0); } stopwatch.Stop(); TimeSpan timeSpan = stopwatch.Elapsed; } private static void TestIt(float aPointX, float aPointY, float lineSegmentPoint1X, float lineSegmentPoint1Y, float lineSegmentPoint2X, float lineSegmentPoint2Y, double expectedAnswer) { // Katz double d1 = DistanceFromPointToLineSegment(new MyVector(aPointX, aPointY), new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y)); Debug.Assert(d1 == expectedAnswer); /* // Katz using squared distance double d2 = DistanceFromPointToLineSegmentSquared(new MyVector(aPointX, aPointY), new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y)); Debug.Assert(Math.Abs(d2 - expectedAnswer * expectedAnswer) < 1E-7f); */ /* // Matti (optimized) double d3 = FloatVector.DistanceToLineSegment(new PointF(aPointX, aPointY), new PointF(lineSegmentPoint1X, lineSegmentPoint1Y), new PointF(lineSegmentPoint2X, lineSegmentPoint2Y)); Debug.Assert(Math.Abs(d3 - expectedAnswer) < 1E-7f); */ } private static double DistanceFromPointToLineSegment(MyVector aPoint, MyVector lineSegmentPoint1, MyVector lineSegmentPoint2) { MyVector closestPoint; // Not used return aPoint.DistanceToLineSegment(lineSegmentPoint1, lineSegmentPoint2, out closestPoint); } private static double DistanceFromPointToLineSegmentSquared(MyVector aPoint, MyVector lineSegmentPoint1, MyVector lineSegmentPoint2) { MyVector closestPoint; // Not used return aPoint.DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, out closestPoint); } }
As you can see, I tried to measure the difference between using the version that avoids the Sqrt() method and the normal version. My tests indicate you can maybe save about 2.5%, but I'm not even sure of that - the variations within the various test runs were of the same order of magnitude. I also tried measuring the version posted by Matti (plus an obvious optimization), and that version seems to be about 4% slower than the version based on Katz/Grumdrig code.
Edit: Incidentally, I've also tried measuring a method that finds the distance to an infinite line (not a line segment) using a cross product (and a Sqrt()), and it's about 32% faster.