diff --git a/src/Math/Geometry/Distance.h b/src/Math/Geometry/Distance.h index 3abb9b78f..e3e710f03 100644 --- a/src/Math/Geometry/Distance.h +++ b/src/Math/Geometry/Distance.h @@ -54,6 +54,64 @@ class Distance { template static T linePointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { return Vector3::cross(point - a, point - b).lengthSquared()/(b - a).lengthSquared(); } + + /** + * @brief %Dístance of point from line segment + * @param a Starting point of the line + * @param b Ending point of the line + * @param point Point + * + * Returns distance of point from line segment or from its + * starting/ending point, depending on where the point lies. + * + * Determining whether the point lies next to line segment or outside + * is done using Pythagorean theorem. If the following equation + * applies, the point **p** lies outside line segment closer to **a**: + * @f[ + * |\boldsymbol p - \boldsymbol b|^2 > |\boldsymbol b - \boldsymbol a|^2 + |\boldsymbol p - \boldsymbol a|^2 + * @f] + * On the other hand, if the following equation applies, the point + * lies outside line segment closer to **b**: + * @f[ + * |\boldsymbol p - \boldsymbol a|^2 > |\boldsymbol b - \boldsymbol a|^2 + |\boldsymbol p - \boldsymbol b|^2 + * @f] + * The last alternative is when the following equation applies. The + * point then lies between **a** and **b** and the distance is + * computed the same way as in linePoint(). + * @f[ + * |\boldsymbol b - \boldsymbol a|^2 > |\boldsymbol p - \boldsymbol a|^2 + |\boldsymbol p - \boldsymbol b|^2 + * @f] + * + * @see lineSegmentPointSquared() + */ + template inline static T lineSegmentPoint(const Vector3& a, const Vector3& b, const Vector3& point) { + return sqrt(lineSegmentPointSquared(a, b, point)); + } + + /** + * @brief %Distance of point from line segment, squared + * + * More efficient than lineSegmentPoint() for comparing distance with + * other values, because it doesn't compute the square root. + */ + template static T lineSegmentPointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { + Vector3 pointMinusA = point - a; + Vector3 pointMinusB = point - b; + T pointDistanceA = pointMinusA.lengthSquared(); + T pointDistanceB = pointMinusB.lengthSquared(); + T bDistanceA = (b - a).lengthSquared(); + + /* Point is before A */ + if(pointDistanceB > bDistanceA + pointDistanceA) + return pointDistanceA; + + /* Point is after B */ + if(pointDistanceA > bDistanceA + pointDistanceB) + return pointDistanceB; + + /* Between A and B */ + return Vector3::cross(pointMinusA, pointMinusB).lengthSquared()/bDistanceA; + } }; }}} diff --git a/src/Math/Geometry/Test/DistanceTest.cpp b/src/Math/Geometry/Test/DistanceTest.cpp index 6311aa0d2..5fff7db48 100644 --- a/src/Math/Geometry/Test/DistanceTest.cpp +++ b/src/Math/Geometry/Test/DistanceTest.cpp @@ -43,4 +43,28 @@ void DistanceTest::linePoint() { Constants::Sqrt2/Constants::Sqrt3); } +void DistanceTest::lineSegmentPoint() { + Vector3 a(0.0f); + Vector3 b(1.0f); + + /* Point on the line segment */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(0.25f))), 0.0f); + + /* Point on the line, outside the segment, closer to A */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(-1.0f))), +Constants::Sqrt3); + + /* Point on the line, outside the segment, closer to B */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(1.0f+1.0f/Constants::Sqrt3))), 1.0f); + + /* Point next to the line segment */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(1.0f, 0.0f, 1.0f))), + Constants::Sqrt2/Constants::Sqrt3); + + /* Point outside the line segment, closer to A */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(1.0f, 0.0f, 1.0f)-Vector3(1.0f))), 1.0f); + + /* Point outside the line segment, closer to B */ + QCOMPARE((Distance::lineSegmentPoint(a, b, Vector3(1.0f, 0.0f, 1.0f)+Vector3(1.0f))), +Constants::Sqrt2); +} + }}}} diff --git a/src/Math/Geometry/Test/DistanceTest.h b/src/Math/Geometry/Test/DistanceTest.h index 13809007f..311c4fc90 100644 --- a/src/Math/Geometry/Test/DistanceTest.h +++ b/src/Math/Geometry/Test/DistanceTest.h @@ -24,6 +24,7 @@ class DistanceTest: public QObject { private slots: void linePoint(); + void lineSegmentPoint(); }; }}}}