Computation of minimal axis-aligned bounding box of an arc segment.

570 Views Asked by At

I'm trying to compute the minimal bounding box of an arc segment so when it's time to render it, I only have to examine pixel coordinates within a minimal rectangular region. The code below covers the case when $0 \leq \theta_0 \lt \pi/2$. Was wondering if there is some sort of rotational symmetry so that I don't need 4x the amount of code statements that the box() function already is (1 for each quadrant where $\theta_0$ could be. The prog language is D.

struct Arc {
protected:
Vec2 c;
float r0, r1;
float t0, t1;

invariant() {
    assert(r0 < r1 && t0 < t1);
}

public:
this(in Vec2 center, float radius0, float radius1,
     float theta0, float theta1) 
{
    c = center;
    r0 = radius0;
    r1 = radius1;
    t0 = theta0;
    t1 = theta1;
}

@property Box2 box() {
    double minX, minY, maxX, maxY;

    if (theta0 >= 0) {
        if (theta0 < PI/2) {
            if (theta1 < 2*PI - theta0)
                maxX = r1 * cos(theta0);
            else
                maxX = r1 * cos(theta1);

            if (theta1 <= PI/2) {
                maxY = r1 * sin(theta1);
                minY = r0 * sin(theta0);
                minX = r0 * cos(theta1);
            }
            else {
                maxY = r1;
                if (theta1 <= PI) {
                    if (theta1 - PI/2 > PI/2 - theta0)
                        minY = r0 * sin(PI - theta1);
                    else
                        minY = r0 * sin(theta0);
                    minX = r1 * cos(PI - theta1);
                }
                else {
                    minX = -r1;
                    if (theta1 <= 3*PI/4) 
                        minY = r1 * sin(theta1);
                    else    // 2PI >= theta1 > 3*PI/4 
                        minY = -r1;
                }
            }
        }
    }
}
}

My guess is is that you compute which quadrant $\theta_0$ is in, then rotate the whole thing into quadrant 1 which I already have code for (rotate by either $\pi/2, \pi,$ or $3\pi/2$), compute the bounding box in that quadrant, then rotate the bounding box by the same angle, negated. Would this work? I think I will need to open up Geogebra and draw some test images to find out.

1

There are 1 best solutions below

0
On BEST ANSWER

Since I'm only rotating by multiples of $90$ degrees, this should work. In other words, the quadrant 1-computed bounding box, rotated back $k\cdot 90$ degrees, is still axis-aligned, and obviously still minimal. And every $\theta_0$ can be brought into quadrant 1 by a multiple-of-$90$-degrees rotation.

module bud.arc;

import bud.seeds;
import bud.leaves;

class Arc : Leaf {
protected:
Vec2 c;
float r0, r1;
float t0, t1;

invariant() {
    assert(r0 < r1 && t0 < t1 && 0 <= t0 && t0 <= 2*PI && 0 <= t1 && t1 <= 2*PI);
}

public:
// theta0, theta1 must be in [0, 2PI]
this(in Vec2 center, float radius0, float radius1,
     float theta0, float theta1) 
{
    c = center;
    r0 = radius0;
    r1 = radius1;
    t0 = theta0;
    t1 = theta1;
}

@property Box2 box() {
    int k;

    if (t0 < PI/2)
        k = 0;
    else if (t0 < PI)
        k = 1;
    else if (t0 < 3*PI/2)
        k = 2;
    else    // must be in last quadrant
        k = 3;

    float dt = k * PI/2;
    Box2 b = box_with_t0_in_quad1(t0 - dt, t1 - dt);
    b.rotate90(k);      // rotate back an integer multiple of 90 degrees
    b.ctr = c;
    return b;
}

protected:
Box2 box_with_t0_in_quad1(float t0, float t1) 
in {
    assert(0 <= t0 && t0 < PI/2);
} body {
    double minX, minY, maxX, maxY;

    if (t1 < 2*PI - t0)
        maxX = r1 * cos(t0);
    else
        maxX = r1 * cos(t1);

    if (t1 <= PI/2) {
        maxY = r1 * sin(t1);
        minY = r0 * sin(t0);
        minX = r0 * cos(t1);
    }
    else {
        maxY = r1;
        if (t1 <= PI) {
            if (t1 - PI/2 > PI/2 - t0)
                minY = r0 * sin(PI - t1);
            else
                minY = r0 * sin(t0);
            minX = r1 * cos(PI - t1);
        }
        else {
            if (t1 <= 3*PI/4) 
                minY = r1 * sin(t1);
            else    // 2PI >= theta1 > 3*PI/4 
                minY = -r1;
            minX = -r1;
        }
    }

    // Return untranslated by center
    return Box2([minX, maxY], [maxX, minY]);        // coordinates on Y-axis are reversed in pixel rendering coords
}
}