An Epic Wheel!

I was really bored, so I thought I would make something for fun. I also haven’t posted code in a while. So, here is a wheel, I commented it sort of like a tutorial!

--# Main
-- Wheel

function setup()
    w=Wheel(
    {x=WIDTH/2,y=HEIGHT/2,size=300},--The Basic Args
    --Insert all the items
    {
    {col=color(255, 0, 0, 169),txt="CODEA!",img="Cargo Bot:Codea Icon"},
    {col=color(0, 255, 0, 167),txt="$$$",img="Cargo Bot:Dialogue Box"},
    {col=color(0, 0, 255, 170),txt="Wheel of\
Fortune",callback=function(i,v)
    print("The circle has rotated "..w.rotation.."\\u{00B0}")end},
    {col=color(255, 123, 0, 255),txt="This is Orange!"},
    },
    --Middle Button Args
    {callback=function()w:spin(50)end}
    )
    --Parameters
    parameter.integer("Position of arrow on the wheel",0,360,w.arrowDirection,function(n)w.arrowDirection=n end)
    parameter.number("Drag of the wheel",0.5,1,w.drag,function(n)w.drag=n end)
    parameter.action("Hide/Show Spin Button",function()if not w.middleButton.hidden then w.middleButton.hide()
     else w.middleButton.show() end end)
    parameter.color("Color_Of_Pointing",nil)
end

-- This function gets called once every frame
function draw()
    background(255, 255, 255, 255)
    w:draw()
end

function touched(t)
    w:touched(t)
end

--# Wheel
--Wheel.lua
--This class is supposed to give you a wheel which is constructed of multiple arrays of data. The wheel can spin and indicate where it landed
--Why did I make this many of you may be asking, because I'm bored, that's why!

Wheel = class()

function Wheel:init(args,items,middle)
    --Basic args
    self.x=args.x or 0
    self.y=args.y or 0
    self.size=args.size or 100
    
    --Arcs
    self.arcs={}
    self.itemCheckData={}
    self.items=items or {}
    for i,item in ipairs(self.items) do
        --Starting degree measure for the arc
        local s=360*(i-1)/#items
        --Ending degree measure for the arc
        local e=360*i/#items
        --Just used for the getPointer method
        table.insert(self.itemCheckData,{start=s,e_nd=e})
        --Make the arc!
        table.insert(self.arcs,Arc(0,0,self.size,s,e,item.col or color(0,0,0,0)))
    end
    
    --Rotation Args
    --The overall rotation
    self.rotation=0
    --The spinning variable
    self.extraRotate=0
    --How slow to decrease the speed of the spinning wheel
    self.drag=args.drag or 0.982
    --When to check if the circle is done spinning
    self.endCheck=args.endCheck or 0.05
    
    --Pointer
    --Where the arrow is pointing at, 0-360 degree measures
    self.arrowDirection=(args.arrowDirection or 90)%360
    --Setup the vertices
    self.pointerMesh=mesh()
    self.pointerMesh.vertices={
    vec2(-20,self.size+10),
    vec2(20,self.size+10),
    vec2(0,self.size-40),
    }
    --I couldn't really find any good colors
    self.pointerMesh.colors={color(255,0,0),color(255,0,0),color(127, 127, 127, 145)}
    
    --Middle Spin Button
    self.middleButton={txt="SPIN",font="HelveticaNeue-Bold",btnSize=100,txtCol=color(255),
    callback=function()self:spin(10)end,shouldDraw=true,selectable=true,btnCol=color(0,249,255,255),
    hide=function()if not self.middleButton.hidden then
            tween(1,self.middleButton,{btnSize=0},tween.easing.elasticInOut,
            function()self.middleButton.hidden=true end)
        end end,
    show=function()if self.middleButton.hidden then
            tween(1,self.middleButton,{btnSize=self.middleButton.originalBtnSize},tween.easing.bounceIn,
            function()self.middleButton.hidden=false end)
        end
        end}
    self.middleButton.originalBtnSize=self.middleButton.btnSize
    for i,v in pairs(middle or {}) do
        self.middleButton[i]=v
    end
end

function Wheel:draw()
    self:drawArcs()
    self:labelArcs()
    if self:isSpinning() then self:rotate()end
    self:drawSpinButton()
end

function Wheel:drawArcs()
    pushMatrix()
    translate(self.x,self.y)
    rotate(self.rotation)
    for i,arc in ipairs(self.arcs) do
        arc:draw()
    end
    rotate(-self.rotation+self.arrowDirection-90)
    self.pointerMesh:draw()
    popMatrix()
end

