diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py index 2cf2640c3..41b3eeb39 100644 --- a/Lib/fontTools/misc/bezierTools.py +++ b/Lib/fontTools/misc/bezierTools.py @@ -879,12 +879,14 @@ def _line_t_of_pt(s, e, pt): sx, sy = s ex, ey = e px, py = pt - if not math.isclose(sx, ex): + if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon: + # Line is a point! + return -1 + # Use the largest + if abs(sx - ex) > abs(sy - ex): return (px - sx) / (ex - sx) - if not math.isclose(sy, ey): + else: return (py - sy) / (ey - sy) - # Line is a point! - return -1 def _both_points_are_on_same_side_of_origin(a, b, origin): @@ -1024,7 +1026,11 @@ def curveLineIntersections(curve, line): intersections = [] for t in _curve_line_intersections_t(curve, line): pt = pointFinder(*curve, t) - intersections.append(Intersection(pt=pt, t1=t, t2=_line_t_of_pt(*line, pt))) + # Back-project the point onto the line, to avoid problems with + # numerical accuracy in the case of vertical and horizontal lines + line_t = _line_t_of_pt(*line, pt) + pt = linePointAtT(*line, line_t) + intersections.append(Intersection(pt=pt, t1=t, t2=line_t)) return intersections diff --git a/Tests/misc/bezierTools_test.py b/Tests/misc/bezierTools_test.py index c5cd1b732..ba20ba6ba 100644 --- a/Tests/misc/bezierTools_test.py +++ b/Tests/misc/bezierTools_test.py @@ -1,6 +1,8 @@ +import fontTools.misc.bezierTools as bezierTools from fontTools.misc.bezierTools import ( - calcQuadraticBounds, calcCubicBounds, segmentPointAtT, splitLine, splitQuadratic, - splitCubic, splitQuadraticAtT, splitCubicAtT, solveCubic) + calcQuadraticBounds, calcCubicBounds, curveLineIntersections, + segmentPointAtT, splitLine, splitQuadratic, splitCubic, splitQuadraticAtT, + splitCubicAtT, solveCubic) import pytest @@ -148,3 +150,13 @@ _segmentPointAtT_testData = [ def test_segmentPointAtT(segment, t, expectedPoint): point = segmentPointAtT(segment, t) assert expectedPoint == point + + +def test_intersections_straight_line(): + curve = ((548, 183), (548, 289), (450, 366), (315, 366)) + line1 = ((330, 376), (330, 286)) + pt = curveLineIntersections(curve, line1)[0][0] + assert pt[0] == 330 + line = (pt, (330, 286)) + pt2 = (330.0001018806911, 295.5635754579425) + assert bezierTools._line_t_of_pt(*line, pt2) > 0