1*e1fe3e4aSElliott Hughes# -*- coding: utf-8 -*- 2*e1fe3e4aSElliott Hughes"""Calculate the perimeter of a glyph.""" 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughesfrom fontTools.pens.basePen import BasePen 5*e1fe3e4aSElliott Hughesfrom fontTools.misc.bezierTools import ( 6*e1fe3e4aSElliott Hughes approximateQuadraticArcLengthC, 7*e1fe3e4aSElliott Hughes calcQuadraticArcLengthC, 8*e1fe3e4aSElliott Hughes approximateCubicArcLengthC, 9*e1fe3e4aSElliott Hughes calcCubicArcLengthC, 10*e1fe3e4aSElliott Hughes) 11*e1fe3e4aSElliott Hughesimport math 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughes 14*e1fe3e4aSElliott Hughes__all__ = ["PerimeterPen"] 15*e1fe3e4aSElliott Hughes 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughesdef _distance(p0, p1): 18*e1fe3e4aSElliott Hughes return math.hypot(p0[0] - p1[0], p0[1] - p1[1]) 19*e1fe3e4aSElliott Hughes 20*e1fe3e4aSElliott Hughes 21*e1fe3e4aSElliott Hughesclass PerimeterPen(BasePen): 22*e1fe3e4aSElliott Hughes def __init__(self, glyphset=None, tolerance=0.005): 23*e1fe3e4aSElliott Hughes BasePen.__init__(self, glyphset) 24*e1fe3e4aSElliott Hughes self.value = 0 25*e1fe3e4aSElliott Hughes self.tolerance = tolerance 26*e1fe3e4aSElliott Hughes 27*e1fe3e4aSElliott Hughes # Choose which algorithm to use for quadratic and for cubic. 28*e1fe3e4aSElliott Hughes # Quadrature is faster but has fixed error characteristic with no strong 29*e1fe3e4aSElliott Hughes # error bound. The cutoff points are derived empirically. 30*e1fe3e4aSElliott Hughes self._addCubic = ( 31*e1fe3e4aSElliott Hughes self._addCubicQuadrature if tolerance >= 0.0015 else self._addCubicRecursive 32*e1fe3e4aSElliott Hughes ) 33*e1fe3e4aSElliott Hughes self._addQuadratic = ( 34*e1fe3e4aSElliott Hughes self._addQuadraticQuadrature 35*e1fe3e4aSElliott Hughes if tolerance >= 0.00075 36*e1fe3e4aSElliott Hughes else self._addQuadraticExact 37*e1fe3e4aSElliott Hughes ) 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes def _moveTo(self, p0): 40*e1fe3e4aSElliott Hughes self.__startPoint = p0 41*e1fe3e4aSElliott Hughes 42*e1fe3e4aSElliott Hughes def _closePath(self): 43*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 44*e1fe3e4aSElliott Hughes if p0 != self.__startPoint: 45*e1fe3e4aSElliott Hughes self._lineTo(self.__startPoint) 46*e1fe3e4aSElliott Hughes 47*e1fe3e4aSElliott Hughes def _lineTo(self, p1): 48*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 49*e1fe3e4aSElliott Hughes self.value += _distance(p0, p1) 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughes def _addQuadraticExact(self, c0, c1, c2): 52*e1fe3e4aSElliott Hughes self.value += calcQuadraticArcLengthC(c0, c1, c2) 53*e1fe3e4aSElliott Hughes 54*e1fe3e4aSElliott Hughes def _addQuadraticQuadrature(self, c0, c1, c2): 55*e1fe3e4aSElliott Hughes self.value += approximateQuadraticArcLengthC(c0, c1, c2) 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes def _qCurveToOne(self, p1, p2): 58*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 59*e1fe3e4aSElliott Hughes self._addQuadratic(complex(*p0), complex(*p1), complex(*p2)) 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughes def _addCubicRecursive(self, c0, c1, c2, c3): 62*e1fe3e4aSElliott Hughes self.value += calcCubicArcLengthC(c0, c1, c2, c3, self.tolerance) 63*e1fe3e4aSElliott Hughes 64*e1fe3e4aSElliott Hughes def _addCubicQuadrature(self, c0, c1, c2, c3): 65*e1fe3e4aSElliott Hughes self.value += approximateCubicArcLengthC(c0, c1, c2, c3) 66*e1fe3e4aSElliott Hughes 67*e1fe3e4aSElliott Hughes def _curveToOne(self, p1, p2, p3): 68*e1fe3e4aSElliott Hughes p0 = self._getCurrentPoint() 69*e1fe3e4aSElliott Hughes self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3)) 70