Internally, Easel represents many types of 2D shapes using Bézier paths and performs many operations on these curves as you work on your project. One frequent operation is calculating the bounding box of a path – and we recently sped up this calculation tenfold.

Easel used to do this by taking the path…

…then sampling a bunch of points on it…

…and then looking at all of those points to find the min and max X and Y values:

We were unhappy with this for two reasons:

1. The result was an approximation limited by the number of samples.
2. For complex paths, it required calculating many thousands of sampled points – so it was slow.

To build a faster solution, we went back to the underlying Bézier path representation, which is a series of cubic Bézier curves, each defined by two endpoints () with two control points () in between:

Between each pair of endpoints, the X and Y values are defined by parametric equations with polynomial coefficients determined by the endpoints $(x_0, y_0)$ and $(x_3, y_3)$ and control points $(x_1, y_1)$ and $(x_2, y_2)$:

$x(t) = (-x_0 + 3 x_1 - 3 x_2 + x_3) \times t^3$ $+ (3 x_0 - 6 x_1 + 3 x_2) \times t^2 + (-3 x_0 + 3 x_1) \times t + x_0$

$y(t) = (-y_0 + 3 y_1 - 3 y_2 + y_3) \times t^3$ $+ (3 y_0 - 6 y_1 + 3 y_2) \times t^2 + (-3 y_0 + 3 y_1) \times t + y_0$

Some high school calculus lets us know any points where the X () or Y () derivatives are zero – which is where those functions have their minimum and maximum values:

Those points, and the original endpoints, are the only points we need to consider to find the bounding box for the path:

And not only is it the precise bounding box, but we found it while evaluating far fewer points!