summaryrefslogtreecommitdiff
path: root/misc/pylib/robofab/pens/reverseContourPointPen.py
blob: 8ce001b4d6bdc6f1c3e46fe5ea7801767be26ff7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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()