Rotating Calipers

Sweep a pair of parallel lines around a convex hull, keeping them tangent to it. Because both contact points move monotonically, a full rotation costs total — a two-pointer walk around the hull.

Diameter (farthest pair) — the canonical use

ll hullDiameter2(const vector<P>& h) {                // squared, exact
    int n = h.size();
    if (n == 1) return 0;
    if (n == 2) return norm2(h[1] - h[0]);
    ll best = 0;
    for (int i = 0, j = 1; i < n; i++) {
        // advance j while the area of the triangle grows
        while (cross(h[(i + 1) % n] - h[i], h[(j + 1) % n] - h[j]) > 0) j = (j + 1) % n;
        best = max({best, norm2(h[j] - h[i]), norm2(h[(i + 1) % n] - h[j])});
    }
    return best;
}

The cross(...) > 0 test asks “does rotating the caliper to the next edge of still leave behind?” — the two-pointer invariant. Each pointer advances at most times in total around the whole loop, so it is , not .

The diameter of a point set is the diameter of its hull, so the full algorithm is (hull) (calipers).

Tested against brute-force all-pairs distance on random point sets (hull first), including collinear and duplicate-point degenerate inputs.

What calipers compute

QuantityMethod
Diameter (farthest pair)as above
Width (minimum-width strip)minimise the distance from each edge to the farthest vertex
Minimum-area enclosing rectangleone side lies on a hull edge (Toussaint); track the four extreme points
Minimum-perimeter enclosing rectanglesame sweep, different objective
Maximum-area inscribed triangletwo nested pointers,
Distance between two convex polygonswalk both simultaneously, or [[#Convex Distance
Closest pair of two convex polygonssame
Common tangents of two hullssame
Merging two hullsfind the upper and lower tangents
All antipodal pairsthe calipers enumerate exactly these, of them

Minimum enclosing rectangle

Toussaint’s theorem: the minimum-area rectangle enclosing a convex polygon has one side collinear with a polygon edge.

So iterate over the edges, and for each maintain (by rotating calipers) the extreme vertex in the perpendicular direction and in both parallel directions. total.

The general principle

Whenever a quantity depends on two points of a convex object and the optimum’s second point moves monotonically as the first one does, a two-pointer walk works. Recognising that monotonicity is the whole insight — the “rotating calipers” name just describes the picture.

The same monotonicity underlies the convex hull trick with monotone queries, D&C DP (monotone argmin), and ordinary two-pointer techniques on sorted arrays.

Antipodal pairs

A pair of hull vertices is antipodal if there exist parallel supporting lines through both. A convex polygon with vertices has antipodal pairs, and the calipers enumerate all of them — which is why the diameter is rather than .

Degenerate cases

Collinear points on the hull

If your hull retains collinear boundary points, the cross(...) > 0 test can stall (the cross product is 0 forever, since consecutive “edges” with a collinear middle vertex are parallel). Either build the strict hull (keepCollinear=false in monotone chain), or change the test to >= 0 with a step counter to guarantee termination.

Also handle and separately, as in the code above.

When it does not apply

Calipers need convexity. For a non-convex polygon the farthest pair is still on the hull (so take the hull first), but “width”, “minimum enclosing rectangle” and distance queries about the polygon itself are different problems.

Bounding Rectangle

Smallest-area and smallest-perimeter rectangle (any orientation) enclosing a convex polygon, and whether a rectangle fits inside an one.

pair<double, double> minBoundingRectangle(const vector<P>& h) {   // {area, perimeter}
    int n = h.size();
    if (n <= 2) { double len = n == 2 ? dist(h[0], h[1]) : 0; return {0, 2 * len}; }
    double bestArea = 1e18, bestPer = 1e18;
    int right = 0, far = 0, left = 0;
    for (int i = 0; i < n; i++) {
        PD a = toPD(h[i]), b = toPD(h[(i + 1) % n]);
        double len = abs_(b - a);
        PD d = (b - a) * (1.0 / len);                              // unit direction of this edge
        auto along = [&](int k) { return dot(toPD(h[k % n]) - a, d); };
        auto height = [&](int k) { return cross(d, toPD(h[k % n]) - a); };
        if (i == 0) {
            for (int k = 0; k < n; k++) {
                if (along(k) > along(right)) right = k;
                if (height(k) > height(far)) far = k;
                if (along(k) < along(left)) left = k;
            }
        } else {
            const double eps = 1e-9;
            while (along(right + 1) > along(right) + eps) right++;
            while (height(far + 1) > height(far) + eps) far++;
            while (along(left + 1) < along(left) - eps) left++;
        }
        double w = along(right) - along(left), ht = height(far);
        bestArea = min(bestArea, w * ht);
        bestPer = min(bestPer, 2 * (w + ht));
    }
    return {bestArea, bestPer};
}
 
bool rectangleFits(double p, double q, double a, double b) {        // can a p x q rectangle fit in an a x b one?
    if (p < q) swap(p, q);
    if (a < b) swap(a, b);
    if (p <= a && q <= b) return true;                              // axis-aligned fit
    if (q > b) return false;
    double s = p * p + q * q;
    if (s < a * a) return false;
    return b >= (2 * p * q * a + (p * p - q * q) * sqrt(s - a * a)) / s;   // tilted fit, Carver 1956
}

Complexity: rotating calipers for minBoundingRectangle; for rectangleFits. An optimal enclosing rectangle has a side on a hull edge (Toussaint), so for every edge, three calipers track: the farthest point along the edge direction, the farthest perpendicular, the farthest against the edge direction.

Convex Distance

Minimum distance between two convex polygons (0 if they intersect), via the Minkowski difference.

double convexPolygonDistance(vector<P> p, vector<P> q) {  // both CCW, strictly convex
    auto rotateToLowest = [](vector<P>& v) {
        int lo = min_element(v.begin(), v.end(), [](P a, P b) {
                      return a.y != b.y ? a.y < b.y : a.x < b.x;
                  }) - v.begin();
        rotate(v.begin(), v.begin() + lo, v.end());
    };
    vector<P> negQ;
    for (auto& v : q) negQ.push_back(v * (-1));          // -Q of a CCW polygon is still CCW (180 degree rotation)
    rotateToLowest(p);
    rotateToLowest(negQ);                                 // minkowski requires both inputs to start at their lowest point
    vector<P> s = minkowski(p, negQ);                     // from Minkowski Sum
    int k = s.size();
    if (k == 1) return dist(s[0], P{0, 0});
    bool inside = true;
    for (int i = 0; i < k; i++) if (cross(s[i], s[(i + 1) % k]) < 0) inside = false;  // origin on the right of some edge
    if (inside) return 0;
    double best = 1e18;
    for (int i = 0; i < k; i++) best = min(best, distToSegment(P{0, 0}, s[i], s[(i + 1) % k]));
    return best;
}

Complexity: . distance from the origin to (see Minkowski Sum for why): the Minkowski sum of two convex polygons is convex with at most vertices; the origin inside or on it means the polygons intersect, and otherwise the answer is the distance from the origin to the nearest edge — exactly `distToSegment` minimised over the sum’s edges. For a point and a polygon use ; for “do two convex polygons intersect”, compare the result with .

Tested against a brute-force scan (minimum over all pairs of edges, via segment-to-segment distance) on random convex polygon pairs, both disjoint and overlapping.