dhermes / bezier

Helper for Bézier Curves, Triangles, and Higher Order Objects
Apache License 2.0
266 stars 36 forks source link

Curve-curve intersection fails on "almost" degree-elevated lines #17

Closed dhermes closed 7 years ago

dhermes commented 7 years ago
>>> nodes1 = [
...     ['-0x1.0000000000000p+0', '0x1.b6db6db6db6d8p-2'],
...     ['-0x1.0000000000000p+0', '0x1.2492492492490p-2'],
...     ['-0x1.0000000000000p+0', '0x1.2492492492490p-3'],
... ]
>>> nodes2 = [
...     ['-0x1.1000000000000p+0', '0x1.d249249249246p-2'],
...     ['-0x1.1000000000000p+0', '0x1.36db6db6db6d9p-2'],
...     ['-0x1.1000000000000p+0', '0x1.36db6db6db6d9p-3'],
... ]
>>> F = float.fromhex
>>> nodes1 = [[F(val) for val in row] for row in nodes1]
>>> nodes2 = [[F(val) for val in row] for row in nodes2]
>>> import numpy as np
>>> nodes1 = np.asfortranarray(nodes1)
>>> nodes2 = np.asfortranarray(nodes2)
>>> import bezier
>>> C1 = bezier.Curve(nodes1, degree=2, _copy=False)
>>> C2 = bezier.Curve(nodes2, degree=2, _copy=False)
>>> import seaborn
>>> ax = C1.plot(256)
>>> _ = C2.plot(256, ax=ax)
>>> _ = ax.axis('scaled')
>>> _ = ax.set_xlim(-1.125, -0.875)
>>> ax.figure.show()
>>> C1.intersect(C2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "bezier/curve.py", line 583, in intersect
    intersections = _intersection_helpers.all_intersections(candidates)
  File "bezier/_intersection_helpers.py", line 1433, in all_intersections
    accepted = intersect_one_round(candidates, intersections)
  File "bezier/_intersection_helpers.py", line 1353, in intersect_one_round
    from_linearized(first, second, intersections)
  File "bezier/_intersection_helpers.py", line 1119, in from_linearized
    second.end_node, orig_second._nodes)
NotImplementedError: Line segments parallel.

figure_1

The issue is that the curves are actually lines but not to machine precision:

>>> from bezier._intersection_helpers import linearization_error
>>> linearization_error(nodes1)
0.0
>>> linearization_error(nodes2)
6.938893903907228e-18

However, if we "project" onto the space of degree-elevated lines (inside the space of quadratics), we see:

>>> projection = np.asfortranarray([
...     [ 2.5, 1.0, -0.5],
...     [ 1.0, 1.0,  1.0],
...     [-0.5, 1.0,  2.5],
... ])
>>> projected1 = projection.dot(nodes1) / 3
>>> projected2 = projection.dot(nodes2) / 3
>>> projected1 - nodes1
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])
>>> projected2 - nodes2
array([[  0.00000000e+00,  -5.55111512e-17],
       [  0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00]])
>>> np.linalg.norm(projected2 - nodes2, ord='fro') / np.linalg.norm(nodes2, ord='fro')
2.8822815212580453e-17

At

$ git log -1 --pretty=%H
09550abcf0efeb239b17f9871ccac93fcd1bcd4a
dhermes commented 7 years ago

Fixed as of ca017b6e8ee6188be85f25ea7756b88bde352ada