Drawing Lines

This blog will cover common methods to drawing lines.To follow along, you should be familiar with Linear Equations, Slope intercept form, and Vectors. The code samples for this blog post are available here. These are my suggested additional resources on line drawing.

Troughout this blog we will implement three algorithms for line drawing. We will implement a floating point version of each, and an integer only version where appropriate. This is the test code that is run to make sure lines are being drawn properly:

double TestLine(fnDrawLine DrawLine, const char* output) {
    Image image(512, 512); // Makes new image, 512 x 512 in size
    
    for (int x = 0; x < image.width; ++x) {
        for (int y = 0; y < image.height; ++y) {
            PutPixel(image, x, y, Color(42, 42, 42));
        }
    }

    Color val(116, 116, 116);


    LARGE_INTEGER timerFrequency;
    QueryPerformanceFrequency(&timerFrequency);
    LARGE_INTEGER timerStart = { 0 };
    LARGE_INTEGER timerStop = { 0 };
    QueryPerformanceCounter(&timerStart);

    for (int angle = 0; angle < 360; angle += 1) {
        float radians = float(angle) * 0.01745329;
        float s = sinf(radians);
        float c = cosf(radians);

        if (angle % 10 == 0) {
            DrawLine(image, (int)(20.0f * s + 256.0f), (int)(20.0f * c + 256.0f), (int)(60.0f * s + 256.0f), (int)(60.0f * c + 256.0f), val);
        }
        if (angle % 7 == 0) {
            DrawLine(image, (int)(70.0f * s + 256.0f), (int)(70.0f * c + 256.0f), (int)(110.0f * s + 256.0f), (int)(110.0f * c + 256.0f), val);
        }
        if (angle % 4 == 0) {
            DrawLine(image, (int)(120.0f * s + 256.0f), (int)(120.0f * c + 256.0f), (int)(160.0f * s + 256.0f), (int)(160.0f * c + 256.0f), val);
        }
        if (angle % 2 == 0) {
            DrawLine(image, (int)(170.0f * s + 256.0f), (int)(170.0f * c + 256.0f), (int)(210.0f * s + 256.0f), (int)(210.0f * c + 256.0f), val);
        }
        DrawLine(image, (int)(220.0f * s + 256.0f), (int)(220.0f * c + 256.0f), (int)(250.0f * s + 256.0f), (int)(250.0f * c + 256.0f), val);
    }

    DrawLine(image, 256 - 15, 256, 256 + 15, 256, val);
    DrawLine(image, 256, 256 - 15, 256, 256 + 15, val);

    QueryPerformanceCounter(&timerStop);

    LONGLONG timerDiff = timerStop.QuadPart - timerStart.QuadPart;
    double ms = (double)timerDiff * 1000.0 / (double)timerFrequency.QuadPart;

    stbi_write_png(output, image.width, image.height, 1, image.data, 0);

    return ms;
}

It produces consecutive rings of lines, like shown below:

Successfull Test

A line can be categorized into one of the following eight octants based on its start and end points. The y axis in the image below increases going up, for most graphics API's y increases going down.

Line octants

Algorithms to draw lines are often presented only for one of the eight octants. With minimal code modification, the line can be mirrored into the other octants. This blog will also present some algorithms in a few octants at first, but will also present the code to handle all octants.