This repository has been archived on 2025-10-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
inter-font/misc/pylib/robofab/pens/reverseContourPointPen.py
Rasmus Andersson 8234b62ab7 Speeds up font compilation by around 200%
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)
2017-09-04 11:12:34 -04:00

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()