pointInsidePen.py revision 4162de350edeb4aa09c508e5a39ac5fa20d8d619
1from fontTools.pens.basePen import BasePen 2from fontTools.misc.bezierTools import solveQuadratic, solveCubic 3 4 5__all__ = ["PointInsidePen"] 6 7 8class PointInsidePen(BasePen): 9 10 def __init__(self, glyphSet, testPoint, evenOdd=0): 11 BasePen.__init__(self, glyphSet) 12 self.setTestPoint(testPoint, evenOdd) 13 14 def setTestPoint(self, testPoint, evenOdd=0): 15 self.testPoint = testPoint 16 self.evenOdd = evenOdd 17 self.firstPoint = None 18 self.intersectionCount = 0 19 20 def getResult(self): 21 if self.firstPoint is not None: 22 self.closePath() 23 if self.evenOdd: 24 result = self.intersectionCount % 2 25 else: 26 result = self.intersectionCount 27 return not not result 28 29 def _addIntersection(self, goingUp): 30 if self.evenOdd or goingUp: 31 self.intersectionCount += 1 32 else: 33 self.intersectionCount -= 1 34 35 def _moveTo(self, point): 36 if self.firstPoint is not None: 37 self.closePath() 38 self.firstPoint = point 39 40 def _lineTo(self, point): 41 # An amazingly clear explanation of the problem: 42 # http://graphics.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html 43 x, y = self.testPoint 44 x1, y1 = self._getCurrentPoint() 45 x2, y2 = point 46 47 if x1 < x and x2 < x: 48 return 49 if y1 < y and y2 < y: 50 return 51 if y1 >= y and y2 >= y: 52 return 53 54 dx = x2 - x1 55 dy = y2 - y1 56 t = float(y - y1) / dy 57 ix = dx * t + x1 58 if ix < x: 59 return 60 self._addIntersection(y2 > y1) 61 62 def _curveToOne(self, bcp1, bcp2, point): 63 x, y = self.testPoint 64 x1, y1 = self._getCurrentPoint() 65 x2, y2 = bcp1 66 x3, y3 = bcp2 67 x4, y4 = point 68 69 if x1 < x and x2 < x and x3 < x and x4 < x: 70 return 71 if y1 < y and y2 < y and y3 < y and y4 < y: 72 return 73 if y1 >= y and y2 >= y and y3 >= y and y4 >= y: 74 return 75 76 dy = y1 77 cy = (y2 - dy) * 3.0 78 by = (y3 - y2) * 3.0 - cy 79 ay = y4 - dy - cy - by 80 solutions = solveCubic(ay, by, cy, dy - y) 81 solutions.sort() 82 solutions = [t for t in solutions if 0 <= t <= 1] 83 if not solutions: 84 return 85 86 dx = x1 87 cx = (x2 - dx) * 3.0 88 bx = (x3 - x2) * 3.0 - cx 89 ax = x4 - dx - cx - bx 90 91 above = y1 >= y 92 lastT = None 93 for t in solutions: 94 if t == lastT: 95 continue 96 lastT = t 97 t2 = t * t 98 t3 = t2 * t 99 100 direction = 3*ay*t2 + 2*by*t + cy 101 if direction == 0.0: 102 direction = 6*ay*t + 2*by 103 if direction == 0.0: 104 direction = ay 105 goingUp = direction > 0.0 106 107 xt = ax*t3 + bx*t2 + cx*t + dx 108 if xt < x: 109 above = goingUp 110 continue 111 112 if t == 0.0: 113 if not goingUp: 114 self._addIntersection(goingUp) 115 elif t == 1.0: 116 if not above: 117 self._addIntersection(goingUp) 118 else: 119 if above != goingUp: 120 self._addIntersection(goingUp) 121 #else: 122 # we're not really intersecting, merely touching the 'top' 123 above = goingUp 124 125 def _qCurveToOne_unfinished(self, bcp, point): 126 # XXX need to finish this, for now doing it through a cubic 127 # (BasePen implements _qCurveTo in terms of a cubic) will 128 # have to do. 129 x, y = self.testPoint 130 x1, y1 = self._getCurrentPoint() 131 x2, y2 = bcp 132 x3, y3 = point 133 c = y1 134 b = (y2 - c) * 2.0 135 a = y3 - c - b 136 solutions = solveQuadratic(a, b, c - y) 137 solutions.sort() 138 solutions = [t for t in solutions if 0 <= t <= 1] 139 if not solutions: 140 return 141 XXX 142 143 def _closePath(self): 144 if self._getCurrentPoint() != self.firstPoint: 145 self.lineTo(self.firstPoint) 146 self.firstPoint = None 147