# Point-to-LineSegment vs Point-to-Line Distance in RDP Algorithm: Why pybind11-rdp Uses Finite Segments

> Discover why pybind11-rdp uses point-to-line-segment distance over point-to-line distance for accurate polyline simplification and error prevention.

- Repository: [cubao/pybind11-rdp](https://github.com/cubao/pybind11-rdp)
- Tags: deep-dive
- Published: 2026-02-28

---

**The pybind11-rdp library calculates point-to-line-segment distance rather than point-to-line distance to ensure that points beyond the endpoints of a segment are measured against the nearest endpoint, preventing erroneous point retention during polyline simplification.**

The Ramer-Douglas-Peucker (RDP) algorithm is the standard method for reducing the complexity of polylines while preserving shape. In the `cubao/pybind11-rdp` repository, the implementation deliberately deviates from the purely mathematical definition of point-to-line distance, opting instead for point-to-line-segment distance. This design choice fundamentally changes how the algorithm handles outlier points that project beyond the segment endpoints.

## Why Distance Calculation Method Matters in RDP

The RDP algorithm works by recursively dividing a curve. For any segment between points A and B, it finds the point furthest from the straight line connecting A and B. If this distance exceeds a threshold **epsilon**, the algorithm splits the segment at that point and repeats.

Mathematically, distance can be measured two ways:

- **Point-to-Line (infinite)**: Measures perpendicular distance to the infinite extension of the line. Points projecting beyond the segment endpoints still show small distances if they lie close to the line's path.
- **Point-to-Line-Segment (finite)**: Measures distance to the actual segment. If the perpendicular projection falls outside the segment, it returns the distance to the nearest endpoint.

## How pybind11-rdp Implements Point-to-Line-Segment Distance

### The LineSegment Class in src/main.cpp

The core implementation resides in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp), where the `LineSegment` class encapsulates the finite segment logic. The `distance2` method (lines 28-38) implements the classic point-to-segment distance formula using vector projection and clamping:

```cpp
double distance2(const Eigen::Vector3d &P) const
{
    double dot = (P - A).dot(AB);
    if (dot <= 0) {
        return (P - A).squaredNorm();          // before A → use endpoint A
    } else if (dot >= len2) {
        return (P - B).squaredNorm();          // after B → use endpoint B
    }
    // projection lies inside the segment
    return (A + (dot * inv_len2 * AB) - P).squaredNorm();
}

```

The method first projects the point onto the line using the dot product. If the projection falls before point A (`dot <= 0`) or after point B (`dot >= len2`), it returns the squared distance to the respective endpoint. Only when the projection lies within the segment bounds does it compute the perpendicular distance.

### Python Wrapper Warnings in src/pybind11_rdp/__init__.py

