blade
blade

Reputation: 12884

Algorithm for drawing line with thickness / width

I'm looking for a fast algorithm that draws lines with a certain thickness. The lines don't have to be antialiased, speed is priority. Something fairly simple like this would suffice:

The use case is a Javascript game where worms leave trails. (HTML5 Canvas obviously draws lines, but getImageData() is very slow and so is collision detection)

I couldn't find anything that does this for the last 2.5 hours. And yes, I'm aware that there are almost identical questions on SO, quite a lot of them in fact, but not a single one has a working solution. The only solution I currently have is to draw circles along a Bresenham line, which is not very efficient.

Some code (pseudo-code, JS or at least a link to an article) would be great.

Upvotes: 11

Views: 8393

Answers (1)

g023
g023

Reputation: 857

http://members.chello.at/~easyfilter/bresenham.html

check at the bottom. It is an anti-aliased line, but should be easy enough to mod to non-anti-aliasing.

edit: I've added a simple function to do this that uses putImageData to display the "pixel" vs fillRect which should speed up the function on longer lines.

<html>
<head>
    <title>An (Optimal?) Bresenham Line Function in Javascript and HTML Canvas</title>
    <script>
        // using imagedata for pixel, because fillRect can be slow on larger lines
        // imagedata will be slower on shorter lines
        function drawLine(ctx, x0, y0, x1, y1, color_r, color_g, color_b, opacity) {
        // Calculate differences and direction to step in each axis
        const dx = Math.abs(x1 - x0);
        const dy = Math.abs(y1 - y0);
        const sx = x0 < x1 ? 1 : -1;
        const sy = y0 < y1 ? 1 : -1;
        
        // Initialize error term and starting point
        let err = dx - dy;
        let x = x0;
        let y = y0;
        
        // Create a new image data object to hold the pixels for the line
        const imageData = ctx.createImageData(1, 1);
        const data = imageData.data;
        
        data[0] = color_r; // R
            data[1] = color_g; // G
            data[2] = color_b; // B
        
        data[3] = opacity; // A (opacity)
        
        // Loop over line until endpoint is reached
        while (x !== x1 || y !== y1) {
            // Set pixel color in image data
            ctx.putImageData(imageData, x, y);
        
            // Calculate error term and update current point
            const e2 = err * 2;
            if (e2 > -dy) {
            err -= dy;
            x += sx;
            }
            if (e2 < dx) {
            err += dx;
            y += sy;
            }
        }
        
        // Set pixel color in image data for endpoint
        ctx.putImageData(imageData, x1, y1);
        }
        
        </script>        
</head>

<body>
    <canvas id="canvas" width="200" height="200"></canvas>

    <script>
        // sample use our line function
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        drawLine(ctx, 0, 0, 200, 200, 255, 0, 0, 255);
        drawLine(ctx, 0, 200, 200, 0, 0, 255, 0, 255);
        drawLine(ctx, 0, 100, 200, 100, 0, 0, 255, 255);
        drawLine(ctx, 100, 0, 100, 200, 255, 255, 0, 255);
    </script>
</body>
</html>

Upvotes: 15

Related Questions