1*e1fe3e4aSElliott Hughesfrom fontTools.pens.basePen import BasePen 2*e1fe3e4aSElliott Hughesfrom functools import partial 3*e1fe3e4aSElliott Hughesfrom itertools import count 4*e1fe3e4aSElliott Hughesimport sympy as sp 5*e1fe3e4aSElliott Hughesimport sys 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughesn = 3 # Max Bezier degree; 3 for cubic, 2 for quadratic 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughest, x, y = sp.symbols("t x y", real=True) 10*e1fe3e4aSElliott Hughesc = sp.symbols("c", real=False) # Complex representation instead of x/y 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott HughesX = tuple(sp.symbols("x:%d" % (n + 1), real=True)) 13*e1fe3e4aSElliott HughesY = tuple(sp.symbols("y:%d" % (n + 1), real=True)) 14*e1fe3e4aSElliott HughesP = tuple(zip(*(sp.symbols("p:%d[%s]" % (n + 1, w), real=True) for w in "01"))) 15*e1fe3e4aSElliott HughesC = tuple(sp.symbols("c:%d" % (n + 1), real=False)) 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughes# Cubic Bernstein basis functions 18*e1fe3e4aSElliott HughesBinomialCoefficient = [(1, 0)] 19*e1fe3e4aSElliott Hughesfor i in range(1, n + 1): 20*e1fe3e4aSElliott Hughes last = BinomialCoefficient[-1] 21*e1fe3e4aSElliott Hughes this = tuple(last[j - 1] + last[j] for j in range(len(last))) + (0,) 22*e1fe3e4aSElliott Hughes BinomialCoefficient.append(this) 23*e1fe3e4aSElliott HughesBinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient) 24*e1fe3e4aSElliott Hughesdel last, this 25*e1fe3e4aSElliott Hughes 26*e1fe3e4aSElliott HughesBernsteinPolynomial = tuple( 27*e1fe3e4aSElliott Hughes tuple(c * t**i * (1 - t) ** (n - i) for i, c in enumerate(coeffs)) 28*e1fe3e4aSElliott Hughes for n, coeffs in enumerate(BinomialCoefficient) 29*e1fe3e4aSElliott Hughes) 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott HughesBezierCurve = tuple( 32*e1fe3e4aSElliott Hughes tuple( 33*e1fe3e4aSElliott Hughes sum(P[i][j] * bernstein for i, bernstein in enumerate(bernsteins)) 34*e1fe3e4aSElliott Hughes for j in range(2) 35*e1fe3e4aSElliott Hughes ) 36*e1fe3e4aSElliott Hughes for n, bernsteins in enumerate(BernsteinPolynomial) 37*e1fe3e4aSElliott Hughes) 38*e1fe3e4aSElliott HughesBezierCurveC = tuple( 39*e1fe3e4aSElliott Hughes sum(C[i] * bernstein for i, bernstein in enumerate(bernsteins)) 40*e1fe3e4aSElliott Hughes for n, bernsteins in enumerate(BernsteinPolynomial) 41*e1fe3e4aSElliott Hughes) 42*e1fe3e4aSElliott Hughes 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughesdef green(f, curveXY): 45*e1fe3e4aSElliott Hughes f = -sp.integrate(sp.sympify(f), y) 46*e1fe3e4aSElliott Hughes f = f.subs({x: curveXY[0], y: curveXY[1]}) 47*e1fe3e4aSElliott Hughes f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1)) 48*e1fe3e4aSElliott Hughes return f 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughesclass _BezierFuncsLazy(dict): 52*e1fe3e4aSElliott Hughes def __init__(self, symfunc): 53*e1fe3e4aSElliott Hughes self._symfunc = symfunc 54*e1fe3e4aSElliott Hughes self._bezfuncs = {} 55*e1fe3e4aSElliott Hughes 56*e1fe3e4aSElliott Hughes def __missing__(self, i): 57*e1fe3e4aSElliott Hughes args = ["p%d" % d for d in range(i + 1)] 58*e1fe3e4aSElliott Hughes f = green(self._symfunc, BezierCurve[i]) 59*e1fe3e4aSElliott Hughes f = sp.gcd_terms(f.collect(sum(P, ()))) # Optimize 60*e1fe3e4aSElliott Hughes return sp.lambdify(args, f) 61*e1fe3e4aSElliott Hughes 62*e1fe3e4aSElliott Hughes 63*e1fe3e4aSElliott Hughesclass GreenPen(BasePen): 64*e1fe3e4aSElliott Hughes _BezierFuncs = {} 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughes @classmethod 67*e1fe3e4aSElliott Hughes def _getGreenBezierFuncs(celf, func): 68*e1fe3e4aSElliott Hughes funcstr = str(func) 69*e1fe3e4aSElliott Hughes if not funcstr in celf._BezierFuncs: 70*e1fe3e4aSElliott Hughes celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func) 71*e1fe3e4aSElliott Hughes return celf._BezierFuncs[funcstr] 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott Hughes def __init__(self, func, glyphset=None): 74*e1fe3e4aSElliott Hughes BasePen.__init__(self, glyphset) 75*e1fe3e4aSElliott Hughes self._funcs = self._getGreenBezierFuncs(func) 76*e1fe3e4aSElliott Hughes self.value = 0 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott Hughes def _moveTo(self, p0): 79*e1fe3e4aSElliott Hughes self.__startPoint = p0 80*e1fe3e4aSElliott Hughes 81*e1fe3e4aSElliott Hughes def _closePath(self): 82*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 83*e1fe3e4aSElliott Hughes if p0 != self.__startPoint: 84*e1fe3e4aSElliott Hughes self._lineTo(self.__startPoint) 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott Hughes def _endPath(self): 87*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 88*e1fe3e4aSElliott Hughes if p0 != self.__startPoint: 89*e1fe3e4aSElliott Hughes # Green theorem is not defined on open contours. 90*e1fe3e4aSElliott Hughes raise NotImplementedError 91*e1fe3e4aSElliott Hughes 92*e1fe3e4aSElliott Hughes def _lineTo(self, p1): 93*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 94*e1fe3e4aSElliott Hughes self.value += self._funcs[1](p0, p1) 95*e1fe3e4aSElliott Hughes 96*e1fe3e4aSElliott Hughes def _qCurveToOne(self, p1, p2): 97*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 98*e1fe3e4aSElliott Hughes self.value += self._funcs[2](p0, p1, p2) 99*e1fe3e4aSElliott Hughes 100*e1fe3e4aSElliott Hughes def _curveToOne(self, p1, p2, p3): 101*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 102*e1fe3e4aSElliott Hughes self.value += self._funcs[3](p0, p1, p2, p3) 103*e1fe3e4aSElliott Hughes 104*e1fe3e4aSElliott Hughes 105*e1fe3e4aSElliott Hughes# Sample pens. 106*e1fe3e4aSElliott Hughes# Do not use this in real code. 107*e1fe3e4aSElliott Hughes# Use fontTools.pens.momentsPen.MomentsPen instead. 108*e1fe3e4aSElliott HughesAreaPen = partial(GreenPen, func=1) 109*e1fe3e4aSElliott HughesMomentXPen = partial(GreenPen, func=x) 110*e1fe3e4aSElliott HughesMomentYPen = partial(GreenPen, func=y) 111*e1fe3e4aSElliott HughesMomentXXPen = partial(GreenPen, func=x * x) 112*e1fe3e4aSElliott HughesMomentYYPen = partial(GreenPen, func=y * y) 113*e1fe3e4aSElliott HughesMomentXYPen = partial(GreenPen, func=x * y) 114*e1fe3e4aSElliott Hughes 115*e1fe3e4aSElliott Hughes 116*e1fe3e4aSElliott Hughesdef printGreenPen(penName, funcs, file=sys.stdout, docstring=None): 117*e1fe3e4aSElliott Hughes if docstring is not None: 118*e1fe3e4aSElliott Hughes print('"""%s"""' % docstring) 119*e1fe3e4aSElliott Hughes 120*e1fe3e4aSElliott Hughes print( 121*e1fe3e4aSElliott Hughes """from fontTools.pens.basePen import BasePen, OpenContourError 122*e1fe3e4aSElliott Hughestry: 123*e1fe3e4aSElliott Hughes import cython 124*e1fe3e4aSElliott Hughes 125*e1fe3e4aSElliott Hughes COMPILED = cython.compiled 126*e1fe3e4aSElliott Hughesexcept (AttributeError, ImportError): 127*e1fe3e4aSElliott Hughes # if cython not installed, use mock module with no-op decorators and types 128*e1fe3e4aSElliott Hughes from fontTools.misc import cython 129*e1fe3e4aSElliott Hughes 130*e1fe3e4aSElliott Hughes COMPILED = False 131*e1fe3e4aSElliott Hughes 132*e1fe3e4aSElliott Hughes 133*e1fe3e4aSElliott Hughes__all__ = ["%s"] 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughesclass %s(BasePen): 136*e1fe3e4aSElliott Hughes 137*e1fe3e4aSElliott Hughes def __init__(self, glyphset=None): 138*e1fe3e4aSElliott Hughes BasePen.__init__(self, glyphset) 139*e1fe3e4aSElliott Hughes""" 140*e1fe3e4aSElliott Hughes % (penName, penName), 141*e1fe3e4aSElliott Hughes file=file, 142*e1fe3e4aSElliott Hughes ) 143*e1fe3e4aSElliott Hughes for name, f in funcs: 144*e1fe3e4aSElliott Hughes print(" self.%s = 0" % name, file=file) 145*e1fe3e4aSElliott Hughes print( 146*e1fe3e4aSElliott Hughes """ 147*e1fe3e4aSElliott Hughes def _moveTo(self, p0): 148*e1fe3e4aSElliott Hughes self.__startPoint = p0 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes def _closePath(self): 151*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 152*e1fe3e4aSElliott Hughes if p0 != self.__startPoint: 153*e1fe3e4aSElliott Hughes self._lineTo(self.__startPoint) 154*e1fe3e4aSElliott Hughes 155*e1fe3e4aSElliott Hughes def _endPath(self): 156*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 157*e1fe3e4aSElliott Hughes if p0 != self.__startPoint: 158*e1fe3e4aSElliott Hughes # Green theorem is not defined on open contours. 159*e1fe3e4aSElliott Hughes raise OpenContourError( 160*e1fe3e4aSElliott Hughes "Green theorem is not defined on open contours." 161*e1fe3e4aSElliott Hughes ) 162*e1fe3e4aSElliott Hughes""", 163*e1fe3e4aSElliott Hughes end="", 164*e1fe3e4aSElliott Hughes file=file, 165*e1fe3e4aSElliott Hughes ) 166*e1fe3e4aSElliott Hughes 167*e1fe3e4aSElliott Hughes for n in (1, 2, 3): 168*e1fe3e4aSElliott Hughes subs = {P[i][j]: [X, Y][j][i] for i in range(n + 1) for j in range(2)} 169*e1fe3e4aSElliott Hughes greens = [green(f, BezierCurve[n]) for name, f in funcs] 170*e1fe3e4aSElliott Hughes greens = [sp.gcd_terms(f.collect(sum(P, ()))) for f in greens] # Optimize 171*e1fe3e4aSElliott Hughes greens = [f.subs(subs) for f in greens] # Convert to p to x/y 172*e1fe3e4aSElliott Hughes defs, exprs = sp.cse( 173*e1fe3e4aSElliott Hughes greens, 174*e1fe3e4aSElliott Hughes optimizations="basic", 175*e1fe3e4aSElliott Hughes symbols=(sp.Symbol("r%d" % i) for i in count()), 176*e1fe3e4aSElliott Hughes ) 177*e1fe3e4aSElliott Hughes 178*e1fe3e4aSElliott Hughes print() 179*e1fe3e4aSElliott Hughes for name, value in defs: 180*e1fe3e4aSElliott Hughes print(" @cython.locals(%s=cython.double)" % name, file=file) 181*e1fe3e4aSElliott Hughes if n == 1: 182*e1fe3e4aSElliott Hughes print( 183*e1fe3e4aSElliott Hughes """\ 184*e1fe3e4aSElliott Hughes @cython.locals(x0=cython.double, y0=cython.double) 185*e1fe3e4aSElliott Hughes @cython.locals(x1=cython.double, y1=cython.double) 186*e1fe3e4aSElliott Hughes def _lineTo(self, p1): 187*e1fe3e4aSElliott Hughes x0,y0 = self._getCurrentPoint() 188*e1fe3e4aSElliott Hughes x1,y1 = p1 189*e1fe3e4aSElliott Hughes""", 190*e1fe3e4aSElliott Hughes file=file, 191*e1fe3e4aSElliott Hughes ) 192*e1fe3e4aSElliott Hughes elif n == 2: 193*e1fe3e4aSElliott Hughes print( 194*e1fe3e4aSElliott Hughes """\ 195*e1fe3e4aSElliott Hughes @cython.locals(x0=cython.double, y0=cython.double) 196*e1fe3e4aSElliott Hughes @cython.locals(x1=cython.double, y1=cython.double) 197*e1fe3e4aSElliott Hughes @cython.locals(x2=cython.double, y2=cython.double) 198*e1fe3e4aSElliott Hughes def _qCurveToOne(self, p1, p2): 199*e1fe3e4aSElliott Hughes x0,y0 = self._getCurrentPoint() 200*e1fe3e4aSElliott Hughes x1,y1 = p1 201*e1fe3e4aSElliott Hughes x2,y2 = p2 202*e1fe3e4aSElliott Hughes""", 203*e1fe3e4aSElliott Hughes file=file, 204*e1fe3e4aSElliott Hughes ) 205*e1fe3e4aSElliott Hughes elif n == 3: 206*e1fe3e4aSElliott Hughes print( 207*e1fe3e4aSElliott Hughes """\ 208*e1fe3e4aSElliott Hughes @cython.locals(x0=cython.double, y0=cython.double) 209*e1fe3e4aSElliott Hughes @cython.locals(x1=cython.double, y1=cython.double) 210*e1fe3e4aSElliott Hughes @cython.locals(x2=cython.double, y2=cython.double) 211*e1fe3e4aSElliott Hughes @cython.locals(x3=cython.double, y3=cython.double) 212*e1fe3e4aSElliott Hughes def _curveToOne(self, p1, p2, p3): 213*e1fe3e4aSElliott Hughes x0,y0 = self._getCurrentPoint() 214*e1fe3e4aSElliott Hughes x1,y1 = p1 215*e1fe3e4aSElliott Hughes x2,y2 = p2 216*e1fe3e4aSElliott Hughes x3,y3 = p3 217*e1fe3e4aSElliott Hughes""", 218*e1fe3e4aSElliott Hughes file=file, 219*e1fe3e4aSElliott Hughes ) 220*e1fe3e4aSElliott Hughes for name, value in defs: 221*e1fe3e4aSElliott Hughes print(" %s = %s" % (name, value), file=file) 222*e1fe3e4aSElliott Hughes 223*e1fe3e4aSElliott Hughes print(file=file) 224*e1fe3e4aSElliott Hughes for name, value in zip([f[0] for f in funcs], exprs): 225*e1fe3e4aSElliott Hughes print(" self.%s += %s" % (name, value), file=file) 226*e1fe3e4aSElliott Hughes 227*e1fe3e4aSElliott Hughes print( 228*e1fe3e4aSElliott Hughes """ 229*e1fe3e4aSElliott Hughesif __name__ == '__main__': 230*e1fe3e4aSElliott Hughes from fontTools.misc.symfont import x, y, printGreenPen 231*e1fe3e4aSElliott Hughes printGreenPen('%s', [""" 232*e1fe3e4aSElliott Hughes % penName, 233*e1fe3e4aSElliott Hughes file=file, 234*e1fe3e4aSElliott Hughes ) 235*e1fe3e4aSElliott Hughes for name, f in funcs: 236*e1fe3e4aSElliott Hughes print(" ('%s', %s)," % (name, str(f)), file=file) 237*e1fe3e4aSElliott Hughes print(" ])", file=file) 238*e1fe3e4aSElliott Hughes 239*e1fe3e4aSElliott Hughes 240*e1fe3e4aSElliott Hughesif __name__ == "__main__": 241*e1fe3e4aSElliott Hughes pen = AreaPen() 242*e1fe3e4aSElliott Hughes pen.moveTo((100, 100)) 243*e1fe3e4aSElliott Hughes pen.lineTo((100, 200)) 244*e1fe3e4aSElliott Hughes pen.lineTo((200, 200)) 245*e1fe3e4aSElliott Hughes pen.curveTo((200, 250), (300, 300), (250, 350)) 246*e1fe3e4aSElliott Hughes pen.lineTo((200, 100)) 247*e1fe3e4aSElliott Hughes pen.closePath() 248*e1fe3e4aSElliott Hughes print(pen.value) 249