Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Ellipse Progress Bar

Hi everyone. I'm trying to create a progress bar but instead of using a rectangle, I want to use an ellipse. So 0% would be an ellipse unfilled, 50% the ellipse would be half filled like a semi circle. At 25% the ellipse would look like the pic below. Would appreciate any help.

Thanks

Will.

Comments

  • Posts: 1,898
    @WilliamHouten - thought about your problem and I think there is a way to do this but it would depend on the interval you need for rotation - or the displacement round the circle for each update.
    My idea was to create an image which you update in memory and display as a Sprite. You could use a line() with a fixed stroke drawn from the centre and if necessary use blend mode with a circle to ensure the circular effect is intact.
    Have you tried anything yet?
  • @WilliamHouten i think you could probably use the ARC shader in the shaderlab to do that.

  • dave1707dave1707 Mod
    edited December 2020 Posts: 8,723

    @WilliamHouten Here’s an example of a timer. It needs some work, but I’ll let you change it for your needs. This is set for 20 seconds. Change sec for what you need.

    Updated code below
    
  • dave1707dave1707 Mod
    Posts: 8,723

    Made changes to my code so changing the radius changes the circle size also.

    viewer.mode=STANDARD
    
    function setup()
        e=0
        sec=20 -- number of seconds to fill circle
        m=mesh()
    end
    
    function draw()
        background(0)
        timer()
        m.vertices=mtab
        m:setColors(0,200,255)
        m.draw(m)
        fill(255)
        text("tap screen to start timer for  "..sec.."  seconds",WIDTH/2,HEIGHT-50)
    end
    
    function touched(t)
        if t.state==BEGAN then
            startTimer=true
        end
    end
    
    function timer()
        if startTimer then
            e=e+6/sec
        end
        circ(WIDTH/2,HEIGHT/2,100,0,e)
    end
    
    -- x, y, radius, starting angle, number of degrees
    function circ(x,y,r,s,e)
        stroke(0,200,255)
        strokeWidth(2)
        noFill()
        ellipse(x,y,r*2+2)
        if not startTimer then
            return
        end
        mtab={}
        ctab={}
        table.insert(ctab,vec2(x,y))
        for z=s,s+e do
            x1=math.cos(math.rad(z))*r+x
            y1=math.sin(math.rad(z))*r+y
            table.insert(ctab,vec2(x1,y1))
        end
        for z=2,#ctab do
            table.insert(mtab,(vec2(ctab[1].x,ctab[1].y)))
            table.insert(mtab,(vec2(ctab[z-1].x,ctab[z-1].y)))
            table.insert(mtab,(vec2(ctab[z].x,ctab[z].y)))
        end
    end
    
  • dave1707dave1707 Mod
    edited December 2020 Posts: 8,723

    Here’s a class version of the above code with some additions.

    Removed code, see updated class version below
    
  • Hi Dave. That's great! Exactly what I was after.
    What would I need to change to start the fill from the top and make it fill going clockwise?

  • dave1707dave1707 Mod
    edited December 2020 Posts: 8,723

    @WilliamHouten Here’s the change. I made a minor change to timer() and circ(). The change to timer was just to stop adding to e when it’s over 360. The change to calc() was 90-z .

    PS. I also added if e<365 in timer() so it stops calling circ when the circle is complete.

    viewer.mode=STANDARD
    
    function setup()
        e=0
        sec=20 -- number of seconds to fill circle
        m=mesh()
    end
    
    function draw()
        background(0)
        timer()
        m.vertices=mtab
        m:setColors(0,200,255)
        m.draw(m)
        fill(255)
        text("tap screen to start timer for  "..sec.."  seconds",WIDTH/2,HEIGHT-50)
    end
    
    function touched(t)
        if t.state==BEGAN then
            startTimer=true
        end
    end
    
    function timer()
        if startTimer and e<360 then
            e=e+6/sec
        end
        if e<365 then
            circ(WIDTH/2,HEIGHT/2,100,0,e)
        end
    end
    
    -- x, y, radius, starting angle, number of degrees
    function circ(x,y,r,s,e)
        stroke(0,200,255)
        strokeWidth(2)
        noFill()
        ellipse(x,y,r*2+2)
        if not startTimer then
            return
        end
        mtab={}
        ctab={}
        table.insert(ctab,vec2(x,y))
        for z=0,s+e do
            x1=math.cos(math.rad(90-z))*r+x
            y1=math.sin(math.rad(90-z))*r+y
            table.insert(ctab,vec2(x1,y1))
        end
        for z=2,#ctab do
            table.insert(mtab,(vec2(ctab[1].x,ctab[1].y)))
            table.insert(mtab,(vec2(ctab[z-1].x,ctab[z-1].y)))
            table.insert(mtab,(vec2(ctab[z].x,ctab[z].y)))
        end
    end
    
  • Thanks mate! That's exactly what I was after. Cheers.

  • dave1707dave1707 Mod
    Posts: 8,723

    Here’s an updated class version. I allow circles or ellipses for the timers.

    viewer.mode=FULLSCREEN
    
    function setup()
        tab={}
        table.insert(tab,timer(200,300,100,100,10))
        table.insert(tab,timer(300,500,150,50,20))
        table.insert(tab,timer(600,500,80,150,30))
    end
    
    function draw()
        background(0)
        for a,b in pairs(tab) do
            b:draw()
            if b.done then
                fill(255)
                text("Done",b.x,b.y)
            end
        end
        text("Tap inside each ellipse to start timer",WIDTH/2,HEIGHT-50)
    end
    
    function touched(t)
        if t.state==BEGAN then
            for a,b in pairs(tab) do
                b:touched(t)
            end
        end
    end
    
    timer=class()
    
    function timer:init(x,y,a,b,t)
        self.x=x
        self.y=y
        self.a=a
        self.b=b
        self.e=0
        self.t=t
        self.run=false
        self.done=false
        self.m=mesh()
    end
    
    function timer:draw()
        self:addAngle()
        self:calcMesh()
        self:drawMesh()
        self:meshOutline()
        self:countdown()
        self:checkAngle()
    end
    
    function timer:touched(t)
        if t.state==BEGAN then
            if (t.x-self.x)^2/self.a^2+(t.y-self.y)^2/self.b^2 <= 1 then
                self.run=true
            end
        end
    end
    
    function timer:drawMesh()
        self.m.vertices=self.mtab
        if self.done then
            self.m:setColors(255,0,0)
        else
            self.m:setColors(0,200,255)
        end
        self.m.draw(self.m)
    end
    
    function timer:meshOutline()
        stroke(0,200,255)
        strokeWidth(2)
        noFill()
        ellipse(self.x,self.y,self.a*2+2,self.b*2+2)
    end
    
    function timer:countdown()
        fill(255)
        if not self.done then
            text(string.format("%.1f",self.t-(self.t/(360/self.e))),self.x,self.y)
        end
    end
    
    function timer:addAngle()
        if self.run then
            self.e=self.e+6/self.t
        end
    end
    
    function timer:checkAngle()
        if self.e>360 then
            self.run=false
            self.done=true
        end
    end
    
    function timer:calcMesh()
        if self.run then
            self.mtab={}
            self.ctab={}
            table.insert(self.ctab,vec2(self.x,self.y))
            for z=0,self.e do
                x1=self.a*math.cos(math.rad(90-z))+self.x
                y1=self.b*math.sin(math.rad(90-z))+self.y
                table.insert(self.ctab,vec2(x1,y1))
            end
            for z=2,#self.ctab do
                table.insert(self.mtab,(vec2(self.ctab[1].x,self.ctab[1].y)))
                table.insert(self.mtab,(vec2(self.ctab[z-1].x,self.ctab[z-1].y)))
                table.insert(self.mtab,(vec2(self.ctab[z].x,self.ctab[z].y)))
            end
        end
    end
    
  • Hi Dave. That's great. Using this code allows me to put 3 progress bars at the same time. Thanks heaps!

  • dave1707dave1707 Mod
    edited December 2020 Posts: 8,723

    @WilliamHouten With the class code, you can have 1 or 100 timers or just about any amount at the same time.

Sign In or Register to comment.