| | from fontTools.pens.basePen import BasePen |
| | from functools import partial |
| | from itertools import count |
| | import sympy as sp |
| | import sys |
| |
|
| | n = 3 |
| |
|
| | t, x, y = sp.symbols("t x y", real=True) |
| | c = sp.symbols("c", real=False) |
| |
|
| | X = tuple(sp.symbols("x:%d" % (n + 1), real=True)) |
| | Y = tuple(sp.symbols("y:%d" % (n + 1), real=True)) |
| | P = tuple(zip(*(sp.symbols("p:%d[%s]" % (n + 1, w), real=True) for w in "01"))) |
| | C = tuple(sp.symbols("c:%d" % (n + 1), real=False)) |
| |
|
| | |
| | BinomialCoefficient = [(1, 0)] |
| | for i in range(1, n + 1): |
| | last = BinomialCoefficient[-1] |
| | this = tuple(last[j - 1] + last[j] for j in range(len(last))) + (0,) |
| | BinomialCoefficient.append(this) |
| | BinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient) |
| | del last, this |
| |
|
| | BernsteinPolynomial = tuple( |
| | tuple(c * t**i * (1 - t) ** (n - i) for i, c in enumerate(coeffs)) |
| | for n, coeffs in enumerate(BinomialCoefficient) |
| | ) |
| |
|
| | BezierCurve = tuple( |
| | tuple( |
| | sum(P[i][j] * bernstein for i, bernstein in enumerate(bernsteins)) |
| | for j in range(2) |
| | ) |
| | for n, bernsteins in enumerate(BernsteinPolynomial) |
| | ) |
| | BezierCurveC = tuple( |
| | sum(C[i] * bernstein for i, bernstein in enumerate(bernsteins)) |
| | for n, bernsteins in enumerate(BernsteinPolynomial) |
| | ) |
| |
|
| |
|
| | def green(f, curveXY): |
| | f = -sp.integrate(sp.sympify(f), y) |
| | f = f.subs({x: curveXY[0], y: curveXY[1]}) |
| | f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1)) |
| | return f |
| |
|
| |
|
| | class _BezierFuncsLazy(dict): |
| | def __init__(self, symfunc): |
| | self._symfunc = symfunc |
| | self._bezfuncs = {} |
| |
|
| | def __missing__(self, i): |
| | args = ["p%d" % d for d in range(i + 1)] |
| | f = green(self._symfunc, BezierCurve[i]) |
| | f = sp.gcd_terms(f.collect(sum(P, ()))) |
| | return sp.lambdify(args, f) |
| |
|
| |
|
| | class GreenPen(BasePen): |
| | _BezierFuncs = {} |
| |
|
| | @classmethod |
| | def _getGreenBezierFuncs(celf, func): |
| | funcstr = str(func) |
| | if not funcstr in celf._BezierFuncs: |
| | celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func) |
| | return celf._BezierFuncs[funcstr] |
| |
|
| | def __init__(self, func, glyphset=None): |
| | BasePen.__init__(self, glyphset) |
| | self._funcs = self._getGreenBezierFuncs(func) |
| | self.value = 0 |
| |
|
| | def _moveTo(self, p0): |
| | self.__startPoint = p0 |
| |
|
| | def _closePath(self): |
| | p0 = self._getCurrentPoint() |
| | if p0 != self.__startPoint: |
| | self._lineTo(self.__startPoint) |
| |
|
| | def _endPath(self): |
| | p0 = self._getCurrentPoint() |
| | if p0 != self.__startPoint: |
| | |
| | raise NotImplementedError |
| |
|
| | def _lineTo(self, p1): |
| | p0 = self._getCurrentPoint() |
| | self.value += self._funcs[1](p0, p1) |
| |
|
| | def _qCurveToOne(self, p1, p2): |
| | p0 = self._getCurrentPoint() |
| | self.value += self._funcs[2](p0, p1, p2) |
| |
|
| | def _curveToOne(self, p1, p2, p3): |
| | p0 = self._getCurrentPoint() |
| | self.value += self._funcs[3](p0, p1, p2, p3) |
| |
|
| |
|
| | |
| | |
| | |
| | AreaPen = partial(GreenPen, func=1) |
| | MomentXPen = partial(GreenPen, func=x) |
| | MomentYPen = partial(GreenPen, func=y) |
| | MomentXXPen = partial(GreenPen, func=x * x) |
| | MomentYYPen = partial(GreenPen, func=y * y) |
| | MomentXYPen = partial(GreenPen, func=x * y) |
| |
|
| |
|
| | def printGreenPen(penName, funcs, file=sys.stdout, docstring=None): |
| | if docstring is not None: |
| | print('"""%s"""' % docstring) |
| |
|
| | print( |
| | """from fontTools.pens.basePen import BasePen, OpenContourError |
| | try: |
| | import cython |
| | |
| | COMPILED = cython.compiled |
| | except (AttributeError, ImportError): |
| | # if cython not installed, use mock module with no-op decorators and types |
| | from fontTools.misc import cython |
| | |
| | COMPILED = False |
| | |
| | |
| | __all__ = ["%s"] |
| | |
| | class %s(BasePen): |
| | |
| | def __init__(self, glyphset=None): |
| | BasePen.__init__(self, glyphset) |
| | """ |
| | % (penName, penName), |
| | file=file, |
| | ) |
| | for name, f in funcs: |
| | print(" self.%s = 0" % name, file=file) |
| | print( |
| | """ |
| | def _moveTo(self, p0): |
| | self.__startPoint = p0 |
| | |
| | def _closePath(self): |
| | p0 = self._getCurrentPoint() |
| | if p0 != self.__startPoint: |
| | self._lineTo(self.__startPoint) |
| | |
| | def _endPath(self): |
| | p0 = self._getCurrentPoint() |
| | if p0 != self.__startPoint: |
| | # Green theorem is not defined on open contours. |
| | raise OpenContourError( |
| | "Green theorem is not defined on open contours." |
| | ) |
| | """, |
| | end="", |
| | file=file, |
| | ) |
| |
|
| | for n in (1, 2, 3): |
| | subs = {P[i][j]: [X, Y][j][i] for i in range(n + 1) for j in range(2)} |
| | greens = [green(f, BezierCurve[n]) for name, f in funcs] |
| | greens = [sp.gcd_terms(f.collect(sum(P, ()))) for f in greens] |
| | greens = [f.subs(subs) for f in greens] |
| | defs, exprs = sp.cse( |
| | greens, |
| | optimizations="basic", |
| | symbols=(sp.Symbol("r%d" % i) for i in count()), |
| | ) |
| |
|
| | print() |
| | for name, value in defs: |
| | print(" @cython.locals(%s=cython.double)" % name, file=file) |
| | if n == 1: |
| | print( |
| | """\ |
| | @cython.locals(x0=cython.double, y0=cython.double) |
| | @cython.locals(x1=cython.double, y1=cython.double) |
| | def _lineTo(self, p1): |
| | x0,y0 = self._getCurrentPoint() |
| | x1,y1 = p1 |
| | """, |
| | file=file, |
| | ) |
| | elif n == 2: |
| | print( |
| | """\ |
| | @cython.locals(x0=cython.double, y0=cython.double) |
| | @cython.locals(x1=cython.double, y1=cython.double) |
| | @cython.locals(x2=cython.double, y2=cython.double) |
| | def _qCurveToOne(self, p1, p2): |
| | x0,y0 = self._getCurrentPoint() |
| | x1,y1 = p1 |
| | x2,y2 = p2 |
| | """, |
| | file=file, |
| | ) |
| | elif n == 3: |
| | print( |
| | """\ |
| | @cython.locals(x0=cython.double, y0=cython.double) |
| | @cython.locals(x1=cython.double, y1=cython.double) |
| | @cython.locals(x2=cython.double, y2=cython.double) |
| | @cython.locals(x3=cython.double, y3=cython.double) |
| | def _curveToOne(self, p1, p2, p3): |
| | x0,y0 = self._getCurrentPoint() |
| | x1,y1 = p1 |
| | x2,y2 = p2 |
| | x3,y3 = p3 |
| | """, |
| | file=file, |
| | ) |
| | for name, value in defs: |
| | print(" %s = %s" % (name, value), file=file) |
| |
|
| | print(file=file) |
| | for name, value in zip([f[0] for f in funcs], exprs): |
| | print(" self.%s += %s" % (name, value), file=file) |
| |
|
| | print( |
| | """ |
| | if __name__ == '__main__': |
| | from fontTools.misc.symfont import x, y, printGreenPen |
| | printGreenPen('%s', [""" |
| | % penName, |
| | file=file, |
| | ) |
| | for name, f in funcs: |
| | print(" ('%s', %s)," % (name, str(f)), file=file) |
| | print(" ])", file=file) |
| |
|
| |
|
| | if __name__ == "__main__": |
| | pen = AreaPen() |
| | pen.moveTo((100, 100)) |
| | pen.lineTo((100, 200)) |
| | pen.lineTo((200, 200)) |
| | pen.curveTo((200, 250), (300, 300), (250, 350)) |
| | pen.lineTo((200, 100)) |
| | pen.closePath() |
| | print(pen.value) |
| |
|