Any way to clip an image to a semicircle?

So I have a rounded button that consists of an circle, a rectangle, and another circle. The rectangle overlaps both circles at the halfway point so that it looks like it’s one piece…is there any (simple) way to create a mesh that would accommodate for this shape? Id like to overlay it with a semi-transparent vignette image for a gradient effect…thoughts, advice, any techniques?

Thanks guys!

You could try using a shader, or a blendmode. I think @Jmv38 found one that can make sprites clipped to another sprite’s outline.

@Invad3rZIM - The following code will use one image (img2) as a mask for another (img1), so it will draw the pixel from img2 wherever it finds a non blank pixel in img2

The setup function creates the combined image and stores it in an image that can then be sprited wherever you want.

Let me know if it isn’t clear.

function setup()
    local bw,bh=200,200 --size of button
    
    --the vignette image overlay
    img=readImage("Cargo Bot:Starry Background")
    --cut this to the same size as the button
    img1=img:copy(0,0,bw,bh)
    
    --this will provide the button outine on which to overlay the vignette
    img2=image(200,200)
    --create the shapes you want on this image
    setContext(img2)
    pushStyle()
    fill(0)
    ellipse(50,50,60)
    ellipse(150,150,60)
    rect(60,60,80,80)
    popStyle()
    setContext()
    
    --now create the mesh that does the overlay
    --don't touch anything except the image names
    local m=mesh()
    u=m:addRect(bw/2,bh/2,bw,bh)
    m:setRectTex(u,0,0,1,1)    
    m.shader = shader(stencilShader.vertexShader, stencilShader.fragmentShader)
    m.texture=img1  --<------------ vignette image
    m.shader.texture2=img2   ---<----- outline image
    --now draw everything onto an image we can sprite 
    --(rather than redoing all this drawing 60 times/sec)
    btnImg=image(img2.width,img2.height) 
    setContext(btnImg)
    m:draw()
    setContext()
end

function draw()
    background(200)
    sprite(btnImg,300,300) --masked button image
    sprite(img2,500,500)  --mask (just drawn to show we got it right)
end

stencilShader = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    vColor = color;
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
uniform lowp sampler2D texture2;
uniform vec4 mask;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    lowp vec4 col1 = texture2D( texture, vTexCoord );
    lowp vec4 col2 = texture2D( texture2, vTexCoord);
    if (col2.a>0.0)  gl_FragColor = col1; else discard; 
}
]]}

@ignatz…that’s brilliant…you’re brilliant.

@skythecoder I started to take your mesh suggestion but I have a bug…would you mind taking a curious look at it?

--# Button
Button = class()

function Button:init(x,y,w,h,d,s)
    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.s = s
    self.d = 1
    self.action = nil
    self.m = mesh()
    
 --   print(unpack(self:makeCenter()))
    self.m.vertices = {unpack(self:makeCenter())}

    self.m:setColors (255,5,255,255)
    self.m.texture = readImage("Character Horn Girl")
end

function Button:draw()
    -- Codea does not automatically call this method
    pushStyle()
    noStroke()
    fill(77, 146, 76, 255)
    
    
    rect(self.d * self.x -1, self.y-self.h/4, self.x +2, self.h/2)

    ellipse(self.x - self.w/2 + self.h/2,self.y - self.h/2 + self.h/2, self.h)
    ellipse(self.x + self.w/2 - self.h/2,self.y - self.h/2 + self.h/2, self.h)
    rect(self.x - self.w/2 + self.h/2 -.5, self.y - self.h/2 +.5, self.w -self.h+1 , self.h -1)

    fill(207, 120, 120, 255)
    rect(self.d * self.x, self.y-self.h/10*1.5, self.x , self.h/5*1.5)

    ellipse(self.x - self.w/2 + self.h/2,self.y - self.h/2 + self.h/2, self.h/4*3)
    ellipse(self.x + self.w/2 - self.h/2,self.y - self.h/2 + self.h/2, self.h/4*3)
    rect(self.x - self.w/2 + self.h/2 , self.y - self.h/8*3 , self.w -self.h , self.h/4*3 )
    
    fill(255, 255, 255, 255)
    text(self.s, self.x, self.y)
    popStyle()
    self.m:draw()
end

function Button:touched(t)
    -- Codea does not automatically call this method
    local x = t.x
    local y = t.y
    if t.state == BEGAN and x>self.x- self.w/2 and x<self.x+self.w/2 and y>self.y-self.h/2 and y<self.y+self.w/2 then
      if self.action ~= nil then
        self.action()
      end
    end
end

