summaryrefslogtreecommitdiff
path: root/scripts/lib/fontbuild/italics2.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/fontbuild/italics2.py')
-rw-r--r--scripts/lib/fontbuild/italics2.py148
1 files changed, 148 insertions, 0 deletions
diff --git a/scripts/lib/fontbuild/italics2.py b/scripts/lib/fontbuild/italics2.py
new file mode 100644
index 0000000..7eee90b
--- /dev/null
+++ b/scripts/lib/fontbuild/italics2.py
@@ -0,0 +1,148 @@
+from curveFitPen import fitGlyph,segmentGlyph
+import numpy as np
+from numpy.linalg import norm
+import math
+from scipy.sparse.linalg import cg
+
+def glyphToMesh(g):
+ points = []
+ edges = {}
+ offset = 0
+ for c in g.contours:
+ if len(c) < 2:
+ continue
+ for i,prev,next in rangePrevNext(len(c)):
+ points.append((c[i].points[0].x, c[i].points[0].y))
+ edges[i + offset] = np.array([prev + offset, next + offset], dtype=int)
+ offset += len(c)
+ return np.array(points), edges
+
+def meshToGlyph(points, g):
+ g1 = g.copy()
+ j = 0
+ for c in g1.contours:
+ if len(c) < 2:
+ continue
+ for i in range(len(c)):
+ c[i].points[0].x = points[j][0]
+ c[i].points[0].y = points[j][1]
+ j += 1
+ return g1
+
+def italicize(glyph, angle=12, stemWidth=180, xoffset=-50):
+ ga,subsegments = segmentGlyph(glyph,25)
+ va, e = glyphToMesh(ga)
+ n = len(va)
+ grad = mapEdges(lambda a,(p,n): normalize(p-a), va, e)
+ cornerWeights = mapEdges(lambda a,(p,n): normalize(p-a).dot(normalize(a-n)), grad, e)[:,0].reshape((-1,1))
+ smooth = np.ones((n,1)) * .02
+ smooth[cornerWeights < .6] = 5
+ # smooth[cornerWeights >= .9999] = 2
+ out = va.copy()
+ if stemWidth > 100:
+ out = skewMesh(poisson(skewMesh(out, angle * 2), grad, e, smooth=smooth), -angle * 2)
+ out = copyMeshDetails(va, out, e, 6)
+ # return meshToGlyph(out,ga)
+ normals = edgeNormals(out, e)
+ center = va + normals * stemWidth * .4
+ if stemWidth > 100:
+ center[:, 0] = va[:, 0]
+ centerSkew = skewMesh(center.dot(np.array([[.97,0],[0,1]])), angle * .7)
+ # centerSkew = skewMesh(center, angle * .7)
+ out = out + (centerSkew - center)
+ out = copyMeshDetails(skewMesh(va, angle * .7), out, e, 12)
+ out = skewMesh(out, angle * .3)
+ out[:,0] += xoffset
+ # out[:,1] = va[:,1]
+ gOut = meshToGlyph(out, ga)
+ # gOut.width *= .97
+ gOut.width += 10
+ # return gOut
+ return fitGlyph(glyph, gOut, subsegments)
+
+def poisson(v, grad, e, smooth=1, P=None, distance=None):
+ n = len(v)
+ if distance == None:
+ distance = mapEdges(lambda a,(p,n): norm(p - a), v, e)
+ if (P == None):
+ P = mP(v,e)
+ P += np.identity(n) * smooth
+ f = v.copy()
+ for i,(prev,next) in e.iteritems():
+ f[i] = (grad[next] * distance[next] - grad[i] * distance[i])
+ out = v.copy()
+ f += v * smooth
+ for i in range(len(out[0,:])):
+ out[:,i] = cg(P, f[:,i])[0]
+ return out
+
+def mP(v,e):
+ n = len(v)
+ M = np.zeros((n,n))
+ for i, edges in e.iteritems():
+ w = -2 / float(len(edges))
+ for index in edges:
+ M[i,index] = w
+ M[i,i] = 2
+ return M
+
+def normalize(v):
+ n = np.linalg.norm(v)
+ if n == 0:
+ return v
+ return v/n
+
+def mapEdges(func,v,e,*args):
+ b = v.copy()
+ for i, edges in e.iteritems():
+ b[i] = func(v[i], [v[j] for j in edges], *args)
+ return b
+
+def getNormal(a,b,c):
+ "Assumes TT winding direction"
+ p = np.roll(normalize(b - a), 1)
+ n = -np.roll(normalize(c - a), 1)
+ p[1] *= -1
+ n[1] *= -1
+ # print p, n, normalize((p + n) * .5)
+ return normalize((p + n) * .5)
+
+def edgeNormals(v,e):
+ "Assumes a mesh where each vertex has exactly least two edges"
+ return mapEdges(lambda a,(p,n) : getNormal(a,p,n),v,e)
+
+def rangePrevNext(count):
+ c = np.arange(count,dtype=int)
+ r = np.vstack((c, np.roll(c, 1), np.roll(c, -1)))
+ return r.T
+
+def skewMesh(v,angle):
+ slope = np.tanh([math.pi * angle / 180])
+ return v.dot(np.array([[1,0],[slope,1]]))
+
+
+from scipy.ndimage.filters import gaussian_filter1d as gaussian
+
+def labelConnected(e):
+ label = 0
+ labels = np.zeros((len(e),1))
+ for i,(prev,next) in e.iteritems():
+ labels[i] = label
+ if next <= i:
+ label += 1
+ return labels
+
+def copyGradDetails(a,b,e,scale=15):
+ n = len(a)
+ labels = labelConnected(e)
+ out = a.astype(float).copy()
+ for i in range(labels[-1]+1):
+ mask = (labels==i).flatten()
+ out[mask,:] = gaussian(b[mask,:], scale, mode="wrap", axis=0) + a[mask,:] - gaussian(a[mask,:], scale, mode="wrap", axis=0)
+ return out
+
+def copyMeshDetails(va,vb,e,scale=5,smooth=.01):
+ gradA = mapEdges(lambda a,(p,n): normalize(p-a), va, e)
+ gradB = mapEdges(lambda a,(p,n): normalize(p-a), vb, e)
+ grad = copyGradDetails(gradA, gradB, e, scale)
+ return poisson(vb, grad, e, smooth=smooth) \ No newline at end of file