raw Software
RAW Software Graphics CAD and Parametric Modeling

Circular Sectors and Annular Arcs in OpenSCAD

Robert Eisele

A circular sector or annular arc can be built in OpenSCAD without subtracting oversized circles or clipping the result with arbitrary triangles. Constructing the boundary polygon directly works in every quadrant, handles sweeps across zero degrees, and keeps the geometry proportional to the requested radii.

// https://raw.org/software/graphics/circular-sector-and-arcs-with-openscad/

function normalized_angle(angle) = (angle % 360 + 360) % 360;

// Circular sector or annular arc.
// r1, r2: radii in either order
// a1, a2: start and end angles in degrees
// $fn: number of segments for a complete circle
module arc(r1, r2, a1, a2, $fn = 128) {
    assert(r1 >= 0 && r2 >= 0, "Radii must be non-negative.");

    inner_radius = min(r1, r2);
    outer_radius = max(r1, r2);
    assert(outer_radius > 0, "At least one radius must be positive.");

    start_angle = normalized_angle(a1);
    end_angle = normalized_angle(a2);
    raw_sweep = (end_angle - start_angle) % 360;
    sweep = raw_sweep < 0 ? raw_sweep + 360 : raw_sweep;

    if (sweep == 0) {
        difference() {
            circle(r = outer_radius, $fn = $fn);
            if (inner_radius > 0) {
                circle(r = inner_radius, $fn = $fn);
            }
        }
    } else {
        segments = max(3, ceil($fn * sweep / 360));
        outer_points = [
            for (index = [0:segments])
                let(angle = start_angle + sweep * index / segments)
                    [outer_radius * cos(angle), outer_radius * sin(angle)]
        ];
        inner_points = inner_radius == 0
            ? [[0, 0]]
            : [
                for (index = [0:segments])
                    let(angle = end_angle - sweep * index / segments)
                        [inner_radius * cos(angle), inner_radius * sin(angle)]
            ];

        polygon(points = concat(outer_points, inner_points));
    }
}

Extruding the Arc

The module returns two-dimensional geometry, so the same shape can become a solid with linear_extrude(). Passing $fn by name keeps the resolution setting consistent between both modules.

module arc3d(r1, r2, a1, a2, height, $fn = 128) {
    linear_extrude(height = height)
        arc(r1, r2, a1, a2, $fn = $fn);
}

arc3d(20, 10, 0, 45, 4, $fn = 96);

Examples

arc(20, 10, 0, 45);        // radii are accepted in either order
arc(150, 120, 0, 90);      // quarter of an annulus
arc(150, 120, 0, 170);     // crosses into another quadrant
arc(150, 120, 350, 10);    // 20-degree sweep across zero
arc(50, 0, 30, 120);       // solid circular sector
arc(50, 0, 0, 0);          // complete disk
arc(50, 40, 0, 0);         // complete ring

Angle and Resolution Semantics

OpenSCAD measures angles in degrees. The module follows the positive, counter-clockwise sweep from a1 to a2 after normalizing both angles to the range from 0 up to, but not including, 360 degrees. Reversing the two angles therefore selects the complementary sweep rather than the same shape in reverse.

Equal normalized start and end angles intentionally mean a complete disk or ring. Use a different end angle when an empty or partial sweep is required.

The special variable $fn controls how many segments would represent a complete circle. Partial arcs receive a proportional number of segments, with a minimum of three. Increase $fn for smoother exported meshes or reduce it for quicker previews.