pointInsidePen: add getWinding()

Resolves https://github.com/behdad/fonttools/issues/621#issuecomment-234764830

Although, leaving contours left open is out of protocol behavior
IMO.
This commit is contained in:
Behdad Esfahbod 2016-08-10 17:30:33 -07:00
parent 5cd0a55635
commit f3ff2f8881
2 changed files with 26 additions and 22 deletions

View File

@ -40,29 +40,33 @@ class PointInsidePen(BasePen):
# http://graphics.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html # http://graphics.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
# I extended the principles outlined on that page to curves. # I extended the principles outlined on that page to curves.
def __init__(self, glyphSet, testPoint, evenOdd=0): def __init__(self, glyphSet, testPoint, evenOdd=False):
BasePen.__init__(self, glyphSet) BasePen.__init__(self, glyphSet)
self.setTestPoint(testPoint, evenOdd) self.setTestPoint(testPoint, evenOdd)
def setTestPoint(self, testPoint, evenOdd=0): def setTestPoint(self, testPoint, evenOdd=False):
"""Set the point to test. Call this _before_ the outline gets drawn.""" """Set the point to test. Call this _before_ the outline gets drawn."""
self.testPoint = testPoint self.testPoint = testPoint
self.evenOdd = evenOdd self.evenOdd = evenOdd
self.firstPoint = None self.firstPoint = None
self.intersectionCount = 0 self.intersectionCount = 0
def getResult(self): def getWinding(self):
"""After the shape has been drawn, getResult() returns True if the test
point lies within the (black) shape, and False if it doesn't.
"""
if self.firstPoint is not None: if self.firstPoint is not None:
# always make sure the sub paths are closed; the algorithm only works # always make sure the sub paths are closed; the algorithm only works
# for closed paths. # for closed paths.
self.closePath() self.closePath()
return self.intersectionCount
def getResult(self):
"""After the shape has been drawn, getResult() returns True if the test
point lies within the (black) shape, and False if it doesn't.
"""
winding = self.getWinding()
if self.evenOdd: if self.evenOdd:
result = self.intersectionCount % 2 result = winding % 2
else: else: # non-zero
result = self.intersectionCount result = self.intersectionCount != 0
return not not result return not not result
def _addIntersection(self, goingUp): def _addIntersection(self, goingUp):

View File

@ -95,12 +95,12 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (750, 295)) # this point is outside piPen = PointInsidePen(None, (750, 295)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
self.assertEqual(piPen.getResult(), False) self.assertEqual(piPen.getResult(), False)
piPen = PointInsidePen(None, (835, 190)) # this point is inside piPen = PointInsidePen(None, (835, 190)) # this point is inside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 1) self.assertEqual(piPen.getWinding(), 1)
self.assertEqual(piPen.getResult(), True) self.assertEqual(piPen.getResult(), True)
def test_contour_square_closed(self): def test_contour_square_closed(self):
@ -113,7 +113,7 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (0, 0)) # this point is inside piPen = PointInsidePen(None, (0, 0)) # this point is inside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 1) self.assertEqual(piPen.getWinding(), 1)
self.assertEqual(piPen.getResult(), True) self.assertEqual(piPen.getResult(), True)
def test_contour_square_opened(self): def test_contour_square_opened(self):
@ -126,8 +126,8 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (0, 0)) # this point is inside piPen = PointInsidePen(None, (0, 0)) # this point is inside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) # value is different from square_closed self.assertEqual(piPen.getWinding(), 1)
self.assertEqual(piPen.getResult(), True) # "is inside" still True self.assertEqual(piPen.getResult(), True)
def test_contour_circle(self): def test_contour_circle(self):
def draw_contour(pen): def draw_contour(pen):
@ -155,19 +155,19 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (-200, 0)) # this point is outside piPen = PointInsidePen(None, (-200, 0)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
piPen = PointInsidePen(None, (-200, 100)) # this point is outside piPen = PointInsidePen(None, (-200, 100)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
piPen = PointInsidePen(None, (-200, -100)) # this point is outside piPen = PointInsidePen(None, (-200, -100)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
piPen = PointInsidePen(None, (-200, 50)) # this point is outside piPen = PointInsidePen(None, (-200, 50)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
def test_contour_integers(self): def test_contour_integers(self):
def draw_contour(pen): def draw_contour(pen):
@ -180,7 +180,7 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (416, 783)) # this point is outside piPen = PointInsidePen(None, (416, 783)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
def test_contour_decimals(self): def test_contour_decimals(self):
def draw_contour(pen): def draw_contour(pen):
@ -193,7 +193,7 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (416.625, 783.140625)) # this point is outside piPen = PointInsidePen(None, (416.625, 783.140625)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
def test_contour2_integers(self): def test_contour2_integers(self):
def draw_contour(pen): def draw_contour(pen):
@ -205,7 +205,7 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (21, 50)) # this point is outside piPen = PointInsidePen(None, (21, 50)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
def test_contour2_decimals(self): def test_contour2_decimals(self):
def draw_contour(pen): def draw_contour(pen):
@ -217,7 +217,7 @@ class PointInsidePenTest(unittest.TestCase):
piPen = PointInsidePen(None, (21.25, 50.0)) # this point is outside piPen = PointInsidePen(None, (21.25, 50.0)) # this point is outside
draw_contour(piPen) draw_contour(piPen)
self.assertEqual(piPen.intersectionCount, 0) self.assertEqual(piPen.getWinding(), 0)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()