The Python layer explicitly warns users about this design choice. In [`src/pybind11_rdp/__init__.py`](https://github.com/cubao/pybind11-rdp/blob/main/src/pybind11_rdp/__init__.py) (lines 11-22), the initialization code prints:

```python
print(
    "we don't support dist function, the only built-in dist function is dist(point,line_segment) (NOT dist(point,line))",
    file=sys.stderr,
)

```

This ensures users understand that the library deliberately does not support infinite line distance calculations.

## Comparing Point-to-Line vs Point-to-Line-Segment Distance

| Aspect | Point-to-Line (infinite) | Point-to-Line-Segment (finite) |
|--------|---------------------------|--------------------------------|
| **Geometric meaning** | Distance to the *extension* of the segment; points far beyond the ends may appear "close". | True perpendicular distance **only while the projection falls inside the segment**, otherwise distance to the nearest endpoint. |
| **Effect on simplification** | Can erroneously keep points that are far outside the actual segment because their projection lies on the infinite line. | Guarantees that a point is considered "far enough" only when it truly deviates from the *actual* segment, leading to more faithful shape preservation. |
| **Numerical stability** | Requires extra handling of extreme cases (e.g., vertical/horizontal lines). | Handles all cases uniformly via dot-product checks (`dot <= 0`, `dot >= len2`). |
| **Implementation in this repo** | Not used – deliberately omitted. | Used throughout the recursive (`douglas_simplify`) and iterative (`douglas_simplify_iter`) passes. |

The library’s design choice prevents the algorithm from mistakenly discarding points that lie outside the original segment’s span, which would otherwise distort the simplified polyline.

## Practical Code Examples

### Basic RDP Usage with Default Segment Distance

The `rdp` function automatically uses point-to-line-segment distance:

```python
from pybind11_rdp import rdp

pts = [
    [0, 0],
    [1, 0.1],
    [2, -0.1],
    [3, 5],      # a clear outlier

    [4, 0],
    [5, 0],
]

# epsilon = 0.5 keeps the outlier, larger epsilon removes it

simplified = rdp(pts, epsilon=0.5)
print(simplified)

# → [[0, 0], [3, 5], [5, 0]]

```

### Accessing LineSegment Directly

You can inspect the distance calculation using the `LineSegment` class:

```python
from pybind11_rdp import LineSegment
import numpy as np

seg = LineSegment(np.array([0, 0, 0]), np.array([10, 0, 0]))
p   = np.array([5, 3, 0])          # 3 units above the segment

print(seg.distance(p))            # → 3.0  (point‑to‑segment)

print(seg.distance2(p))           # → 9.0  (squared distance)

```

### Using rdp_mask for Boolean Filtering

The `rdp_mask` function returns a boolean array indicating which points to keep:

```python
from pybind11_rdp import rdp_mask
import numpy as np

coords = np.array([[0, 0], [1, 0.2], [2, -0.2], [3, 5], [4, 0], [5, 0]])
mask = rdp_mask(coords, epsilon=0.5)   # same epsilon as before

print(mask)                           # → [1 0 0 1 0 1]

print(coords[mask.astype(bool)])       # → [[0, 0], [3, 5], [5, 0]]

```

### Demonstrating the Difference with Infinite Line (Conceptual)

This example shows why point-to-line distance would be problematic:

```python
import numpy as np
from pybind11_rdp import LineSegment

seg = LineSegment(np.array([0, 0, 0]), np.array([10, 0, 0]))
p   = np.array([15, 3, 0])   # beyond the segment endpoint

print(seg.distance(p))      # → 5.830951894 (distance to endpoint B)

# If we mistakenly used point-to-line distance, the result would be 3.0,

# which would wrongly suggest the point lies close to the line.

```

## Summary

- **Point-to-line-segment distance** measures deviation from the actual finite segment, falling back to endpoint distance when projections fall outside the segment bounds.
- **Point-to-line distance** measures deviation from an infinite line extension, which can misclassify outlier points beyond the segment endpoints as "close" to the line.
- The `cubao/pybind11-rdp` implementation in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp) explicitly uses the segment-based approach via the `LineSegment.distance2()` method to ensure geometrically correct polyline simplification.
- The Python wrapper in [`src/pybind11_rdp/__init__.py`](https://github.com/cubao/pybind11-rdp/blob/main/src/pybind11_rdp/__init__.py) warns users that only `dist(point, line_segment)` is supported, preventing misuse.

## Frequently Asked Questions

### What is the difference between point-to-line and point-to-line-segment distance in RDP?

Point-to-line distance calculates the perpendicular distance from a point to the infinite extension of a line, regardless of whether the projection falls between the endpoints. Point-to-line-segment distance calculates the perpendicular distance only when the projection falls within the segment bounds; otherwise, it returns the distance to the nearest endpoint. In RDP algorithms, using the infinite line distance can cause the algorithm to retain points that are geometrically far from the actual segment but close to its infinite extension.

### Why does pybind11-rdp use point-to-line-segment distance instead of infinite line distance?

The `cubao/pybind11-rdp` library uses point-to-line-segment distance to ensure that the Ramer-Douglas-Peucker algorithm preserves the true geometric shape of the original polyline. If infinite line distance were used, points located far beyond the endpoints of a segment—but aligned with its direction—would register as having small deviations, causing the algorithm to incorrectly discard critical anchor points. The segment-based approach guarantees that deviation is measured only against the actual portion of the line being considered, as implemented in the `LineSegment::distance2` method in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp).

### How does the point-to-line-segment distance calculation affect polyline simplification results?

Using point-to-line-segment distance produces more conservative and geometrically accurate simplifications. When a point projects beyond the endpoints of the current segment, the algorithm measures its distance to the nearest endpoint rather than to the infinite line. This prevents the algorithm from mistakenly thinking that far-away outliers are "close" to the segment just because they align with its direction. Consequently, the simplified polyline retains the original shape's endpoints and critical inflection points more faithfully than it would if using infinite line distance.

### Can I use point-to-line distance with the pybind11-rdp library?

No, the `pybind11-rdp` library does not support point-to-line (infinite) distance calculations. The Python wrapper explicitly prints a warning on import stating that the only built-in distance function is `dist(point, line_segment)`, not `dist(point, line)`. The underlying C++ implementation in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp) hardcodes the segment-based logic in the `LineSegment` class, and there is no exposed API to switch to infinite line distance. If your application requires infinite line distance, you would need to implement a custom distance metric outside of this library.