function Wheel:labelArcs()
    for i,item in ipairs(self.items) do
        pushMatrix()
        pushStyle()
        translate(self.x,self.y)
        rotate((self.itemCheckData[i].start+self.itemCheckData[i].e_nd)/2-90+self.rotation)
        local pos=vec2(0,self.size*7/10)
        if item.txt then
            fill(0, 0, 0, 255)
            font("HoeflerText-Black")
            textWrapWidth(self.size*4/#self.items)
            fontSize(self.size*3/5/#self.items)
            textAlign(CENTER)
            textMode(CENTER)
            if item.img then
                pos=vec2(0,self.size*6/8)
            end
            pos.y = pos.y + (70-self.middleButton.originalBtnSize)
            text(item.txt,pos:unpack())
        end
        if item.img then
            spriteMode(CENTER)
            if item.txt then
                pos=vec2(0,self.size*4/8)
            end
            pos.y = pos.y + (70-self.middleButton.originalBtnSize)
            sprite(item.img,pos.x,pos.y,100*5/#self.items,100*5/#self.items)
        end
        popStyle()
        popMatrix()
    end
end

function Wheel:drawSpinButton()
    if not self.middleButton.shouldDraw then return end
    pushStyle()
    pushMatrix()
    translate(self.x,self.y)
    ellipseMode(CENTER)
    fill(0)
    local c=self.middleButton.btnCol;c.a=100
    stroke(c)
    strokeWidth(self.middleButton.btnSize*0.05)
    ellipse(0,0,self.middleButton.btnSize,self.middleButton.btnSize)
    noStroke()
    c.a=255;fill(c)
    ellipse(0,0,self.middleButton.btnSize*0.8,self.middleButton.btnSize*0.8)
    fill(0, 0, 0, 255)
    fontSize(self.middleButton.btnSize/4)
    font(self.middleButton.font)
    textAlign(CENTER)
    textMode(CENTER)
    text(self.middleButton.txt,-1,-1)
    fill(self.middleButton.txtCol)
    text(self.middleButton.txt)
    popMatrix()
    popStyle()
end

function Wheel:rotate()
    self.rotation = self.rotation + self.extraRotate
    self.extraRotate = self.extraRotate * self.drag
    if self.extraRotate<self.endCheck then
        self.landedOn=self:getPointer()
        self.extraRotate=0
        if self.items[self.landedOn].callback then
            self.items[self.landedOn].callback(self.landedOn,self.items[self.landedOn])
        end
        print(self.items[self.landedOn].txt)
        Color_Of_Pointing=self.items[self.landedOn].col
    end
end

function Wheel:touched(t)
    local sc=97/100
    local mult=1
    local function enlarge()self.middleButton.btnSize = self.middleButton.btnSize * math.pow(sc,-1)end
    if (t.x-self.x)^2 +(t.y-self.y)^2<=self.middleButton.btnSize^2 then
        if self.middleButton.shouldDraw and self.middleButton.selectable and not self:isSpinning() then
            if t.state==ENDED then
                enlarge()
                self.middleButton.selected=nil
                self.middleButton.callback()
                --self.middleButton.hide()
            elseif t.state==BEGAN then
                self.middleButton.selected=true
                self.middleButton.btnSize = self.middleButton.btnSize * sc
            end
        end
    elseif t.state==MOVING and self.middleButton.selected then
        enlarge()
        self.middleButton.selected=false
    end
    if self.middleButton.selected and t.state==ENDED then
        enlarge()
    end
end

function Wheel:isSpinning()return self.extraRotate>0 end

function Wheel:getPointer()
    for i,v in ipairs(self.itemCheckData) do
        local pert=360/#self.items
        local check=self.arrowDirection
        local s=(v.start+self.rotation)%360
        local e=(v.e_nd+self.rotation)%360
        ::check::
        if s<=check and e>check then
            return i
        else
            local per=pert*2
            if 360-s<=per/2 and self.arrowDirection<=per/2 then
                s=s-360
                goto check
            elseif e<=per/2 and self.arrowDirection>=360-per/2 then
                e=e+360
                goto check
            end
        end
    end
    assert(false,"Damn it, I thought this was working perfectly!")
end

function Wheel:spin(x)self.landedOn=nil;self.extraRotate=x or 100 end

--Modified from something I found on the forums

Arc = class()

function Arc:init(x, y, r, s, e, c)
    if not self.mesh then
        self.mesh=mesh()
        self.points = {}
        self.points[1] = vec2(0, 0)
        for i = s, e, 3  do
            i = i / 180 * math.pi
            table.insert(self.points, 1, vec2(math.cos(i), math.sin(i)))
        end
        self.mesh.vertices = triangulate(self.points)
    end
    self.x=x or 0
    self.y=y or 0
    self.r=r or 0
    self.col=c or color(0,0,0,0)
end

function Arc:draw()
    pushMatrix()
    pushStyle()
    translate(self.x, self.y)
    scale(self.r)
    fill(self.col)
    self.mesh:draw()
    popStyle()
    popMatrix()
end

Hope you guys like it!

@CamelCoder I like the way you used Arc and mesh for your wheel. Here’s a wheel I posted back in Feb, 2014. I cheated and used an image for my wheel.

displayMode(FULLSCREEN)

function setup()
    c=physics.body(CIRCLE,20)
    c.x=WIDTH/2
    c.y=HEIGHT/2
    c.gravityScale=0
    c.angularVelocity=0
    c.angularDamping=.4
    getImg()    
end

function draw()
    background(255)
    if img then
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2)
        rotate(c.angle)
        sprite(img,-2,-1,800)
        popMatrix()
        if c.angularVelocity<10 then
            c.angularVelocity=0
        end
        fill(0)
        text("Swipe screen to spin wheel",WIDTH/2,HEIGHT-30)
    else
        fill(0)
        text("Loading image",WIDTH/2,HEIGHT/2)
    end
end

function touched(t)
    if t.state==MOVING then
        v=math.min(math.abs(math.max(math.abs(t.deltaX),math.abs(t.deltaY)))*10,200)
        c.angularVelocity=v
    end
end

function getImg()
    http.request('http://s2.postimg.org/yq31er9nd/image.jpg',gotImage)
end

function gotImage(image,status,header)
    img=image
end

Nice

@EvanDavis Thanks!
@dave1707 Thank you! I actually had no choice, I had to be able to modify how many sections the circle is split up into. I liked yours as well, and I’m glad you liked mine!

Here’s another wheel if anyone can use it. You can set how many sections you want, it’s currently set to 20. It will also center the section on the pointer.

displayMode(FULLSCREEN)

function setup()
    sections=20
    deg=360/sections
    ang=0
    val=0
    m=mesh() 
    tab={}
    for z=0,359,deg do 
        table.insert(tab,arc(WIDTH/2,HEIGHT/2,z-deg/2,deg,80,250,math.random(1,36)))
    end
    img=image(WIDTH,HEIGHT)
    setContext(img)
    fill(255,0,0)
    ellipse(WIDTH/2,HEIGHT/2,670)
    ellipse(WIDTH/2,HEIGHT/2,170)
    for a,b in pairs(tab) do
        b:draw()
    end
    setContext()
end

function draw()
    background(40, 40, 50)
    pushMatrix()
    translate(WIDTH/2,HEIGHT/2)
    rotate(ang)
    sprite(img,0,0)
    popMatrix()
    ang=ang+val
    if val>0 then
        val=val-.03
        if val<0 then
            val=0
            ang=ang%360
        end
    else
        if ang%deg>=deg/2 then
            ang=ang+.2
        else
            ang=ang-.2
        end
        fontSize(18)
        text("Tap screen to spin",WIDTH/2,HEIGHT/2)
    end
    fill(255)
    ellipse(WIDTH/2+250+90,HEIGHT/2,50,20)
end

function touched(t)
    if t.state==BEGAN then
        ang=math.random(360)
        val=10
    end
end

arc=class()

function arc:init(xPos,yPos,startAngle,addAngle,radius,width,txt)
    self.x=xPos
    self.y=yPos
    self.s=startAngle
    self.e=addAngle
    self.r=radius
    self.t=width
    self.txt=txt*50
    self.col=color(math.random(255),math.random(255),math.random(255))
end

function arc:draw() 
    local tab1={}
    for ang=self.s+1,self.s+self.e do
        local x1=math.cos(math.rad(ang-1))*self.r+self.x
        local y1=math.sin(math.rad(ang-1))*self.r+self.y
        local x2=math.cos(math.rad(ang-1))*(self.r+self.t)+self.x
        local y2=math.sin(math.rad(ang-1))*(self.r+self.t)+self.y
        local x3=math.cos(math.rad(ang))*self.r+self.x
        local y3=math.sin(math.rad(ang))*self.r+self.y
        local x4=math.cos(math.rad(ang))*(self.r+self.t)+self.x
        local y4=math.sin(math.rad(ang))*(self.r+self.t)+self.y

        table.insert(tab1,vec2(x1,y1))
        table.insert(tab1,vec2(x2,y2))
        table.insert(tab1,vec2(x4,y4))
        table.insert(tab1,vec2(x1,y1))
        table.insert(tab1,vec2(x3,y3))
        table.insert(tab1,vec2(x4,y4))
    end
    m.vertices=tab1
    m:setColors(self.col)
    m:draw()
    pushMatrix()
    xx=math.cos(math.rad(self.s+self.e/2))*250
    yy=math.sin(math.rad(self.s+self.e/2))*250
    fill(0)
    fontSize(50)
    translate(WIDTH/2+xx,HEIGHT/2+yy)
    rotate(self.s+self.e/2)
    text(self.txt,0,0)
    popMatrix()
end

@dave1707 I’ve seen that your style of coding deals a lot with table.insert, I like your wheel! Nice job!

The use of tables makes programming a lot easier. Using table.insert is an easy way of building a table on the fly without having to worry about keeping track of an index value. A lot of my coding style is based on habit and not necessarily based on efficiency.