blob: 4d14a0aec22b8dd93f563003f7272e02dad3992f [file] [log] [blame]
Behdad Esfahbod1ae29592014-01-14 15:07:50 +08001from __future__ import print_function, division, absolute_import
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05002from fontTools.misc.py23 import *
jvrb369ef32003-08-23 20:19:33 +00003from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
4from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05005from fontTools.pens.basePen import BasePen
jvrb369ef32003-08-23 20:19:33 +00006
7
8__all__ = ["BoundsPen", "ControlBoundsPen"]
9
10
11class ControlBoundsPen(BasePen):
12
13 """Pen to calculate the "control bounds" of a shape. This is the
14 bounding box of all control points, so may be larger than the
15 actual bounding box if there are curves that don't have points
16 on their extremes.
17
18 When the shape has been drawn, the bounds are available as the
19 'bounds' attribute of the pen object. It's a 4-tuple:
20 (xMin, yMin, xMax, yMax)
21 """
22
23 def __init__(self, glyphSet):
24 BasePen.__init__(self, glyphSet)
25 self.bounds = None
26
27 def _moveTo(self, pt):
28 bounds = self.bounds
29 if bounds:
30 self.bounds = updateBounds(bounds, pt)
31 else:
32 x, y = pt
33 self.bounds = (x, y, x, y)
34
35 def _lineTo(self, pt):
36 self.bounds = updateBounds(self.bounds, pt)
37
38 def _curveToOne(self, bcp1, bcp2, pt):
39 bounds = self.bounds
40 bounds = updateBounds(bounds, bcp1)
41 bounds = updateBounds(bounds, bcp2)
42 bounds = updateBounds(bounds, pt)
43 self.bounds = bounds
44
45 def _qCurveToOne(self, bcp, pt):
46 bounds = self.bounds
47 bounds = updateBounds(bounds, bcp)
48 bounds = updateBounds(bounds, pt)
49 self.bounds = bounds
50
51
52class BoundsPen(ControlBoundsPen):
53
54 """Pen to calculate the bounds of a shape. It calculates the
55 correct bounds even when the shape contains curves that don't
56 have points on their extremes. This is somewhat slower to compute
57 than the "control bounds".
58
59 When the shape has been drawn, the bounds are available as the
60 'bounds' attribute of the pen object. It's a 4-tuple:
61 (xMin, yMin, xMax, yMax)
62 """
63
64 def _curveToOne(self, bcp1, bcp2, pt):
65 bounds = self.bounds
66 bounds = updateBounds(bounds, pt)
67 if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
jvr82ef2a52003-08-23 20:24:42 +000068 bounds = unionRect(bounds, calcCubicBounds(
69 self._getCurrentPoint(), bcp1, bcp2, pt))
jvrb369ef32003-08-23 20:19:33 +000070 self.bounds = bounds
71
72 def _qCurveToOne(self, bcp, pt):
73 bounds = self.bounds
74 bounds = updateBounds(bounds, pt)
75 if not pointInRect(bcp, bounds):
jvr82ef2a52003-08-23 20:24:42 +000076 bounds = unionRect(bounds, calcQuadraticBounds(
77 self._getCurrentPoint(), bcp, pt))
jvrb369ef32003-08-23 20:19:33 +000078 self.bounds = bounds
79
80
81if __name__ == "__main__":
82 def draw(pen):
83 pen.moveTo((0, 0))
84 pen.lineTo((0, 100))
85 pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0))
86 pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100))
87 pen.closePath()
88
89 pen = ControlBoundsPen(None)
90 draw(pen)
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050091 print(pen.bounds)
jvrb369ef32003-08-23 20:19:33 +000092
93 pen = BoundsPen(None)
94 draw(pen)
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050095 print(pen.bounds)