Calculating the Bounding Rectangle of a Circular Sector

2 weeks ago 1

Sun
19
Oct 2025

This article will be short and straight to the point. While working with geometry in 2D, I was recently looking for an algorithm to calculate the bounding box of a specific shape that I initially called a "cone". Actually, as I'm talking about 2D, I should rather say I needed the bounding rectangle of a circular sector - a part of a circle with a limited angle around an axis pointing in a specific direction.

When developing a 2D game, this shape can represent, for example, the area of effect of an attack, such as punching nearby enemies, firing a shotgun, spraying some substance ahead, or casting a magical spell. Calculating its bounding rectangle can be useful for querying a space-partitioning data structure (like a grid, a quadtree, etc.) for potentially affected objects.

I prototyped my solution in ShaderToy, which you can see here: shadertoy.com/view/w3jcRw.

A circular sector is described by:

  • vec2 apex - the starting point and the center of the circle that this shape is part of
  • vec2 direction - a vector pointing in the direction of the axis (must be normalized)
  • float halfAngle - the angle between the axis and the edges, or half of the angle between the opposing edges (in radians, in range 0...π)
    • For 0, the shape becomes just a line segment.
    • For π, it becomes a full circle.
  • float radius - the radius of the circle that this shape is part of

The output bounding rectangle is described by just vec2 MinPos, MaxPox - two points defining the minimum and maximum coordinates it contains.

To calculate the bounding rectangle of our cone, we need to consider all possible points that extend the furthest along the X and Y axes, and take their min/max. The first such point is the apex. The next two are what I call "edge points."

However, there are cases where this is not enough. We also need to check four "extra points" located at a distance of radius from the apex along -X, +X, -Y, +Y, as long as each of these points belongs to the cone.

My final algorithm in GLSL is:

void CalcConeBoundingRect(vec2 apex, vec2 direction, float halfAngle, float radius, out vec2 boundingRectMinPos, out vec2 boundingRectMaxPos) { float sinHalfAngle = sin(halfAngle); float cosHalfAngle = cos(halfAngle); vec2 edgeVec1 = vec2( direction.x * cosHalfAngle - direction.y * sinHalfAngle, direction.y * cosHalfAngle + direction.x * sinHalfAngle); vec2 edgeVec2 = vec2( direction.x * cosHalfAngle + direction.y * sinHalfAngle, direction.y * cosHalfAngle - direction.x * sinHalfAngle); vec2 edgePoint1 = apex + edgeVec1 * radius; vec2 edgePoint2 = apex + edgeVec2 * radius; boundingRectMinPos = min(min(edgePoint1, edgePoint2), apex); boundingRectMaxPos = max(max(edgePoint1, edgePoint2), apex); vec2 unitVec[4] = vec2[]( vec2(-1.0, 0.0), vec2(1.0, 0.0), vec2(0.0, -1.0), vec2(0.0, 1.0)); for(int i = 0; i < 4; ++i) { if(dot(unitVec[i], direction) >= cosHalfAngle) { vec2 extraPoint = apex + unitVec[i] * radius; boundingRectMinPos = min(boundingRectMinPos, extraPoint); boundingRectMaxPos = max(boundingRectMaxPos, extraPoint); } } }

Note that we don't use raw angles here, apart from the initial parameter. We don't call the atan2 function, nor do we compare whether one angle is smaller than another. We simply operate on vectors - a common theme in well-designed geometric algorithms.

The algorithm can be optimized further if we store the sine and cosine of the angle in advance. Alternatively, if we have only one of them, we can compute the other using the formula below. This way, we never need to use the raw angle value at all.

float sinHalfAngle = sqrt(1.0 - cosHalfAngle * cosHalfAngle);

EDIT: Big thanks to Matthew Arcus for suggesting an improvement to the code! I applied it to the listing above.

Comments | #math Share

Read Entire Article