summaryrefslogtreecommitdiff
path: root/misc/pylib/robofab/pens/mathPens.py
blob: 1fe1026e1be02e40e88718e724b15baaffacefe6 (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
"""Some pens that are needed during glyph math"""


from robofab.pens.pointPen import BasePointToSegmentPen, AbstractPointPen


class GetMathDataPointPen(AbstractPointPen):
	
	"""
	Point pen that converts all "line" segments into
	curve segments containing two off curve points.
	"""
	
	def __init__(self):
		self.contours = []
		self.components = []
		self.anchors = []
		self._points = []
	
	def _flushContour(self):
		points = self._points
		if len(points) == 1:
			segmentType, pt, smooth, name = points[0]
			self.anchors.append((pt, name))
		else:
			self.contours.append([])
			prevOnCurve = None
			offCurves = []
			# deal with the first point
			segmentType, pt, smooth, name = points[0]
			# if it is an offcurve, add it to the offcurve list
			if segmentType is None:
				offCurves.append((segmentType, pt, smooth, name))
			# if it is a line, change the type to curve and add it to the contour
			# create offcurves corresponding with the last oncurve and
			# this point and add them to the points list
			elif segmentType == "line":
				prevOnCurve = pt
				self.contours[-1].append(("curve", pt, False, name))
				lastPoint = points[-1][1]
				points.append((None, lastPoint, False, None))
				points.append((None, pt, False, None))
			# a move, curve or qcurve. simple append the data.
			else:
				self.contours[-1].append((segmentType, pt, smooth, name))
				prevOnCurve = pt
			# now go through the rest of the points
			for segmentType, pt, smooth, name in points[1:]:
				# store the off curves
				if segmentType is None:
					offCurves.append((segmentType, pt, smooth, name))
					continue
				# make off curve corresponding the the previous
				# on curve an dthis point
				if segmentType == "line":
					segmentType = "curve"
					offCurves.append((None, prevOnCurve, False, None))
					offCurves.append((None, pt, False, None))
				# add the offcurves to the contour
				for offCurve in offCurves:
					self.contours[-1].append(offCurve)
				# add the oncurve to the contour
				self.contours[-1].append((segmentType, pt, smooth, name))
				# reset the stored data
				prevOnCurve = pt
				offCurves = []
			# catch offcurves that belong to the first
			if len(offCurves) != 0:
				self.contours[-1].extend(offCurves)
	
	def beginPath(self):
		self._points = []
	
	def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
		self._points.append((segmentType, pt, smooth, name))
	
	def endPath(self):
		self._flushContour()
	
	def addComponent(self, baseGlyphName, transformation):
		self.components.append((baseGlyphName, transformation))
	
	def getData(self):
		return {
			'contours':list(self.contours),
			'components':list(self.components),
			'anchors':list(self.anchors)
			}


class CurveSegmentFilterPointPen(AbstractPointPen):
	
	"""
	Point pen that turns curve segments that contain
	unnecessary anchor points into line segments.
	"""
	# XXX it would be great if this also checked to see if the
	# off curves are on the segment and therefre unneeded
	
	def __init__(self, anotherPointPen):
		self._pen = anotherPointPen
		self._points = []
	
	def _flushContour(self):
		points = self._points
		# an anchor
		if len(points) == 1:
			pt, segmentType, smooth, name = points[0]
			self._pen.addPoint(pt, segmentType, smooth, name)
		else:
			prevOnCurve = None
			offCurves = []
			
			pointsToDraw = []
			
			# deal with the first point
			pt, segmentType, smooth, name = points[0]
			# if it is an offcurve, add it to the offcurve list
			if segmentType is None:
				offCurves.append((pt, segmentType, smooth, name))
			else:
				# potential redundancy
				if segmentType == "curve":
					# gather preceding off curves
					testOffCurves = []
					lastPoint = None
					for i in xrange(len(points)):
						i = -i - 1
						testPoint = points[i]
						testSegmentType = testPoint[1]
						if testSegmentType is not None:
							lastPoint = testPoint[0]
							break
						testOffCurves.append(testPoint[0])
					# if two offcurves exist we can test for redundancy
					if len(testOffCurves) == 2:
						if testOffCurves[1] == lastPoint and testOffCurves[0] == pt:
							segmentType = "line"
							# remove the last two points
							points = points[:-2]
				# add the point to the contour
				pointsToDraw.append((pt, segmentType, smooth, name))
				prevOnCurve = pt
			for pt, segmentType, smooth, name in points[1:]:
				# store offcurves
				if segmentType is None:
					offCurves.append((pt, segmentType, smooth, name))
					continue
				# curves are a potential redundancy
				elif segmentType == "curve":
					if len(offCurves) == 2:
						# test for redundancy
						if offCurves[0][0] == prevOnCurve and offCurves[1][0] == pt:
							offCurves = []
							segmentType = "line"
				# add all offcurves
				for offCurve in offCurves:
					pointsToDraw.append(offCurve)
				# add the on curve
				pointsToDraw.append((pt, segmentType, smooth, name))
				# reset the stored data
				prevOnCurve = pt
				offCurves = []
			# catch any remaining offcurves
			if len(offCurves) != 0:
				for offCurve in offCurves:
					pointsToDraw.append(offCurve)
			# draw to the pen
			for pt, segmentType, smooth, name in pointsToDraw:
				self._pen.addPoint(pt, segmentType, smooth, name)
	
	def beginPath(self):
		self._points = []
		self._pen.beginPath()
	
	def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
		self._points.append((pt, segmentType, smooth, name))
	
	def endPath(self):
		self._flushContour()
		self._pen.endPath()
	
	def addComponent(self, baseGlyphName, transformation):
		self._pen.addComponent(baseGlyphName, transformation)