function Button:makeCenter()
    --returns the dataPoints for the interior meshing that buttons will utilize...
    local t = {}
    
    for a = 90,150,2 do
        table.insert(t, vec2(self.x - self.w/2 + self.h*4.2/8
        + math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
    end
 if self.d == 0 then
        table.insert(t,vec2(0, self.y + math.sin(math.rad(155)) * self.h/8*3))
        table.insert(t,vec2(0, self.y + math.sin(math.rad(205)) * self.h/8*3))
    else 
        for a = 150, 200, 2 do
            table.insert(t, vec2(self.x - self.w/2 + self.h*4.2/8
            + math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
        end 
  end
    
    for a = 200,270,2 do
        table.insert(t, vec2(self.x - self.w/2 + self.h*4.2/8
        + math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
    end
    
--    table.insert(t, vec2(self.x - self.w/2 + self.h*4.2/8
 --       + math.cos(math.rad(90)) * self.h/8*3, self.y + math.sin(math.rad(90)) * self.h/8*3))
    for a = 270, 330,10 do
        table.insert(t,vec2(self.x-self.w/2  + self.w  - self.h *4.2/8+math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
    end
    if self.d == 1 then
        table.insert(t,vec2(WIDTH, self.y + math.sin(math.rad(335)) * self.h/8*3))
        table.insert(t,vec2(WIDTH, self.y + math.sin(math.rad(25)) * self.h/8*3))
        
    else
        for a = 325, 390,4 do
            table.insert(t,vec2(self.x-self.w/2  + self.w  - self.h *4.2/8+math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
        end
    end
    
    for a = 25, 90,4 do
        table.insert(t,vec2(self.x-self.w/2  + self.w  - self.h *4.2/8+math.cos(math.rad(a)) * self.h/8*3, self.y + math.sin(math.rad(a)) * self.h/8*3))
    end
    
    return t
end

--# Main
-- 0 Little Quackys Rainy Day Adventure

function setup()
    displayMode(FULLSCREEN)
    supportedOrientations(PORTRAIT_ANY)
--  WIDTH = 480
--  HEIGHT = 480
    displayMode(FULLSCREEN)
    mode = TheTitle()
end

function draw()
    
    mode:draw()
   --fill(226, 115, 116, 138)
 --  rect(0,0,WIDTH,HEIGHT)
end

function touched(t)
    mode:touched(t)
end

--# TheGame
TheGame = class()

function TheGame:init(x)

end

function TheGame:draw()
    
end

function TheGame:touched(t)

end

--# TheTitle
TheTitle = class()

function TheTitle:init()
    self.buttons = {Button(WIDTH/2, HEIGHT/5, WIDTH/2, HEIGHT/10, 1,"PLAY")}
    self.buttons[1].action = function() print("&") end
end

function TheTitle:draw()
    -- Codea does not automatically call this method
    background(22, 247, 172, 255)
    for a,b in pairs(self.buttons) do
        b:draw()
    end
end

function TheTitle:touched(t)
    -- Codea does not automatically call this method
    for a,b in pairs(self.buttons) do
        b:touched(t)
    end
end

To explain what’s going on to make it easier to find the bug…

mode is used for class control.
The Button:makeCenter() class is supposed to return all the points involved in the mesh that outlines the center of the button…and it does… However, when I go to draw the mesh, it doesn’t draw correctly…it should overlay everything in purple (as a test to ensure it works)…

self.d = direction of button (0-1)

But if anyone could help, that’d be wonderful!!

Thanks friends!

Well, you haven’t set the texture coordinates for the mesh, nor the shader, so there’s a start

You should be able to use my code pretty much as is, all you need to is change the button shape

Thank you. I’m actually going to take a look at your code in the morning and I had spent a fair bit of time writing my faulty mesh code…it’s 1:30am here, so I’m starting to make a bunch of mistakes ^.^ (and not the best at meshes clearly in the first place)

The uniform/varying stuff on your code looks intimidating ignatz, so that’s really why I’m putting it off till morning. Never seen it before aha.

here is a shorter alternative with blendmodes

    -- apply mask to image a, result in image b
    setContext(b)
        sprite(a,100,100)
        blendMode( ZERO, SRC_ALPHA)
        sprite(mask,100,100)
        blendMode(NORMAL)
    setContext()

complete example

-- mask with half circle
function setup()
    a = image(200,200)
    mask = image(200,200)
    b = image(200,200)
    
    -- initial image
    spriteMode(CENTER)
    setContext(a)
        background(255, 0, 0, 255)
        sprite("Planet Cute:Character Boy",100,100,300)
    setContext()
    
    -- create mask
    -- circle part
    setContext(mask)
        background(0,0)
        fill(255)
        ellipseMode(CENTER)
        ellipse(100,100,200)
    setContext()
    
    -- a helper image with the rect
    mask2 = image(200,200)
    setContext(mask2)
        background(0,0)
        fill(255)
        rect(0,0,200,100)
    setContext()
    
    -- remove the rect from the cicle mask
    setContext(mask)
        blendMode( ZERO, SRC_ALPHA)
        sprite(mask2,100,100)
        blendMode(NORMAL)
    setContext()
    
    -- apply mask
    setContext(b)
        sprite(a,100,100)
        blendMode( ZERO, SRC_ALPHA)
        sprite(mask,100,100)
        blendMode(NORMAL)
    setContext()
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(30, 30, 124, 255)

    sprite(a,120, HEIGHT/2 )
    sprite(mask,340, HEIGHT/2 )
    sprite(b,560, HEIGHT/2 )

end