Cython is used to compile some hot paths into native Python extensions. These hot paths were identified through running ufocompile with the hotshot profiler and then converting file by file to Cython, starting with the "hottest" paths and continuing until returns were deminishing. This means that only a few Python files were converted to Cython. Closes #23 Closes #20 (really this time)
125 lines
3.5 KiB
Python
Executable file
125 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()
|