126 lines
3.5 KiB
Python
Executable File
126 lines
3.5 KiB
Python
Executable File
"""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()
|