The Midpoint Circle Algorithm

The Midpoint Circle Algorithm

🌐 Auf Deutsch lesen
📅 2026-07-05 ✍️ Andreas Wittmann 👁️ ... algorithm graphics computer-graphics

Bresenham's line algorithm draws a straight line on a pixel grid using only integer arithmetic, tracking an error term instead of a real-valued slope. The midpoint circle algorithm applies the same trick to circles: no floating point, no trigonometry, no square roots — just integer additions and a running decision variable.

The idea

A circle has a symmetry a line doesn't: reflecting a point across either axis, or swapping its x and y, gives another point on the same circle. So the algorithm only needs to compute the points for one 45° arc — say, from the top of the circle to the point where the arc crosses the diagonal — and can mirror each one into the other seven positions for free.

Along that one-eighth arc, x increases by exactly one pixel per step, same as the shallow-slope case in Bresenham's line algorithm, while y either stays the same or decreases by one. The question at each step is the same kind of question too: is the true circle closer to the current row or the one below it? The midpoint in the algorithm's name is the point exactly between those two candidate pixels — checking which side of the true circle that midpoint falls on decides which pixel to pick, and that check can be expressed with an integer decision variable d that only ever needs +, -, and comparisons against zero to update.

Implementation

#include "plot.hpp"

void plot_circle_points(int cx, int cy, int x, int y) {
    PlotPixel(cx + x, cy + y);
    PlotPixel(cx - x, cy + y);
    PlotPixel(cx + x, cy - y);
    PlotPixel(cx - x, cy - y);
    PlotPixel(cx + y, cy + x);
    PlotPixel(cx - y, cy + x);
    PlotPixel(cx + y, cy - x);
    PlotPixel(cx - y, cy - x);
}

void midpoint_circle(int cx, int cy, int r) {
    int x = 0;
    int y = r;
    int d = 1 - r;

    while (x <= y) {
        plot_circle_points(cx, cy, x, y);
        x++;
        if (d < 0) {
            d += 2 * x + 1;
        } else {
            y--;
            d += 2 * x - 2 * y + 1;
        }
    }
}

void frame() {
    PlotBackground(250, 250, 250);
    PlotColor(30, 100, 200);
    midpoint_circle(320, 240, 180);
}
Open in full editor →

plot_circle_points is the eightfold mirroring: given one point (x, y) relative to the center, it plots all eight symmetric positions at once. midpoint_circle is the loop that walks the one-eighth arc — d starts at 1 - r (the midpoint test for the very first step) and gets updated by either 2x + 1 or 2x - 2y + 1 depending on whether the midpoint fell inside or outside the true circle, exactly mirroring how Bresenham's line algorithm updates its error term by 2*dy or 2*dy - 2*dx. Edit the radius or center in the code above and press "Compile & Run" to see a different circle.

Walking through an example

For a circle of radius 5 centered at the origin, the loop starts at x = 0, y = 5, d = 1 - 5 = -4:

step plotted (this octant) d at decision branch updates
1 (0, 5) −4 (< 0) keep y x→1, d→ −4 + 2(1)+1 = −1
2 (1, 5) −1 (< 0) keep y x→2, d→ −1 + 2(2)+1 = 4
3 (2, 5) 4 (≥ 0) y→4 x→3, d→ 4 + 2(3)−2(4)+1 = 3
4 (3, 4) 3 (≥ 0) y→3 x→4, d→ 3 + 2(4)−2(3)+1 = 6
x=4 > y=3, loop ends

The one-eighth arc is (0,5) (1,5) (2,5) (3,4), ending right at the diagonal where x catches up to y. Mirroring those four points into all eight octants — swapping x/y and flipping signs — produces the full circle outline (with a couple of pixels landing on top of each other right at the axis crossings, where a mirrored point coincides with the original), all from four midpoint tests.

Why the same trick works twice

Both Bresenham's line algorithm and the midpoint circle algorithm reduce a real-valued question — "which pixel is the curve actually closest to?" — to the sign of an integer expression that can be updated incrementally instead of recomputed from scratch at every step. The line algorithm gets away with tracking one error term because a line has one slope for its entire length; the circle algorithm needs the eightfold symmetry on top, because a circle's slope keeps changing, but each of the eight mirrored octants shares the same shallow, monotonic shape that makes a single incrementally-updated decision variable enough.

The same midpoint idea extends further still — ellipses need two decision regions instead of one (the slope crosses the shallow/steep boundary at a different point than a circle's), but the underlying technique of comparing a real curve against an integer midpoint carries over directly.

Conclusion

Where Bresenham's algorithm turns "which row is closest" into one running comparison, the midpoint circle algorithm turns "which row is closest, on one-eighth of a circle" into the same kind of comparison, and then lets symmetry do the rest of the work for free. It's a good example of how far a little bit of geometry can stretch a technique originally built for a much simpler shape.