Cristian Molina

Posted on

Basic painting with Ruby: implementing a flood-fill algorithm with DragonRuby GTK

Continuing with my learning and experimentation with DRGTK, I wanted to implement a flood-fill algorithm on a canvas with some drawings.

This idea came from an old interview exercise I did some years ago.

The Flood Fill algorithm is commonly used in graphics software to fill an area with a specific color. It’s what powers the paint bucket tool.

So having a canvas and a pair of coordinates on that, the initial color is the one located in this location and the plan is to change it to another color, filling all adjacent pixels with the original color with the new one until a different one from the original one is reached.
My implementation is a recursive one:

`````` def paint(x:, y:, base_color:, new_color:, sleep_time: 0)
return if new_color == base_color

coords_inside_matrix = y >= 0 && y < map.size && x >= 0 && x < map[0].size

pixel_should_be_painted = coords_inside_matrix && map[y][x] == base_color

return unless pixel_should_be_painted

map[y][x] = new_color  # paint the cell

paint(x: x+1, y: y, base_color: base_color, new_color: new_color)
paint(x: x, y: y+1, base_color: base_color, new_color: new_color)
paint(x: x-1, y: y, base_color: base_color, new_color: new_color)
paint(x: x, y: y-1, base_color: base_color, new_color: new_color)
end
``````

I worked on my solution using TDD, with Minitest spec tests, so it was easier to start with basic scenarios and also printed it while it moved the brush position recursively.

``````# Running:

paint(2-1)
changing x:2 y:1
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 2, 3, 8]
[2, 2, 2, 0, 0]
[0, 0, 2, 0, 5]
paint(2-2)
changing x:2 y:2
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 2, 0, 0]
[0, 0, 2, 0, 5]
paint(2-3)
changing x:2 y:3
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 8, 0, 0]
[0, 0, 2, 0, 5]
paint(2-4)
changing x:2 y:4
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 8, 0, 0]
[0, 0, 8, 0, 5]
``````

On the Dragon Ruby side, I drew the matrix as a canvas and queried the mouse functions to get the cell position to paint or to change the color of the brush by clicking on a color palette box.
There is no pencil functionality so the canvas starts with a default drawing to play with it:

...and that's basically it. Not sure what will be next on this series. There is a game jam with Dragon Ruby starting soon, maybe I could participate on it :) .

Links to the app and code:

Happy hacking!

Amir Rajan

If you’re rendering a lot of solids, use the sprites output. It’s significantly faster because of texture caching.

``````args.outputs.sprites << {
x: 0,
y: 0,
w: 32,
h: 32,
path: :solid,
r: 0,
g: 0,
b: 255
}
``````