diff --git a/Lib/fontTools/pens/areaPen.py b/Lib/fontTools/pens/areaPen.py index 610140fb5..da7319f07 100644 --- a/Lib/fontTools/pens/areaPen.py +++ b/Lib/fontTools/pens/areaPen.py @@ -5,29 +5,6 @@ from fontTools.misc.py23 import * from fontTools.pens.basePen import BasePen -def polygon_area(p0, p1): - return -(p1[0] - p0[0]) * (p1[1] + p0[1]) * 0.5 - - -def quadratic_curve_area(p0, p1, p2): - x0, y0 = p0[0], p0[1] - x1, y1 = p1[0] - x0, p1[1] - y0 - x2, y2 = p2[0] - x0, p2[1] - y0 - return (x1*y2 - x2*y1) / 3 - - -def cubic_curve_area(p0, p1, p2, p3): - x0, y0 = p0[0], p0[1] - x1, y1 = p1[0] - x0, p1[1] - y0 - x2, y2 = p2[0] - x0, p2[1] - y0 - x3, y3 = p3[0] - x0, p3[1] - y0 - return -( - x1 * ( - y2 - y3) + - x2 * (y1 - 2*y3) + - x3 * (y1 + 2*y2 ) - ) * 0.15 - - class AreaPen(BasePen): def __init__(self, glyphset=None): @@ -35,23 +12,56 @@ class AreaPen(BasePen): self.value = 0 def _moveTo(self, p0): - self.__startPoint = p0 + """Remember the first point in this contour, in case it's closed. Also + set the initial value for p0 in this contour, which will always refer to + the most recent point. + """ + self._p0 = self._startPoint = p0 def _lineTo(self, p1): - p0 = self._getCurrentPoint() - self.value += polygon_area(p0, p1) - - def _curveToOne(self, p1, p2, p3): - p0 = self._getCurrentPoint() - self.value += cubic_curve_area(p0, p1, p2, p3) - self.value += polygon_area(p0, p3) + """Add the signed area beneath the line from the latest point to this + one. Signed areas cancel each other based on the horizontal direction of + the line. + """ + x0, y0 = self._p0 + x1, y1 = p1 + self.value -= (x1 - x0) * (y1 + y0) * .5 + self._p0 = p1 def _qCurveToOne(self, p1, p2): - p0 = self._getCurrentPoint() - self.value += quadratic_curve_area(p0, p1, p2) - self.value += polygon_area(p0, p2) + """Add the signed area of this quadratic curve. + https://github.com/Pomax/bezierinfo/issues/44 + """ + p0 = self._p0 + x0, y0 = p0[0], p0[1] + x1, y1 = p1[0] - x0, p1[1] - y0 + x2, y2 = p2[0] - x0, p2[1] - y0 + self.value -= (x2 * y1 - x1 * y2) / 3 + self._lineTo(p2) + self._p0 = p2 + + def _curveToOne(self, p1, p2, p3): + """Add the signed area of this cubic curve. + https://github.com/Pomax/bezierinfo/issues/44 + """ + p0 = self._p0 + x0, y0 = p0[0], p0[1] + x1, y1 = p1[0] - x0, p1[1] - y0 + x2, y2 = p2[0] - x0, p2[1] - y0 + x3, y3 = p3[0] - x0, p3[1] - y0 + self.value -= ( + x1 * ( - y2 - y3) + + x2 * (y1 - 2*y3) + + x3 * (y1 + 2*y2 ) + ) * 0.15 + self._lineTo(p3) + self._p0 = p3 def _closePath(self): - p0 = self._getCurrentPoint() - if p0 != self.__startPoint: - self.value += polygon_area(p0, self.__startPoint) + """Add the area beneath this contour's closing line.""" + self._lineTo(self._startPoint) + del self._p0, self._startPoint + + def _endPath(self): + """Area is not defined for open contours.""" + raise NotImplementedError