10.1 Your Personality and a Little Maths
This tutorial is based on some code that we ported from our iPhone App Personality Profile. In this App you use a dial to answer a number of questions and a profile is provided based on your selection. We used a dial because we wanted a touch friendly solution and this seemed like an intuitive approach. The App is FREE on iTunes so download it and compare the functionality of the Objective C version to our Lua implementation. The embedded video above gives you an idea of how it works but the effect is better understood when used on your iDevice.
The trick to working out how to move the dial is a mathematical one. In essence you need to convert the cartesian co-ordinates of your finger on the screen to equivalent polar co-ordinates.
It is about now that you realise that those maths lessons way back weren't completely useless and perhaps you should have paid more attention. Don't panic, all you need to know is that it is possible to convert from a point (x, y) to polar co-ordinates which consist of an angle and a distance. The angle tells you how much to rotate the dial and you can use the distance portion to work out whether a point is within a circle which is handy for detecting if a tap was on the dial.
To make life easy we created a function which does all the hard work. We have reproduced it below but note that you also need the math.hypot() function which isn't available in the Codea version of Lua. Both of these functions (and more) are contained in the Math tab of RSLibrary.
We have made use of the new Codea dependency functionality so if you download the entire tutorial code in one file, then you also need to download RSLibrary and link it as a dependency to DialDemo. See Tutorial 9 if you want to understand dependencies better or see what is in RSLibrary. For this tute you only need the Math and Button tabs but you might as well grab the whole thing.
As an aside, a handy trick which we use in the math.polar function is to assign a default value to a parameter using "or". This works because nil evaluates to false in Lua. See the code below to understand how you can use this to overload functions.
1 function math.polar(x, y, originX, originY) 2 3 -- Usage: math.polar(x, y) - origin is assumed at (0, 0) 4 -- math.polar(x, y, originX, originY) 5 -- 6 -- This function converts from cartesian co-ordinates (x, y) to 7 -- polar co-ordinates (distance, angle) using the two functions: 8 -- 9 -- 1. distance = hypot(x, y) - the hypotenuse of a right-angle triangle; and 10 -- 2. angle = atan2(x, y) - is the arc tangent of y/x in radians. 11 -- 12 -- Since nil evaluates to false in Lua, you can use "or" to assign a default value. 13 -- 14 -- Reefwing Software (www.reefwing.com.au) 15 -- Version 1.0 16 17 local oX = originX or 0 18 local oY = originY or 0 19 local dx = x - oX 20 local dy = y - oY 21 local distance = math.hypot(dx, dy) 22 local angleInDegrees = math.deg(math.atan2(dy, dx)) 23 -- Functions may return multiple results in Lua. 24 return distance, angleInDegrees 25 26 end
In this instance the Main class is just there to demonstrate the Dial Class. Both Landscape and Portrait orientations are supported using the orientationChanged() function.
1 --# Main 2 -- DialDemo 3 -- Define supported orientations 4 supportedOrientations(ANY) 5 -- Use this function to perform your initial setup 6 7 function setup() 8 9 -- Project Metadata 10 version = 1.0 11 saveProjectInfo("Description", "Codea Dial Demonstration") 12 saveProjectInfo("Author", "Reefwing Software") 13 saveProjectInfo("Date", "26th July 2012") 14 saveProjectInfo("Version", version) 15 print("DialDemo v"..version.."\n") 16 -- Keep an eye on Frame Rate (Frames Per Second, FPS) so we can 17 -- compare and contrast different approaches. 18 FPS = 0 19 watch("FPS") 20 -- Define a new dial = Dial(x, y, angle) 21 -- 22 -- Where: (x, y) are the CENTER co-ordinates of the dial. (default alignment is CENTER). 23 -- angle = the current angle (in degrees) of the dial. 24 dial = Dial(WIDTH/2, HEIGHT/2, 0) 25 -- Create a button which will zero the dial 26 -- The Button Class is contained within RSLibrary 27 local rbX = WIDTH/2 - dial.width/2 28 local rbY = HEIGHT/2 - dial.height - 20 29 resetButton = Button("Reset", rbX, rbY, dial.width, 50) 30 resetButton.action = function() resetButtonTapped() end 31 32 end 33 34 -- Drawing Functions 35 function draw() 36 -- Update instantaneous FPS 37 FPS = math.round(1/DeltaTime) 38 -- This sets a black background color 39 background(blackColor) 40 -- Draw the dial and reset button 41 dial:draw() 42 resetButton:draw() 43 end 44 45 function orientationChanged(newOrientation) 46 -- If the iPad orientation changes we need to 47 -- adjust object co-ordinates. 48 local rbX = WIDTH/2 - dial.width/2 49 local rbY = HEIGHT/2 - dial.height - 20 50 dial.x, dial.y = WIDTH/2, HEIGHT/2 51 resetButton.x, resetButton.y = rbX, rbY 52 end 53 54 -- Handle Touches 55 function touched(touch) 56 dial:touched(touch) 57 resetButton:touched(touch) 58 end 59 60 function resetButtonTapped() 61 dial:reset() 62 end
The Dial Class is where the magic happens. We only have a licence to use the knob and dial images in our App, so you are going to have to draw or obtain your own version. Strictly speaking you only need a knob image (the bit that moves). The class should handle any differences in size to our sprites.
1 --# Dial 2 Dial = class() 3 -- A Dial Class 4 -- Reefwing Software (www.reefwing.com.au) 5 -- 6 -- 26 July 2012 7 -- Version 1.0 8 9 function Dial:init(x, y, angle) 10 11 -- These parameters are used to customise your Dial 12 self.x = x -- x screen co-ordinate for the dial 13 self.y = y -- y screen co-ordinate for the dial 14 self.alignment = CENTER -- Dial uses CENTER alignment 15 self.visible = true -- Boolean to indicate whether dial is drawn and touches handled 16 self.currentAngle = angle or 0 -- Current Angle of the dial in degrees (default 0) 17 dialImage = readImage("Dropbox:pDial200px") 18 knobImage = readImage("Dropbox:pKnob147px") 19 radius = knobImage.width/2 -- Radius of knob circle sprite 20 self.width = dialImage.width -- Defined by the dial sprite width 21 self.height = dialImage.height -- Defined by the dial sprite height 22 23 end 24 25 -- Draw function 26 function Dial:draw() 27 28 -- Codea does not automatically call this method 29 if self.visible then 30 sprite(dialImage, self.x, self.y) 31 pushMatrix() 32 rotateDialByDegrees(self.x, self.y, self.currentAngle) 33 sprite(knobImage, 0, 0) 34 popMatrix() 35 end 36 37 end 38 39 -- Touch Handler 40 function Dial:touched(touch) 41 42 -- Codea does not automatically call this method 43 -- 44 -- The pointInCircle() function determines if the touch was 45 -- on the dial. This function is contained in RSLibrary. 46 -- 47 -- math.polar(x, y) converts from cartesian co-ordinates (x, y) to polar 48 -- co-ordinates (distance, angle). We only use the returned angle not the 49 -- distance. This function is also contained in RSLibrary. 50 51 if self.visible and pointInCircle(touch.x, touch.y, self.x, self.y, radius) then 52 if touch.state == MOVING then 53 _, self.currentAngle = math.polar(touch.x, touch.y, self.x, self.y) 54 end 55 end 56 57 end 58 59 -- Utility Functions 60 61 function Dial:reset() 62 self.currentAngle = 0 63 end 64 65 function rotateDialByDegrees(centreX, centreY, angle) 66 translate(centreX, centreY) 67 rotate(angle) 68 end
http://codeatuts.blogspot.in/2012/07/tutorial-10-simple-dial-class.html
It appears as though you entered a website, phone number, or email address. Please, do not exchange your Skype/email contact on the platform, for any questions regarding the product do not hesitate to make your demand here, your question could be useful for future buyers on the platform. Moreover, writing on the comments area allows us to have a record of the issues and to act in case of any problem.
OkJoin our Mobile Developers Community to get unlimited access to:
Creating an account means you’re okay with Chupamobile’s Terms of Service and Privacy Policy.
Please login in order to leave a comment.