from fontTools.pens.basePen import BasePen
from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds


__all__ = ["BoundsPen", "ControlBoundsPen"]


class ControlBoundsPen(BasePen):

	"""Pen to calculate the "control bounds" of a shape. This is the
	bounding box of all control points, so may be larger than the
	actual bounding box if there are curves that don't have points
	on their extremes.

	When the shape has been drawn, the bounds are available as the
	'bounds' attribute of the pen object. It's a 4-tuple:
		(xMin, yMin, xMax, yMax)
	"""

	def __init__(self, glyphSet):
		BasePen.__init__(self, glyphSet)
		self.bounds = None

	def _moveTo(self, pt):
		bounds = self.bounds
		if bounds:
			self.bounds = updateBounds(bounds, pt)
		else:
			x, y = pt
			self.bounds = (x, y, x, y)

	def _lineTo(self, pt):
		self.bounds = updateBounds(self.bounds, pt)

	def _curveToOne(self, bcp1, bcp2, pt):
		bounds = self.bounds
		bounds = updateBounds(bounds, bcp1)
		bounds = updateBounds(bounds, bcp2)
		bounds = updateBounds(bounds, pt)
		self.bounds = bounds

	def _qCurveToOne(self, bcp, pt):
		bounds = self.bounds
		bounds = updateBounds(bounds, bcp)
		bounds = updateBounds(bounds, pt)
		self.bounds = bounds


class BoundsPen(ControlBoundsPen):

	"""Pen to calculate the bounds of a shape. It calculates the
	correct bounds even when the shape contains curves that don't
	have points on their extremes. This is somewhat slower to compute
	than the "control bounds".

	When the shape has been drawn, the bounds are available as the
	'bounds' attribute of the pen object. It's a 4-tuple:
		(xMin, yMin, xMax, yMax)
	"""

	def _curveToOne(self, bcp1, bcp2, pt):
		bounds = self.bounds
		bounds = updateBounds(bounds, pt)
		if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
			bounds = unionRect(bounds, calcCubicBounds(
					self._getCurrentPoint(), bcp1, bcp2, pt))
		self.bounds = bounds

	def _qCurveToOne(self, bcp, pt):
		bounds = self.bounds
		bounds = updateBounds(bounds, pt)
		if not pointInRect(bcp, bounds):
			bounds = unionRect(bounds, calcQuadraticBounds(
					self._getCurrentPoint(), bcp, pt))
		self.bounds = bounds


if __name__ == "__main__":
	def draw(pen):
		pen.moveTo((0, 0))
		pen.lineTo((0, 100))
		pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0))
		pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100))
		pen.closePath()

	pen = ControlBoundsPen(None)
	draw(pen)
	print pen.bounds

	pen = BoundsPen(None)
	draw(pen)
	print pen.bounds
