How to turn a dial

This, I am sure, has a very simple answer.

I have 5 dials (circles) on a screen that I want the user to be able to turn by twisting them, the dial only needs to rotate in place and not to freely rotate (i.e. its not like the gears sample) and no momentum.

I have a touch function which stores the starting point in the began phase and then uses the new x and y in the moved phase to try and work out if the rotate is anti clockwise or clock wise.

I have been trying various bits of code and it obviously gets quite busy as you have to check if the user is touching above/below and left/right of the centre of the dial in conjunction with the new/old x and new/oldy of the actual touch to work out the correct direction.

Whilst I have not got it working yet (close but not fully), the code looks horrible and clunky which to me usually means there is a better way. Any thoughts?

[import]uid: 18643 topic_id: 11598 reply_id: 311598[/import]

Can you post a sample? I think you’re on the right track but just need to simplify a little. [import]uid: 10903 topic_id: 11598 reply_id: 42146[/import]

Here is the code, this all works but as I said its clunky I was hoping there was an easier way

Also I am wondering how I can refer to the passing object in the function to perform the rotate so I do not need a function for each dial.

local function turn(event)
local t=event.target
local phase=event.phase

local dialX=dial1.x
local dialY=dial1.y

local rotate=0

if “began” ==phase then
t.x0=event.x
t.y0=event.y
elseif “moved”==phase then
–Top Right
if event.x > dialX and event.y < dialY then
–Straight move top to bottom
if t.x0 == event.x and t.y0<event.y then> rotate = 1
--Straight move bottom to top
elseif t.x0 == event.x and t.y0>event.y then
rotate = -1
– Straight move right to left
elseif t.y0 == event.y and t.x0>event.x then
rotate = -1
– Straight move left to right
elseif t.y0 == event.y and t.x0<event.x then> rotate = 1
– Diagonal bottom right to top left
elseif t.y0 >event.y and t.x0>event.x then
rotate = -1
– Diagonal top left to bottom right
elseif t.y0 < event.y and t.x0 < event.x then
rotate = 1
end
elseif event.x>dialX and event.y>dialY then
– Bottom Right
--Straight move top to bottom
if t.x0 == event.x and t.y0<event.y then> rotate = 1
--Straight move bottom to top
elseif t.x0 == event.x and t.y0>event.y then
rotate = -1
– Straight move right to left
elseif t.y0 == event.y and t.x0>event.x then
rotate = 1
– Straight move left to right
elseif t.y0 == event.y and t.x0<event.x then> rotate = -1
– Diagonal top right to bottom left
elseif t.y0 >event.y and t.x0>event.x then
rotate = 1
– Diagonal bottom left to top right
elseif t.y0 > event.y and t.x0 < event.x then
rotate = -1
end

elseif event.xdialY then
– Bottom Left
--Straight move top to bottom
if t.x0 == event.x and t.y0<event.y then> rotate = -1
--Straight move bottom to top
elseif t.x0 == event.x and t.y0>event.y then
rotate = 1
– Straight move right to left
elseif t.y0 == event.y and t.x0>event.x then
rotate = 1
– Straight move left to right
elseif t.y0 == event.y and t.x0<event.x then> rotate = -1
– Diagonal Bottom right to Top left
elseif t.y0 >event.y and t.x0>event.x then
rotate = -1
– Diagonal bottom left to top right
elseif t.y0 > event.y and t.x0 < event.x then
rotate = 1
end

elseif event.x – Top Left
– Bottom Left
--Straight move top to bottom
if t.x0 == event.x and t.y0<event.y then> rotate = -1
--Straight move bottom to top
elseif t.x0 == event.x and t.y0>event.y then
rotate = 1
– Straight move right to left
elseif t.y0 == event.y and t.x0>event.x then
rotate = -1
– Straight move left to right
elseif t.y0 == event.y and t.x0<event.x then> rotate = 1
– Diagonal Bottom Left to Top Right
elseif t.y0 >event.y and t.x0<event.x then> rotate = 1
– Diagonal bottom left to top right
elseif t.y0 < event.y and t.x0 >event.x then
rotate = -1
end
end
elseif “ended”==phase then

end


t.x0=event.x
t.y0=event.y
dial1.rotation = dial1.rotation + rotate

end [import]uid: 18643 topic_id: 11598 reply_id: 42152[/import] </event.x></event.x></event.y></event.x></event.y></event.x></event.y></event.x></event.y>

When you’re posting code, try to use the code tags.

My solution has some similarities, though I see issues with some making an “L” shaped movement on the dial. Not sure how to solve that issue, I’ll have to think about it more.

