diff options
Diffstat (limited to 'misc/pylib/robofab/pens/reverseContourPointPen.py')
-rwxr-xr-x | misc/pylib/robofab/pens/reverseContourPointPen.py | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/misc/pylib/robofab/pens/reverseContourPointPen.py b/misc/pylib/robofab/pens/reverseContourPointPen.py new file mode 100755 index 000000000..8ce001b4d --- /dev/null +++ b/misc/pylib/robofab/pens/reverseContourPointPen.py @@ -0,0 +1,125 @@ +"""PointPen for reversing the winding direction of contours.""" + + +__all__ = ["ReverseContourPointPen"] + + +from robofab.pens.pointPen import AbstractPointPen + + +class ReverseContourPointPen(AbstractPointPen): + + """This is a PointPen that passes outline data to another PointPen, but + reversing the winding direction of all contours. Components are simply + passed through unchanged. + + Closed contours are reversed in such a way that the first point remains + the first point. + """ + + def __init__(self, outputPointPen): + self.pen = outputPointPen + self.currentContour = None # a place to store the points for the current sub path + + def _flushContour(self): + pen = self.pen + contour = self.currentContour + if not contour: + pen.beginPath() + pen.endPath() + return + + closed = contour[0][1] != "move" + if not closed: + lastSegmentType = "move" + else: + # Remove the first point and insert it at the end. When + # the list of points gets reversed, this point will then + # again be at the start. In other words, the following + # will hold: + # for N in range(len(originalContour)): + # originalContour[N] == reversedContour[-N] + contour.append(contour.pop(0)) + # Find the first on-curve point. + firstOnCurve = None + for i in range(len(contour)): + if contour[i][1] is not None: + firstOnCurve = i + break + if firstOnCurve is None: + # There are no on-curve points, be basically have to + # do nothing but contour.reverse(). + lastSegmentType = None + else: + lastSegmentType = contour[firstOnCurve][1] + + contour.reverse() + if not closed: + # Open paths must start with a move, so we simply dump + # all off-curve points leading up to the first on-curve. + while contour[0][1] is None: + contour.pop(0) + pen.beginPath() + for pt, nextSegmentType, smooth, name in contour: + if nextSegmentType is not None: + segmentType = lastSegmentType + lastSegmentType = nextSegmentType + else: + segmentType = None + pen.addPoint(pt, segmentType=segmentType, smooth=smooth, name=name) + pen.endPath() + + def beginPath(self): + assert self.currentContour is None + self.currentContour = [] + self.onCurve = [] + + def endPath(self): + assert self.currentContour is not None + self._flushContour() + self.currentContour = None + + def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): + self.currentContour.append((pt, segmentType, smooth, name)) + + def addComponent(self, glyphName, transform): + assert self.currentContour is None + self.pen.addComponent(glyphName, transform) + + +if __name__ == "__main__": + from robofab.pens.pointPen import PrintingPointPen + pP = PrintingPointPen() + rP = ReverseContourPointPen(pP) + + rP.beginPath() + rP.addPoint((339, -8), "curve") + rP.addPoint((502, -8)) + rP.addPoint((635, 65)) + rP.addPoint((635, 305), "curve") + rP.addPoint((635, 545)) + rP.addPoint((504, 623)) + rP.addPoint((340, 623), "curve") + rP.addPoint((177, 623)) + rP.addPoint((43, 545)) + rP.addPoint((43, 305), "curve") + rP.addPoint((43, 65)) + rP.addPoint((176, -8)) + rP.endPath() + + rP.beginPath() + rP.addPoint((100, 100), "move", smooth=False, name='a') + rP.addPoint((150, 150)) + rP.addPoint((200, 200)) + rP.addPoint((250, 250), "curve", smooth=True, name='b') + rP.addPoint((300, 300), "line", smooth=False, name='c') + rP.addPoint((350, 350)) + rP.addPoint((400, 400)) + rP.addPoint((450, 450)) + rP.addPoint((500, 500), "curve", smooth=True, name='d') + rP.addPoint((550, 550)) + rP.addPoint((600, 600)) + rP.addPoint((650, 650)) + rP.addPoint((700, 700)) + rP.addPoint((750, 750), "qcurve", smooth=False, name='e') + rP.endPath() |