perimeterPen.py 2.1 KB

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