xref: /aosp_15_r20/external/fonttools/Lib/fontTools/pens/perimeterPen.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
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