Fix PointInsidePen

https://github.com/behdad/fonttools/issues/621
This commit is contained in:
Behdad Esfahbod 2016-07-16 21:47:37 -07:00
parent 9c037fc826
commit 81d84e6f85
3 changed files with 65 additions and 48 deletions

View File

@ -18,8 +18,8 @@ __all__ = [
from fontTools.misc.arrayTools import calcBounds
epsilonDigits = 12
epsilon = 1e-12
epsilonDigits = 6
epsilon = 1e-10
def calcQuadraticBounds(pt1, pt2, pt3):

View File

@ -11,12 +11,6 @@ from fontTools.misc.bezierTools import solveQuadratic, solveCubic
__all__ = ["PointInsidePen"]
# working around floating point errors
EPSILON = 1e-10
ONE_PLUS_EPSILON = 1 + EPSILON
ZERO_MINUS_EPSILON = 0 - EPSILON
class PointInsidePen(BasePen):
"""This pen implements "point inside" testing: to test whether
@ -123,7 +117,7 @@ class PointInsidePen(BasePen):
by = (y3 - y2) * 3.0 - cy
ay = y4 - dy - cy - by
solutions = sorted(solveCubic(ay, by, cy, dy - y))
solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
solutions = [t for t in solutions if -0. <= t <= 1.]
if not solutions:
return
@ -142,29 +136,30 @@ class PointInsidePen(BasePen):
t3 = t2 * t
direction = 3*ay*t2 + 2*by*t + cy
incomingGoingUp = outgoingGoingUp = direction > 0.0
if direction == 0.0:
direction = 6*ay*t + 2*by
outgoingGoingUp = direction > 0.0
incomingGoingUp = not outgoingGoingUp
if direction == 0.0:
direction = ay
goingUp = direction > 0.0
incomingGoingUp = outgoingGoingUp = direction > 0.0
xt = ax*t3 + bx*t2 + cx*t + dx
if xt < x:
above = goingUp
continue
if t == 0.0:
if not goingUp:
self._addIntersection(goingUp)
if t in (0.0, -0.0):
if not outgoingGoingUp:
self._addIntersection(outgoingGoingUp)
elif t == 1.0:
if not above:
self._addIntersection(goingUp)
if incomingGoingUp:
self._addIntersection(incomingGoingUp)
else:
if above != goingUp:
self._addIntersection(goingUp)
if incomingGoingUp == outgoingGoingUp:
self._addIntersection(outgoingGoingUp)
#else:
# we're not really intersecting, merely touching the 'top'
above = goingUp
# we're not really intersecting, merely touching
def _qCurveToOne_unfinished(self, bcp, point):
# XXX need to finish this, for now doing it through a cubic

View File

@ -71,34 +71,6 @@ class PointInsidePenTest(unittest.TestCase):
"** ***",
self.render(draw_qCurves, even_odd=False))
def test_contour_integers(self):
def draw_contour(pen):
pen.moveTo( (728, 697) )
pen.curveTo( (723, 698) , (718, 699) , (713, 699) )
pen.lineTo( (504, 699) )
pen.curveTo( (487, 719) , (508, 783) , (556, 783) )
pen.lineTo( (718, 783) )
pen.curveTo( (739, 783) , (749, 712) , (728, 697) )
pen.closePath()
piPen = PointInsidePen(None, (416, 783)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.getResult(), False)
def test_contour_decimals(self):
def draw_contour(pen):
pen.moveTo( (727.546875, 697.0) )
pen.curveTo( (722.953125, 697.953125), (718.109375, 698.515625), (712.9375, 698.515625) )
pen.lineTo( (504.375, 698.515625) )
pen.curveTo( (487.328125, 719.359375), (507.84375, 783.140625), (555.796875, 783.140625) )
pen.lineTo( (717.96875, 783.140625) )
pen.curveTo( (738.890625, 783.140625), (748.796875, 711.5), (727.546875, 697.0) )
pen.closePath()
piPen = PointInsidePen(None, (416.625, 783.140625)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.getResult(), False)
@staticmethod
def render(draw_function, even_odd):
result = BytesIO()
@ -113,6 +85,56 @@ class PointInsidePenTest(unittest.TestCase):
return tounicode(result.getvalue())
def test_contour_diamond(self):
def draw_contour(pen):
pen.moveTo( (0, 100) )
pen.lineTo( (100, 0) )
pen.lineTo( (0, -100) )
pen.lineTo( (-100, 0) )
pen.closePath()
piPen = PointInsidePen(None, (-200, 0)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
piPen = PointInsidePen(None, (-200, 100)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
piPen = PointInsidePen(None, (-200, -100)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
piPen = PointInsidePen(None, (-200, 50)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
def test_contour_integers(self):
def draw_contour(pen):
pen.moveTo( (728, 697) )
pen.lineTo( (504, 699) )
pen.curveTo( (487, 719) , (508, 783) , (556, 783) )
pen.lineTo( (718, 783) )
pen.curveTo( (739, 783) , (749, 712) , (728, 697) )
pen.closePath()
piPen = PointInsidePen(None, (416, 783)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
def test_contour_decimals(self):
def draw_contour(pen):
pen.moveTo( (727.546875, 697.0) )
pen.lineTo( (504.375, 698.515625) )
pen.curveTo( (487.328125, 719.359375), (507.84375, 783.140625), (555.796875, 783.140625) )
pen.lineTo( (717.96875, 783.140625) )
pen.curveTo( (738.890625, 783.140625), (748.796875, 711.5), (727.546875, 697.0) )
pen.closePath()
piPen = PointInsidePen(None, (416.625, 783.140625)) # this point is outside
draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0)
if __name__ == "__main__":
unittest.main()