Another analog clock

Found 2 analog clock sample codes in this forum, but both using translate() and rotate() functions which make the line looks jiggy and a bit pixelated. At least on my iPad 1. So here I offer another way to draw analog clock using simple trigonometry so the hour hands look smooth. Here’s the code…

abs = math.abs
sin = math.sin
cos = math.cos
rad = math.rad

function circle(x,y,r)
  ellipseMode(RADIUS)
  ellipse(x,y,r,r)
end

function getLocalDateTime()
  datetime = os.date("*t")
  return datetime
end

function clock12ToDeg(clockVal)
  if clockVal >= 12 then clockVal = clockVal-12 end
  degVal = 90 - (30 * clockVal)
  return degVal
end

function clock60ToDeg(clockVal)
  if clockVal >= 60 then clockVal = clockVal-60 end
  degVal = 90 - (6 * clockVal)
  return degVal
end

function degToClock(degree, r,x,y)
  if x == nil then x = clockX end
  if y == nil then y = clockY end
  if r == nil then r = clockR end
  -- get position of a clock hand's degree
  mx = x + r * cos(rad(degree))
  my = y + r * sin(rad(degree))
  return mx, my
end

function clockMode(sweep)
  if sweep then
    if last_sec == second then 
      sweepStep = sweepStep + 1 
    else 
      maxStep   = sweepStep + 1
      sweepStep = 0 
      last_sec  = second
    end
  else
    sweepStep = 0
  end
end

function drawNumbers()
  textMode(CENTER)
  font("AmericanTypewriter-CondensedBold")
  fontSize(digitSize)
  local w,h = textSize('0')
  -- adjust text position to clock size
  local r = clockR - clockR/10 - borderThick
  local x = clockX 
  local y = clockY 
  -- draw 12 clock numbers
  for i = 5, 55, 5 do
    local x,y = degToClock(clock60ToDeg(i),r,x,y)
    text(i/5,x,y)
  end
  text('12', degToClock(clock60ToDeg(0),r,x,y))
end

function drawDial()
  -- draw clock border
  fill(backColor)
  stroke(borderColor)
  strokeWidth(borderThick)  
  circle(clockX, clockY, clockR + borderThick/2 + clockR/10)
  -- draw clock dot marks
  fill(clockColor)
  stroke(backColor)
  noStroke()
  local dx,dy
  for i = 0, 59 do
    dx,dy = degToClock(clock60ToDeg(i))
    if i % 5 ~= 0 then
      circle(dx,dy,clockR/100) -- small tick
    else
      circle(dx,dy,2.5*clockR/100) -- big tick
    end
  end
  drawNumbers()
end

function drawHour(hourVal)
  local r = clockR*2/3 - borderThick
  strokeWidth(hourThick)
  stroke(hourColor)
  line(clockX, clockY, degToClock(clock12ToDeg(hourVal),r))
end

function drawMinute(minuteVal)
  local r = clockR - borderThick*2
  strokeWidth(minuteThick)
  stroke(minuteColor)
  line(clockX, clockY, degToClock(clock60ToDeg(minuteVal),r))
end

function drawSecond(secondVal)
  -- movement mode
  if isSweep then 
    secondVal = secondVal + sweepStep/maxStep 
  end
  -- hand
  local r = clockR
  strokeWidth(secondThick)
  stroke(secondColor)
  line(clockX, clockY, degToClock(clock60ToDeg(secondVal),r))
  -- tail
  local r = clockR/5
  strokeWidth(secondThick*2)
  line(clockX, clockY, degToClock(clock60ToDeg(secondVal+30),r))
  -- center
  fill(secondColor)
  circle(clockX, clockY, secondThick*3)
end

function adjustSize()
  borderThick = clockR/30
  hourThick   = clockR/10
  minuteThick = clockR/15
  secondThick = clockR/30
  digitSize = clockR/5
end

function setup()
  -- clock size
  clockX = WIDTH/2
  clockY = HEIGHT/2
  clockR = 200 --play with this value to see it auto-adjust
  adjustSize()
  -- clock mode setup
  isSweep   = true
  maxStep   = 30
  sweepStep = 0
  last_sec  = getLocalDateTime().sec
  iparameter("quartz",0,1,0)
  -- clock color
  backColor   = color(192,192,192,255)
  clockColor  = color(  0,  0,  0,255)
  borderColor = color(255,255,255,255)
  hourColor   = color(  0,  0,192,255)
  minuteColor = color(  0,  0,128,255)
  secondColor = color(192,  0,  0,255)
  -- param and watch
  iparameter("clockR",50,300,clockR)
  --watch("second")
  iparameter("add_minute",-30,30,0)
  iparameter("add_hour",-12,12,0)
  --watch("maxStep")
end

function draw()
  background(0)
  smooth()
  adjustSize()
  -- get local time
  datetime = getLocalDateTime()
  second   = datetime.sec
  minute   = datetime.min + add_minute
  hour     = datetime.hour + add_hour
  -- set clock mode
  isSweep  = (quartz == 0)
  clockMode(isSweep)
  -- draw clock
  drawDial()
  drawHour(hour + minute/60)
  drawMinute(minute + second/60)
  drawSecond(second)
end

Any suggestions are welcome. Hope this code will be useful. Thank you. :slight_smile:

And here is the video of it: http://www.youtube.com/watch?v=AHUx6BR-k08

Hi @Bee, my son loved your clock! We added a tick sound. It would be great but tricky to have the hands moveable through touch… !
:slight_smile:

Thanks @Fred. Glad to know my simple little code got another fan. The other fan is my own kid. I use this to teach him reading clock, using the parameters. Indeed, it’s tricky to move the hands by touch. :slight_smile:

I really like it, but when I watched the video, the clock was off by about 25 minutes.

Just kidding.

Thank you. Really? In the video, I made it read the local time. Maybe you should check your clock setting.

Oh, sorry… Just read it that you’re kidding. So am I. :smiley: