Utility:Filling a vector object with colour

There doesn’t seem to be a fill routine in Codea that can fill vector drawn polygons, so I built one. Essentially, it fills your shape by creating an image (just once), which can then be drawn as many times as you like. You initialise it by passing an array of vectors and a color, and then just call its draw routine when you want to, so it’s pretty easy to use.

I’m sure the code can be improved, comments welcome. Note it can be easily adapted to fill any hollow shape/image.

--# Main
-- Fill

function setup()
    --set up an array of vectors which make up our shape
    v={vec2(0,0),vec2(100,0),vec2(150,75),vec2(80,200),vec2(40,50)}
    c=color(255,0,0,255) --fill color
    f=FillShape(v,c) --create filled object
end

function draw()
    background(128)
    f:draw(100,400) --draw filled object
    f:draw(400,600)
end

--# FillShape
FillShape = class()

--parameters are
--1. an array of 2D vectors making up the shape, the last one will be joined to the first automatically
--2. the fill color
--the result is an image that is only created once and can be drawn as many times as you like
function FillShape:init(v,c)
    self.maxx,self.maxy=0,0  --to store size of object
    --calculate x,y bounds
    self.n = #v
    for i=1,self.n do
        if v[i].x>self.maxx then self.maxx=v[i].x end
        if v[i].y>self.maxy then self.maxy=v[i].y end
    end
    --create image
    self.img=image(self.maxx,self.maxy)
    --tell Codea to draw onto our image instead of the screen
    setContext(self.img)
    --draw the shape on our image, and return a starting x,y point within the image
    local x,y=self:DrawShape(v,c)
    --fill the image
    self:DoFill(x,y,c) 
    --let Codea draw onthe screen again
    setContext()
end

--this function draws the shape
--you can change it any way you want as long as it draws a closed object
--and returns a starting x,y position within the object
function FillShape:DrawShape(v,c)
    pushStyle()
    stroke(c)
    strokeWidth(1)
    local x,y=0,0
    for i=1,self.n-1 do
        line(v[i].x,v[i].y,v[i+1].x,v[i+1].y)
        x=x+v[i].x --we'll return a starting x,y position of the average of all x and y values
        y=y+v[i].y
    end
    line(v[self.n].x,v[self.n].y,v[1].x,v[1].y) --join the last point to the first point
    popStyle()

    return x/self.n,y/self.n --return the average x and y position
end

function FillShape:draw(x,y)
    sprite(self.img,x,y)
end

--this is a recursive function. 
--Starting with the initial x,y position, it looks at all its neighbours, colouring any blanks, and
--running itself again for each neighbour that is blank
function FillShape:DoFill(x,y,c) 
    if x<1 or x>self.maxx or y<1 or y>self.maxy then return end --stop if out of bounds
    local r,g,b,a=self.img:get(x,y)
    if r+g+b==0 then --fill in blank cells
        self.img:set(x,y,c)
        self:DoFill(x-1,y,c) --run for all neighbours
        self:DoFill(x+1,y,c)
        self:DoFill(x,y-1,c)
        self:DoFill(x,y+1,c)
    end
end

Excellent @Ignatz! You are a coding machine gun!

I’m nearly retired, so I have more time than most :slight_smile:

Nice. you might be able to do something similar with a call to triangulate and use that as the vertices for a mesh, but your solution should work nice for any fillable shape, not just vectors.

I would have liked to use a callback to let the user draw the object on my image any way they want, but that makes it a little more complex to use.