summaryrefslogtreecommitdiff
path: root/scripts/lib/fontbuild/curveFitPen.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/fontbuild/curveFitPen.py')
-rw-r--r--scripts/lib/fontbuild/curveFitPen.py107
1 files changed, 56 insertions, 51 deletions
diff --git a/scripts/lib/fontbuild/curveFitPen.py b/scripts/lib/fontbuild/curveFitPen.py
index 7c232c0..f7c0cae 100644
--- a/scripts/lib/fontbuild/curveFitPen.py
+++ b/scripts/lib/fontbuild/curveFitPen.py
@@ -17,25 +17,24 @@
__all__ = ["SubsegmentPen","SubsegmentsToCurvesPen", "segmentGlyph", "fitGlyph"]
+
from fontTools.pens.basePen import BasePen
-from fontTools.misc import bezierTools
-from robofab.pens.pointPen import AbstractPointPen
-from robofab.pens.adapterPens import PointToSegmentPen, GuessSmoothPointPen
import numpy as np
-from numpy.linalg import norm
from numpy import array as v
-from random import random
-
+from numpy.linalg import norm
+from robofab.pens.adapterPens import GuessSmoothPointPen
from robofab.pens.pointPen import BasePointToSegmentPen
+
+
class SubsegmentsToCurvesPointPen(BasePointToSegmentPen):
def __init__(self, glyph, subsegmentGlyph, subsegments):
BasePointToSegmentPen.__init__(self)
self.glyph = glyph
self.subPen = SubsegmentsToCurvesPen(None, glyph.getPen(), subsegmentGlyph, subsegments)
-
+
def setMatchTangents(self, b):
self.subPen.matchTangents = b
-
+
def _flushContour(self, segments):
#
# adapted from robofab.pens.adapterPens.rfUFOPointPen
@@ -56,13 +55,13 @@ class SubsegmentsToCurvesPointPen(BasePointToSegmentPen):
self.subPen.setLastSmooth(True)
if segmentType == 'line':
del segments[-1]
-
+
self.subPen.moveTo(movePt)
-
+
# do the rest of the segments
for segmentType, points in segments:
isSmooth = True in [smooth for pt, smooth, name, kwargs in points]
- pp = [pt for pt, smooth, name, kwargs in points]
+ pp = [pt for pt, smooth, name, kwargs in points]
if segmentType == "line":
assert len(pp) == 1
if isSmooth:
@@ -73,17 +72,18 @@ class SubsegmentsToCurvesPointPen(BasePointToSegmentPen):
assert len(pp) == 3
if isSmooth:
self.subPen.smoothCurveTo(*pp)
- else:
+ else:
self.subPen.curveTo(*pp)
elif segmentType == "qcurve":
assert 0, "qcurve not supported"
else:
assert 0, "illegal segmentType: %s" % segmentType
self.subPen.closePath()
-
+
def addComponent(self, glyphName, transform):
self.subPen.addComponent(glyphName, transform)
+
class SubsegmentsToCurvesPen(BasePen):
def __init__(self, glyphSet, otherPen, subsegmentGlyph, subsegments):
BasePen.__init__(self, None)
@@ -95,10 +95,10 @@ class SubsegmentsToCurvesPen(BasePen):
self.lastPoint = (0,0)
self.lastSmooth = False
self.nextSmooth = False
-
+
def setLastSmooth(self, b):
self.lastSmooth = b
-
+
def _moveTo(self, (x, y)):
self.contourIndex += 1
self.segmentIndex = 0
@@ -106,7 +106,7 @@ class SubsegmentsToCurvesPen(BasePen):
p = self.ssglyph.contours[self.contourIndex][0].points[0]
self.otherPen.moveTo((p.x, p.y))
self.lastPoint = (x,y)
-
+
def _lineTo(self, (x, y)):
self.segmentIndex += 1
index = self.subsegments[self.contourIndex][self.segmentIndex][0]
@@ -114,39 +114,39 @@ class SubsegmentsToCurvesPen(BasePen):
self.otherPen.lineTo((p.x, p.y))
self.lastPoint = (x,y)
self.lastSmooth = False
-
+
def smoothLineTo(self, (x, y)):
self.lineTo((x,y))
self.lastSmooth = True
-
+
def smoothCurveTo(self, (x1, y1), (x2, y2), (x3, y3)):
self.nextSmooth = True
self.curveTo((x1, y1), (x2, y2), (x3, y3))
self.nextSmooth = False
self.lastSmooth = True
-
- def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
+
+ def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
self.segmentIndex += 1
c = self.ssglyph.contours[self.contourIndex]
n = len(c)
startIndex = (self.subsegments[self.contourIndex][self.segmentIndex-1][0])
segmentCount = (self.subsegments[self.contourIndex][self.segmentIndex][1])
endIndex = (startIndex + segmentCount + 1) % (n)
-
+
indices = [(startIndex + i) % (n) for i in range(segmentCount + 1)]
points = np.array([(c[i].points[0].x, c[i].points[0].y) for i in indices])
prevPoint = (c[(startIndex - 1)].points[0].x, c[(startIndex - 1)].points[0].y)
nextPoint = (c[(endIndex) % n].points[0].x, c[(endIndex) % n].points[0].y)
prevTangent = prevPoint - points[0]
nextTangent = nextPoint - points[-1]
-
+
tangent1 = points[1] - points[0]
tangent3 = points[-2] - points[-1]
prevTangent /= np.linalg.norm(prevTangent)
nextTangent /= np.linalg.norm(nextTangent)
tangent1 /= np.linalg.norm(tangent1)
tangent3 /= np.linalg.norm(tangent3)
-
+
tangent1, junk = self.smoothTangents(tangent1, prevTangent, self.lastSmooth)
tangent3, junk = self.smoothTangents(tangent3, nextTangent, self.nextSmooth)
if self.matchTangents == True:
@@ -162,7 +162,7 @@ class SubsegmentsToCurvesPen(BasePen):
self.otherPen.curveTo((cp[1,0], cp[1,1]), (cp[2,0], cp[2,1]), (cp[3,0], cp[3,1]))
self.lastPoint = (x3, y3)
self.lastSmooth = False
-
+
def smoothTangents(self,t1,t2,forceSmooth = False):
if forceSmooth or (abs(t1.dot(t2)) > .95 and norm(t1-t2) > 1):
# print t1,t2,
@@ -170,15 +170,13 @@ class SubsegmentsToCurvesPen(BasePen):
t2 = -t1
# print t1,t2
return t1 / norm(t1), t2 / norm(t2)
-
-
+
def _closePath(self):
self.otherPen.closePath()
-
+
def _endPath(self):
self.otherPen.endPath()
-
-
+
def addComponent(self, glyphName, transformation):
self.otherPen.addComponent(glyphName, transformation)
@@ -189,10 +187,10 @@ class SubsegmentPointPen(BasePointToSegmentPen):
self.glyph = glyph
self.resolution = resolution
self.subPen = SubsegmentPen(None, glyph.getPen())
-
+
def getSubsegments(self):
return self.subPen.subsegments[:]
-
+
def _flushContour(self, segments):
#
# adapted from robofab.pens.adapterPens.rfUFOPointPen
@@ -210,9 +208,9 @@ class SubsegmentPointPen(BasePointToSegmentPen):
movePt, smooth, name, kwargs = points[-1]
if segmentType == 'line':
del segments[-1]
-
+
self.subPen.moveTo(movePt)
-
+
# do the rest of the segments
for segmentType, points in segments:
points = [pt for pt, smooth, name, kwargs in points]
@@ -227,12 +225,13 @@ class SubsegmentPointPen(BasePointToSegmentPen):
else:
assert 0, "illegal segmentType: %s" % segmentType
self.subPen.closePath()
-
+
def addComponent(self, glyphName, transform):
self.subPen.addComponent(glyphName, transform)
+
class SubsegmentPen(BasePen):
-
+
def __init__(self, glyphSet, otherPen, resolution=25):
BasePen.__init__(self,glyphSet)
self.resolution = resolution
@@ -240,7 +239,7 @@ class SubsegmentPen(BasePen):
self.subsegments = []
self.startContour = (0,0)
self.contourIndex = -1
-
+
def _moveTo(self, (x, y)):
self.contourIndex += 1
self.segmentIndex = 0
@@ -250,7 +249,7 @@ class SubsegmentPen(BasePen):
self.startContour = (x,y)
self.lastPoint = (x,y)
self.otherPen.moveTo((x,y))
-
+
def _lineTo(self, (x, y)):
count = self.stepsForSegment((x,y),self.lastPoint)
if count < 1:
@@ -262,7 +261,7 @@ class SubsegmentPen(BasePen):
y1 = self.lastPoint[1] + (y - self.lastPoint[1]) * i/float(count)
self.otherPen.lineTo((x1,y1))
self.lastPoint = (x,y)
-
+
def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
count = self.stepsForSegment((x3,y3),self.lastPoint)
if count < 2:
@@ -277,46 +276,53 @@ class SubsegmentPen(BasePen):
for i in range(count):
self.otherPen.lineTo((x[i],y[i]))
self.lastPoint = (x3,y3)
-
+
def _closePath(self):
if not (self.lastPoint[0] == self.startContour[0] and self.lastPoint[1] == self.startContour[1]):
self._lineTo(self.startContour)
+
+ # round values used by otherPen (a RoboFab SegmentToPointPen) to decide
+ # whether to delete duplicate points at start and end of contour
+ #TODO(jamesgk) figure out why we have to do this hack, then remove it
+ c = self.otherPen.contour
+ for i in [0, -1]:
+ c[i] = [[round(n, 5) for n in c[i][0]]] + list(c[i][1:])
+
self.otherPen.closePath()
-
+
def _endPath(self):
self.otherPen.endPath()
-
+
def addComponent(self, glyphName, transformation):
self.otherPen.addComponent(glyphName, transformation)
-
+
def stepsForSegment(self, p1, p2):
dist = np.linalg.norm(v(p1) - v(p2))
out = int(dist / self.resolution)
return out
-
+
def renderCurve(self,p,count):
curvePoints = []
t = 1.0 / float(count)
temp = t * t
-
+
f = p[0]
fd = 3 * (p[1] - p[0]) * t
fdd_per_2 = 3 * (p[0] - 2 * p[1] + p[2]) * temp
fddd_per_2 = 3 * (3 * (p[1] - p[2]) + p[3] - p[0]) * temp * t
-
+
fddd = fddd_per_2 + fddd_per_2
fdd = fdd_per_2 + fdd_per_2
fddd_per_6 = fddd_per_2 * (1.0 / 3)
-
+
for i in range(count):
f = f + fd + fdd_per_2 + fddd_per_6
fd = fd + fdd + fddd_per_2
fdd = fdd + fddd
fdd_per_2 = fdd_per_2 + fddd_per_2
curvePoints.append(f)
-
- return curvePoints
+ return curvePoints
def fitBezierSimple(pts):
@@ -363,14 +369,14 @@ def fitBezier(pts,tangent0=None,tangent3=None):
pout = pts.copy()
pout[:,0] -= (T[:,0] * pts[0,0]) + (T[:,3] * pts[-1,0])
pout[:,1] -= (T[:,0] * pts[0,1]) + (T[:,3] * pts[-1,1])
-
+
TT = np.zeros((n*2,4))
for i in range(n):
for j in range(2):
TT[i*2,j*2] = T[i,j+1]
TT[i*2+1,j*2+1] = T[i,j+1]
pout = pout.reshape((n*2,1),order="C")
-
+
if tangent0 != None and tangent3 != None:
tangentConstraintsT = np.array([
[tangent0[1], -tangent0[0], 0, 0],
@@ -414,4 +420,3 @@ if __name__ == '__main__':
[1,1]
])
print np.array(p.renderCurve(pts,10)) * 10
-