There are so many issues when managing arcs, but @chris answer above made sense to me.
I've added a counter-clockwise argument to the bounds and corrected an error in one of the arrays. The function has minor fixes and has been battle tested in a CAD app. Works great.
Below is a clip from an Arc class with start and end angles (sa, ea) and ccw property. Start and end angles are simply reversed for ccw arcs.
bounds():Segment {
this.normalizeAngles();
const start = this.ccw ? this.ea : this.sa;
const end = this.ccw ? this.sa : this.ea;
const iniQuad = this.getQuadrant(start);
const endQuad = this.getQuadrant(end);
const r = this.r;
const ix = Math.cos(start) * this.r;
const iy = Math.sin(start) * this.r;
const ex = Math.cos(end) * this.r;
const ey = Math.sin(end) * this.r;
const minX = Math.min(ix, ex);
const minY = Math.min(iy, ey);
const maxX = Math.max(ix, ex);
const maxY = Math.max(iy, ey);
const xMax = [[maxX, r, r, r], [maxX, maxX, r, r], [maxX, maxX, maxX, r], [maxX, maxX, maxX, maxX]];
const yMax = [[maxY, maxY, maxY, maxY], [r, maxY, r, r], [r, maxY, maxY, r], [r, maxY, maxY, maxY]];
const xMin = [[minX, -r, minX, minX], [minX, minX, minX, minX], [-r, -r, minX, -r], [-r, -r, minX, minX]];
const yMin = [[minY, -r, -r, minY], [minY, minY, -r, minY], [minY, minY, minY, minY], [-r, -r, -r, minY]];
const x1 = xMin[endQuad][iniQuad];
const y1 = yMin[endQuad][iniQuad];
const x2 = xMax[endQuad][iniQuad];
const y2 = yMax[endQuad][iniQuad];
return new Segment(
new Point(this.c.x + x1, this.c.y + y1),
new Point(this.c.x + x2, this.c.y + y2)
);
}
private getQuadrant(angle:number) {
const PI = Math.PI;
const HALF_PI = Math.PI / 2;
const TWO_PI = Math.PI * 2;
if (angle > TWO_PI) angle = angle % TWO_PI;
if (angle >= 0 && angle < HALF_PI) return 0;
if (angle >= HALF_PI && angle < PI) return 1;
if (angle >= PI && angle < (PI + HALF_PI)) return 2;
return 3;
};
normalizeAngles() {
this.sa = this.normalizeAngle(this.sa);
this.ea = this.normalizeAngle(this.ea);
if (this.sa >= this.ea) this.ea += Math.PI * 2;
}
normalizeAngle(angle:number):number {
let remainder = angle % (2 * Math.PI);
if (remainder < 0) remainder += 2 * Math.PI;
return remainder;
}