[code]

local storey, movey, dy, changedy
local storex, movex, dx, changedx

local dial = display.newRect( 200, 200, 100, 100 )

local function diallistener( event )

if event.phase == “began” then

storey = event.y
storex = event.x

if event.x < dial.x then

changedy = 1

else

changedy = 0

end

elseif event.phase == “moved” then

if event.y < dial.y then

changedx = 0

elseif event.y > dial.y then

changedx = 1

end

movey = event.y
movex = event.x

dx = storex - movex
dy = storey - movey

storex = movex
storey = movey

if dx > 0 then

if changedx == 0 then

dial.rotation = dial.rotation - 2

else

dial.rotation = dial.rotation + 2

end

elseif dx < 0 then

if changedx == 0 then

dial.rotation = dial.rotation + 2

else

dial.rotation = dial.rotation - 2

end

end

if dy > 0 then

if changedy == 0 then

dial.rotation = dial.rotation - 2

else

dial.rotation = dial.rotation + 2

end

elseif dy < 0 then

if changedy == 0 then

dial.rotation = dial.rotation + 2

else

dial.rotation = dial.rotation - 2

end

end

end

end

dial:addEventListener( “touch”, diallistener )

[/code] [import]uid: 10903 topic_id: 11598 reply_id: 42195[/import]

simple solution - work in polar coordinates

use this…
[lua]local sq = display.newRect(100,200,100,100)

local x0 , y0, x1, x2

local function angleBetween(a, b) --returns angle between 2 vectors a and b
local angleA = math.deg(math.atan(a.y/a.x))
local angleB = math.deg(math.atan(b.y/b.x))

print(angleB - angleA)
return (angleB - angleA)

end

local function turn(e)
if e.phase == “began” then
x0 = e.x
y0 = e.y
elseif e.phase == “moved” then
x1 = e.x
y1 = e.y

local vector1 = {x=x0-sq.x, y=y0-sq.y}
local vector2 = {x=x1-sq.x, y=y1-sq.y}

local angle = angleBetween(vector1,vector2)

e.target.rotation = e.target.rotation + angle

x0 = x1
y0 = y1

end

end

sq:addEventListener(“touch”,turn)[/lua] [import]uid: 48521 topic_id: 11598 reply_id: 42201[/import]

crssmn Many thanks for the code and the suggestion about the tags

Chinmay that works well thanks, a bit of an issue when the user gets to 12 o/c or 6 o/c, at these points the image rotates through 180 degrees but I sorted that just by checking for those points.

[import]uid: 18643 topic_id: 11598 reply_id: 42243[/import]

What if I would make a Mixer Table of something with lots of dials that can be rotated, is it possible to reuse this code somehow and make the target to be anything or assign Self to the target?

Also if it was a volume knob, then it would have a min and max point. How do you set up that?
[import]uid: 34126 topic_id: 11598 reply_id: 42250[/import]

That code is universal and will work with as many dials as you want just set up a listener on each

eg

dial1:addEventListener("touch", turn)  
dial2:addEventListener("touch", turn)  

you can use the rotation value of each dial to find out what level it is set to, to create a min max just check the rotation value and if it goes above/below the max/min levels set it to the max/min level.

Ie if you min level is 0 if rotation goes below 0 then set rotation to 0

[import]uid: 18643 topic_id: 11598 reply_id: 42251[/import]

Thanks for the code, chinmay.patil! I spent probably 2 hours yesterday trying to get some objects to rotate and couldn’t get anything working. I could calculate the angle between the two vectors just fine (yaay for getting a math minor!), I just couldn’t figure out how to get it to rotate how I wanted.

To get around the problem of rotating at 12 and 6 o’clock, I did this:

[lua]if (angle > 90) then
angle = angle - 180
elseif (angle < -90) then
angle = angle + 180
end[/lua]

It seems to work pretty well, but I don’t know if it’s the best way to go about it. [import]uid: 70734 topic_id: 11598 reply_id: 42415[/import]

Since tan and tan inverse (atan) are discontinuous function you are getting the flipping at 90 degree rotation. Try using sine or cosine inverse to get angle from vector. That will give u continuous values of angle. [import]uid: 48521 topic_id: 11598 reply_id: 42440[/import]

This is great for a lock I am putting in my next app!!! Is there a way to play a unlocking animation when the right rotation is put in. Does it have to do with all these random numbers showing up in the trace statement? [import]uid: 51459 topic_id: 11598 reply_id: 43285[/import]

