Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong calculation of pldist #13

Open
flbbronnec opened this issue Oct 2, 2020 · 3 comments
Open

Wrong calculation of pldist #13

flbbronnec opened this issue Oct 2, 2020 · 3 comments

Comments

@flbbronnec
Copy link

flbbronnec commented Oct 2, 2020

The current pldist function returns the distance between a point and the infinte line generated by two other points (i.e the distance to the orthogonal projection), which is incorrect. It should return the distance between a point and a line-segment.

Here is a fix coming from this answer https://stackoverflow.com/a/54442561:

def pldist(point, start, end):
    """
    Calculates the distance from ``point`` to the line given
    by the points ``start`` and ``end``.

    :param point: a point
    :type point: numpy array
    :param start: a point of the line
    :type start: numpy array
    :param end: another point of the line
    :type end: numpy array
    """
    if np.all(start == end)):
        return np.linalg.norm(point - start)

    # normalized tangent vector
    d = np.divide(end - start, np.linalg.norm(end - start))

    # signed parallel distance components
    s = np.dot(start - point, d)
    t = np.dot(point - end, d)

    # clamped parallel distance
    h = np.max([s, t, 0])

    # perpendicular distance component, as before
    c = np.cross(point - start, d)

    # use hypot for Pythagoras to improve accuracy
    return np.hypot(h, c)
anton-petrov added a commit to anton-petrov/rdp that referenced this issue Mar 5, 2022
Source: fhirschmann#13.

The current pldist function returns the distance between a point and the infinte line generated by two other points (i.e the distance to the orthogonal projection), which is incorrect. It should return the distance between a point and a line-segment.
Here is a fix coming from this answer https://stackoverflow.com/a/54442561.
@district10
Copy link

pybind11-rdp handles this pretty well:

https://github.com/cubao/pybind11-rdp/blob/master/src/main.cpp#L26-L37

struct LineSegment
{
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    const Eigen::Vector3d A, B, AB;
    const double len2, inv_len2;
    LineSegment(const Eigen::Vector3d &a, const Eigen::Vector3d &b)
        : A(a), B(b), AB(b - a), //
          len2((b - a).squaredNorm()), inv_len2(1.0 / len2)
    {
    }
    double distance2(const Eigen::Vector3d &P) const
    {
        double dot = (P - A).dot(AB);
        if (dot <= 0) {
            return (P - A).squaredNorm();
        } else if (dot >= len2) {
            return (P - B).squaredNorm();
        }
        // P' = A + dot/length * normed(AB)
        //    = A + dot * AB / (length^2)
        return (A + (dot * inv_len2 * AB) - P).squaredNorm();
    }
    double distance(const Eigen::Vector3d &P) const
    {
        return std::sqrt(distance2(P));
    }
};

Some tests:

TEST(RdpTest, Distance)
{
    auto seg = LineSegment({0.0, 0.0, 0.0}, {10.0, 0.0, 0.0});
    ASSERT_EQ(4.0, dbg(seg.distance({5.0, 4.0, 0.0})));
    ASSERT_EQ(5.0, dbg(seg.distance({-4.0, 3.0, 0.0})));
    ASSERT_EQ(5.0, dbg(seg.distance({14.0, 3.0, 0.0})));
}

TEST(RdpTest, Distance_dup)
{
    auto seg = LineSegment({0.0, 0.0, 0.0}, {0.0, 0.0, 0.0});
    ASSERT_EQ(5.0, dbg(seg.distance({3.0, 4.0, 0.0})));
    ASSERT_EQ(5.0, dbg(seg.distance({-4.0, 3.0, 0.0})));
    ASSERT_EQ(13.0, dbg(seg.distance({5.0, 12.0, 0.0})));
}

@star-plu-cn-sk2
Copy link

star-plu-cn-sk2 commented Nov 1, 2022 via email

@district10
Copy link

As #13 points out, pdist in rdp is WRONGLY Point-to-Line distance. We (pybind11_rdp) use Point-to-LineSegment distance.

from rdp import rdp
print(rdp([[0, 0], [10, 0.1], [1, 0]], epsilon=1.0)) # wrong
# [[0.0, 0.0],
#  [1.0, 0.0]]

from pybind11_rdp import rdp
print(rdp([[0, 0], [10, 0.1], [1, 0]], epsilon=1.0)) # correct
# [[ 0.   0. ]
#  [10.   0.1]
#  [ 1.   0. ]]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants