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

Line-line intersection edge cases #3396

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 18 additions & 3 deletions Lib/fontTools/misc/bezierTools.py
Expand Up @@ -1171,6 +1171,10 @@
e1x, e1y = e1
s2x, s2y = s2
e2x, e2y = e2

def closepoints(pt1, pt2):
return math.isclose(pt1[0], pt2[0]) and math.isclose(pt1[1], pt2[1])

if (
math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
): # Parallel vertical
Expand All @@ -1179,11 +1183,20 @@
math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
): # Parallel horizontal
return []
if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
return []
if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
if closepoints(s2, e2) or closepoints(s1, e1): # Line segment is tiny
return []
if closepoints(s1, s2): # Coincident at starts
return [Intersection(pt=s1, t1=0, t2=0)]
if closepoints(e1, e2): # Coincident at ends
return [Intersection(pt=e1, t1=1, t2=1)]

Check warning on line 1191 in Lib/fontTools/misc/bezierTools.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/misc/bezierTools.py#L1191

Added line #L1191 was not covered by tests
if closepoints(s1, e2):
return [Intersection(pt=s1, t1=0, t2=1)]

Check warning on line 1193 in Lib/fontTools/misc/bezierTools.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/misc/bezierTools.py#L1193

Added line #L1193 was not covered by tests
if closepoints(s2, e1):
return [Intersection(pt=s2, t1=1, t2=0)]

if math.isclose(e1x, s1x):
if math.isclose(e2x, s2x):
return []
x = s1x
slope34 = (e2y - s2y) / (e2x - s2x)
y = slope34 * (x - s2x) + s2y
Expand All @@ -1194,6 +1207,8 @@
)
]
if math.isclose(s2x, e2x):
if math.isclose(s1x, e1x):
return []

Check warning on line 1211 in Lib/fontTools/misc/bezierTools.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/misc/bezierTools.py#L1211

Added line #L1211 was not covered by tests
x = s2x
slope12 = (e1y - s1y) / (e1x - s1x)
y = slope12 * (x - s1x) + s1y
Expand Down
14 changes: 14 additions & 0 deletions Tests/misc/bezierTools_test.py
Expand Up @@ -5,6 +5,7 @@
calcCubicBounds,
curveLineIntersections,
curveCurveIntersections,
lineLineIntersections,
segmentPointAtT,
splitLine,
splitQuadratic,
Expand Down Expand Up @@ -197,3 +198,16 @@ def test_intersections_linelike():
seg2 = [(0.0, 0.5), (0.25, 0.5), (0.75, 0.5), (1.0, 0.5)]
pt = curveCurveIntersections(seg1, seg2)[0][0]
assert pt == (0.0, 0.5)


def test_intersections_samestart():
seg1 = [(250, 1000), (250, 810)]
seg2 = [(250, 1000), (250, 400)]
pt = lineLineIntersections(*seg1, *seg2)[0][0]
assert pt == (250.0, 1000.0)
# E1 == S2
seg3 = [(250, 810), (250, 500)]
pt = lineLineIntersections(*seg1, *seg3)[0][0]
assert pt == (250.0, 810.0)
# Entirely contained, give up
assert lineLineIntersections(*seg2, *seg3) == []