The random numbers in the trace are the angles that are produced between the drag of the dials, the angle is then passed back to be used with the rotation attribute to animate the turning of the dials. [import]uid: 18643 topic_id: 11598 reply_id: 43286[/import]

THANKS FOR GETTING BACK TO ME SO FAAAAST!!

So how do I make the lock open when its has done a complete rotation?

[lua]if rotation is complete then open lock?? [/lua]

How do I say this in the programming language? [import]uid: 51459 topic_id: 11598 reply_id: 43289[/import]

You will need to test the rotation value at the end of the touch, the rotation figure keeps on growing if the user keeps turning the same way, ie one full rotation is 360, 2 is 720 and like wise backward will take you to -ve figures.

i’ve used code like this

if e.target.rotation\>360 then e.target.rotation=e.target.rotation-360 end  
if e.target.rotation\<0 then e.target.rotation=e.target.rotation+360 end  

this way my dial rotation is always between 0 and 360, this way I know how far it has been turned.

[import]uid: 18643 topic_id: 11598 reply_id: 43291[/import]

I have 2 faucet knobs and I want to turn both of them… The left knob doesn’t seem to be turning properly… Can someone help with this

[lua]display.setStatusBar (display.HiddenStatusBar)
–> Hides the status bar

display.setDefault(“background”, 255, 255, 255)

local sink = display.newImage(“sink.png”)
sink.x = 150; sink.y = 110

local dial = display.newImage(“Knob.png”)
dial.x = 220; dial.y = 60

local dial2 = display.newImage(“Knob.png”)
dial2.x = 90; dial2.y = 60

local x0 , y0, x1, x2

local function angleBetween(a, b) --returns angle between 2 vectors a and b
local angleA = math.deg(math.atan(a.y/a.x))
local angleB = math.deg(math.atan(b.y/b.x))

print(angleB - angleA)
return (angleB - angleA)

end

local function turn(e)
if e.phase == “began” then
x0 = e.x
y0 = e.y
elseif e.phase == “moved” then
x1 = e.x
y1 = e.y

local vector1 = {x=x0-dial.x, y=y0-dial.y}
local vector2 = {x=x1-dial.x, y=y1-dial.y}

local angle = angleBetween(vector1,vector2)

if (angle > 90) then
angle = angle - 180
elseif (angle < -90) then
angle = angle + 180
end

e.target.rotation = e.target.rotation + angle

x0 = x1
y0 = y1

end

end

dial2:addEventListener(“touch”,turn)
dial:addEventListener(“touch”,turn)[/lua] [import]uid: 51459 topic_id: 11598 reply_id: 44908[/import]

jakescarano you are referring to the object ‘dial’ directly in the vector statements of the turn function, the code should use e.target for the object so it is taking values from the passing object.

I expect that is causing the issue you are seeing.

local vector1 = {x=x0-e.target.x, y=y0-e.target.y} local vector2 = {x=x1-e.target.x, y=y1-e.target.y} [import]uid: 18643 topic_id: 11598 reply_id: 44934[/import]

I knew that was the source of the issue!!! Thanks for your help gmankelow!!! [import]uid: 51459 topic_id: 11598 reply_id: 44988[/import]

I would like to expand on this to get the dial to rotate a lid.

I move the center point of the lid to the bottom edge for a hinge like effect.

How would I add or subtract to the lid.rotation = 0 I’m using the above code example and my dial works.
Thanks
Dan

[code]
– Turn Dial
local function angleBetween(a, b)
local angleA = math.deg(math.atan(a.y/a.x))
local angleB = math.deg(math.atan(b.y/b.x))

print(angleB - angleA)
return (angleB - angleA)

end

local function turn(e)
if e.phase == “began” then
x0 = e.x
y0 = e.y
elseif e.phase == “moved” then
x1 = e.x
y1 = e.y

local vector1 = {x=x0-e.target.x, y=y0-e.target.y}
local vector2 = {x=x1-e.target.x, y=y1-e.target.y}

local angle = angleBetween(vector1,vector2)

if (angle > 90) then
angle = angle - 180
elseif (angle < -90) then
angle = angle + 180
end

e.target.rotation = e.target.rotation + angle

x0 = x1
y0 = y1

end

end
lid = display.newImage( “lid.png” )
game:insert( lid )
physics.addBody( lid, “static”, { friction=0.5, bounce=3.2 } )
lid.x = 462
lid.y = 40
lid.rotation = 0

lid.yReference = 30
lid.xReference = -44
[/code] [import]uid: 78446 topic_id: 11598 reply_id: 49384[/import]