Analogue Clock Second hand Sweep not Tick

Hi All,

I’m busting my brains with this one.

I’ve taken the very simple clock sketch from the examples, but instead of drawing the lines I’m using images of clock hands.

I currently have my clock working perfectly, however I want the second hand to sweep, not tick.

float s = map(second(), 0, 60, 0, 360);

The above command is the one i’m using. (s is used in my radians)

pushMatrix();
  translate(411, 349);
  rotate(radians(s));
  image(secondHand, -16, -190);
  popMatrix();

Now understandably the hand moves 6 degrees every second (tick), but how can i sweep it so its constantly moving throughout the full rotation of the clock.

I’m sure the answer is staring me in the face.

Cheers Guys.

1 Like

Have you considered using millis() besides seconds().

Something like float s = map(millis(), 0, 1000 * 60, 0, 360); will give you a smaller step than using only seconds.

You need a finer value than second() provides. Here’s one approach:

int mill;
int psecond;

void setup(){
  size(400,400);
  psecond = second() - 1;
}

void draw(){
  background(0);
  stroke(255);
  int sec = second();
  if( sec != psecond ){
    mill = millis()%1000;
  }
  float s = map(1000*second()+(millis()-mill)%1000, 0, 60000, 0, 360);
  translate(200,200);
  rotate(radians(s));
  line(0,0,200,0);
  psecond = sec;
}

Notice that this still is not perfect - the hand has to adjust a small amount after the very first second.

1 Like

Thanks Alex,

I had thought about millis(), but need it to be linked to my computer time as opposed to starting the count when the program runs, like the milli function.

Thanks TfGuy, will have a play, see if i can move forward.

Cheers.

You can use Java’s GregorianCalendar class to get the current time to millisecond precision like this.

import java.util.*;

GregorianCalendar cdr;
int hr, min, sec, ms, lastT, deltaT;
String time;

void setup() {
  size(400, 100);
  textSize(30);
  // Create a calendar using local time zone
  cdr = new GregorianCalendar();
  lastT = 0;
  deltaT = 0;
}

void draw() {
  background(0);
  fill(255);
  // Get the elapsed time since the last draw method
  int now = millis();
  deltaT = now - lastT; 
  lastT = now;
  // Update the calendar with the elapsed time
  cdr.add(Calendar.MILLISECOND, deltaT);
  // Get the current time
  hr = cdr.get(Calendar.HOUR);
  min = cdr.get(Calendar.MINUTE);
  sec = cdr.get(Calendar.SECOND);
  ms = cdr.get(Calendar.MILLISECOND);
  // Calculate a string representation of the time and display it
  String time = hr + ":" + min + ":" + sec + "." + ms;
  text(time, 30, 50);
}
1 Like

Thanks Quark,

The GregorianCalendar class work a treat mate, many thanks.

Cheers

1 Like

Hello. I tried expanding this idea for minutes, and it seems to almost work but the minute hand only gets about half way to the next tick and then snaps into place at 0 seconds. Any suggestions what’s not right? Or is it just expecting too much accuracy for millis() over this period of time?

// Time class 
// https://discourse.processing.org/t/analogue-clock-second-hand-sweep-not-tick/2331/3

Time t = new Time();
float faceRadius = 175;
void setup() {
   size(400, 600);
}
void draw() {
   background(127);
   stroke(255);
   textSize(24);
   
   pushMatrix(); // center everything
   translate(width/2, height/2);
   showTicks();

   // seconds
   pushMatrix(); // rotate seconds
   rotate(-HALF_PI + radians(t.getSecondAngle()));
   line(0, 0, faceRadius, 0);
   fill(255);
   text(t.csecond, faceRadius-40, 0);
   popMatrix(); // rotate seconds

   // minutes
   pushMatrix(); // rotate minutes
   rotate(-HALF_PI + radians(t.getMinuteAngle()));
   line(0, 0, faceRadius, 0);
   fill(255);
   text(t.cminute, faceRadius-40, 0);
   popMatrix(); // rotate minutes
   
   popMatrix(); // center everything
}
class Time {
   int mill;
   int psecond;
   int csecond;
   int pminute;
   int cminute;
   Time() {
      psecond = second() - 1;
      pminute = minute() - 1;
   }
   int getSecondAngle() {
      int sec = second();
      csecond = sec;
      if ( sec != psecond ) {
         mill = millis()%1000;
      }
      float s = map(1000*second() + (millis()-mill) % 1000, 0, 60000, 0, 360);
      
      psecond = sec;
      return s;
   }
   float getMinuteAngle() {
      int min = minute();
      cminute = min;
      if ( min != pminute ) {
         mill = millis()%1000;
      }
      float m = map(100000 * minute() + 1000 * second() + (millis()-mill) % 1000, 0, 6000000, 0, 360);
      
      pminute = min;
      return m;
   }
}
void showTicks() {
   for (int a = 0; a < 360; a+=6) {
      float angle = radians(a);
      float x = faceRadius*cos(angle);
      float y = faceRadius*sin(angle);
      strokeWeight(1);
      fill(127);
      stroke(200);
      ellipse(x, y, 10, 10);
      stroke(255);
      point(x, y);
   }
}
2 Likes

Hello,

This is not a solution (below); I am sharing how I examined what your code was doing.

  1. Added a timer to replace second() and minute(); I wanted to speed things up to see what code was doing.
  2. Used println() to see what the code was doing.
  3. I did get a cleaner sweep with some experimentation.
  4. This is not a “solution” and just my efforts working through your code as a morning exercise for my brain.
  5. Leaving the rest with you.
  6. Off for a hike in the woods for some different excercise.

Note: It crashed on me at one point and this is resurrected version from the file in %temp% so may look a bit different.

/// Time class 
// https://discourse.processing.org/t/analogue-clock-second-hand-sweep-not-tick/2331/3

Time t = new Time();
float faceRadius = 175;
int sCount = 0;
int mCount = 0;

public void setup() {
   
}
public void draw() {
   background(127);
   stroke(255);
   textSize(24);
   
   pushMatrix(); // center everything
   translate(width/2, height/2);
   showTicks();

   // seconds
   pushMatrix(); // rotate seconds
   rotate(-HALF_PI + radians(t.getSecondAngle()));
   line(0, 0, faceRadius, 0);
   fill(255);
   text(t.csecond, faceRadius-40, 0);
   popMatrix(); // rotate seconds

   // minutes
   pushMatrix(); // rotate minutes
   rotate(-HALF_PI + radians(t.getMinuteAngle()));
   line(0, 0, faceRadius, 0);
   fill(255);
   text(t.cminute, faceRadius-40, 0);
   popMatrix(); // rotate minutes
   
   popMatrix(); // center everything
   
   if (frameCount%1 == 0) // Change %1 to %60 to slow things down!
     {
     sCount++;
     if (sCount%60 == 0)
       {
       sCount = 0;
       mCount++;
       }
       if (mCount%60 == 0)
         mCount = 0;
     }
//     println(sCount, mCount);
  }

class Time {
   int mill;
   int psecond;
   int csecond;
   int pminute;
   int cminute;
   Time() {
      psecond = sCount - 1;
      pminute = mCount - 1;
   }
   public float getSecondAngle() {
      int sec = sCount;
      csecond = sec;
      if ( sec != psecond ) {
         mill = millis()%1000;
      }
      float s = map(1000*sCount + (millis()-mill) % 1000, 0, 60000, 0, 360);
      
      psecond = sec;
      return s;
   }
   public float getMinuteAngle() {
      int min = mCount;
      cminute = min;
//      if ( min != pminute ) {
         mill = millis()%60000;
 //     }
      //float m = map(100000 * minute() + 1000 * second() + (millis()-mill) % 1000, 0, 6000000, 0, 360);
      //float m = map(100000*mCount     + 1000f*sCount    + (millis()-mill)%1000,   0, 6000000, 0, 360);   
      float m = map(mill,   0,  60000, 0, 360); 
      
      println(mill, (millis()-mill)%1000, m);
      
      pminute = min;
      return m;
   }
}
public void showTicks() {
   for (int a = 0; a < 360; a+=6) {
      float angle = radians(a);
      float x = faceRadius*cos(angle);
      float y = faceRadius*sin(angle);
      strokeWeight(1);
      fill(127);
      stroke(200);
      ellipse(x, y, 10, 10);
      stroke(255);
      point(x, y);
   }
}
  public void settings() {  size(400, 600); }

This doesn’t make any noticeable difference and the use of the Gregorian calendar has several benefits, including using local time and handles daylight saving time etc but it could have the advantage of encapsulating everything you need to do with a clock.

This example sketch does just that

import java.util.*;

Time t;
float clockRadius;

void setup() {
  size(400, 400);
  t = new Time(this);
  clockRadius = min(width, height)/ 2 - 10;
  textSize(30);
  // Create a calendar using local time zone
}

void draw() {
  background(255);
  drawDigital24(t);
  translate(width/2, height/2);
  drawTicks();
  drawHands(t);
}

void drawDigital24(Time t) {
  String dt = nf(t.getHour(), 2) + ":" 
    + nf(t.getMinute(), 2) + ":"
    + nf( (t.getSecond() * 1000 + t.getMilli()) / 1000f, 2, 3);
  textSize(16);
  textAlign(CENTER, CENTER);
  fill(20, 20, 192);
  text(dt, 2, 2, 100, 20);
}

void drawTicks() {
  float da = TWO_PI / 12;
  strokeWeight(10);
  stroke(140);
  pushMatrix();
  for (int i = 0; i < 12; i++) {
    line(clockRadius - 20, 0, clockRadius, 0);
    rotate(da);
  }
  popMatrix();
}


void drawHands(Time t) {
  // Hour hand
  pushMatrix();
  stroke(0);
  strokeWeight(8);
  rotate(t.getHourHandAngle());
  line(-5, 0, clockRadius * 0.5, 0);
  popMatrix();

  // Minute hand
  pushMatrix();
  stroke(0);
  strokeWeight(4);
  rotate(t.getMinuteHandAngle());
  line(-10, 0, clockRadius * 0.95, 0);
  popMatrix();

  // Second hand
  pushMatrix();
  stroke(255, 0, 0);
  strokeWeight(2);
  rotate(t.getSecondHandAngle());
  line(-5, 0, clockRadius * 0.9, 0);
  popMatrix();
}

public class Time {
  private GregorianCalendar cdr;

  private int lastT;
  private int deltaT;
  int hour, minute, second, milli;

  private final float ANGLE_OFFSET = - PI /2;

  private final float ANGLE_HOUR = TWO_PI / 12;
  private final float ANGLE_MINUTE = TWO_PI /  60;
  private final float ANGLE_SECOND = TWO_PI /  60;

  Time(PApplet pa) {
    cdr = new GregorianCalendar();
    lastT = millis();
    deltaT = 0;
    pa.registerMethod("pre", this);
  }

  public void pre() {
    // Get the elapsed time since the last draw method
    int now = millis();
    deltaT = now - lastT; 
    lastT = now;
    // Update the calendar with the elapsed time
    cdr.add(Calendar.MILLISECOND, deltaT);
  }

  int getHour() {
    return cdr.get(Calendar.HOUR);
  }

  float getHourHandAngle() {
    float t = (getHour() % 12) 
      + (getMinute() /  60f) 
      + (getSecond() / 3600f)
      + (getMilli() / 3600000);
    return t * ANGLE_HOUR + ANGLE_OFFSET;
  }

  int getMinute() {
    return cdr.get(Calendar.MINUTE);
  }

  float getMinuteHandAngle() {
    float t = getMinute()  
      + (getSecond() / 60f)
      + (getMilli() / 60000f);
    return t * ANGLE_MINUTE + ANGLE_OFFSET;
  }

  int getSecond() {
    return cdr.get(Calendar.SECOND);
  }

  float getSecondHandAngle() {
    float t = getSecond() + (getMilli() / 1000f);
    return t * ANGLE_SECOND + ANGLE_OFFSET;
  }

  int getMilli() {
    return cdr.get(Calendar.MILLISECOND);
  }

  public String toString() {
    return getHour() + ":" + getMinute() + ":" + getSecond() + "." + milli;
  }
}
2 Likes

I really do like this Gregorian Calendar solution. Sadly, I can’t use java.util.* with the Processing for iOS App that I’m primarily working on.

Is there any chance maybe I’ve just got some math wrong? It seems like I must be generating the wrong angle, by about 3deg/min.

I have to admit that I am having a bit of trouble conceptualizing how this works for seconds, and I’m trying to learn from the example by forcing it to work with minutes and maybe even hours. :partying_face:

I’m on an ongoing quest to make friends with map() and modulo, and I really like clock hands and gears with smooth motion.

Check your math…

Consider this:
float m = map(60*mCount*1000 + sCount*1000 + (millis()-mill)%1000, 0, 3600*1000, 0, 360);

I used my owner counter for testing your code to speed things up; you can go back to second() and minute();:

// Simple timer for testing to speed up "time" for testing
   if (frameCount%1 == 0) // Change %1 to %60 to slow things down!
     {
     if (sCount>0 && sCount%60 == 0)
       {
       sCount = 0;
       mCount++;
       }
     if (mCount%60 == 0)
       mCount = 0;
     sCount++;
     }
     println(sCount, mCount);
1 Like

It is simple to change my last example to avoid using Gregorian calendar.

Time t;
float clockRadius;

void setup() {
  size(400, 400);
  t = new Time(this, 13, 48, 21);
  clockRadius = min(width, height)/ 2 - 10;
  textSize(30);
  // Create a calendar using local time zone
}

void draw() {
  background(255);
  drawDigital24(t);
  translate(width/2, height/2);
  drawTicks();
  drawHands(t);
}

void drawDigital24(Time t) {
  String dt = nf(t.getHour(), 2) + ":" 
    + nf(t.getMinute(), 2) + ":"
    + nf( (t.getSecond() * 1000 + t.getMilli()) / 1000f, 2, 3);
  textSize(16);
  textAlign(CENTER, CENTER);
  fill(20, 20, 192);
  text(dt, 2, 2, 100, 20);
}

void drawTicks() {
  float da = TWO_PI / 12;
  strokeWeight(10);
  stroke(140);
  pushMatrix();
  for (int i = 0; i < 12; i++) {
    line(clockRadius - 20, 0, clockRadius, 0);
    rotate(da);
  }
  popMatrix();
}


void drawHands(Time t) {
  // Hour hand
  pushMatrix();
  stroke(0);
  strokeWeight(8);
  rotate(t.getHourHandAngle());
  line(-5, 0, clockRadius * 0.5, 0);
  popMatrix();

  // Minute hand
  pushMatrix();
  stroke(0);
  strokeWeight(4);
  rotate(t.getMinuteHandAngle());
  line(-10, 0, clockRadius * 0.95, 0);
  popMatrix();

  // Second hand
  pushMatrix();
  stroke(255, 0, 0);
  strokeWeight(2);
  rotate(t.getSecondHandAngle());
  line(-5, 0, clockRadius * 0.9, 0);
  popMatrix();
}

public class Time {
  private int lastT;
  private int deltaT;
  private int hour, minute, second, milli;

  private final float ANGLE_OFFSET = - PI /2;

  private final float ANGLE_HOUR = TWO_PI / 12;
  private final float ANGLE_MINUTE = TWO_PI /  60;
  private final float ANGLE_SECOND = TWO_PI /  60;

  Time(PApplet pa, int h, int m, int s) {
    hour = h;
    minute = m;
    second = s;
    milli = 0;
    lastT = millis();
    deltaT = 0;
    pa.registerMethod("pre", this);
  }

  public void pre() {
    // Get the elapsed time since the last draw method
    int carry = 0;
    int now = millis();
    deltaT = now - lastT; 
    lastT = now;
    milli += deltaT;
    carry = milli / 1000; // integer division
    milli %= 1000;

    second += carry;
    carry = second / 60; // integer division
    second %= 60;

    minute += carry;
    carry = minute / 60; // integer division
    minute %= 60;

    hour += carry;
    hour %= 24;
  }

  int getHour() {
    return hour;
  }

  float getHourHandAngle() {
    float t = (hour % 12) 
      + (minute /  60f) 
      + (second / 3600f)
      + (milli/ 3600000);
    return t * ANGLE_HOUR + ANGLE_OFFSET;
  }

  int getMinute() {
    return minute;
  }

  float getMinuteHandAngle() {
    float t = minute  
      + (second / 60f)
      + (milli / 60000f);
    return t * ANGLE_MINUTE + ANGLE_OFFSET;
  }

  int getSecond() {
    return second;
  }

  float getSecondHandAngle() {
    float t = second + (milli / 1000f);
    return t * ANGLE_SECOND + ANGLE_OFFSET;
  }

  int getMilli() {
    return milli;
  }

  public String toString() {
    return getHour() + ":" + getMinute() + ":" + getSecond() + "." + milli;
  }
}
1 Like

Thank you for this, it really does help to be able to change this visualization rate.

1 Like

This is really helpful, I’ll have to work around the use of PApplet, etc on the iPhone compiler, but I really appreciate you putting all the ingredients together so concisely.

I’m really kind of stuck with using things that work on the iPhone compiler because I’m trying to make things I can easily put into my 4 year old son’s hands so I can watch him have fun with it. I’ve been working on learning how to use Xcode and Swift but totally choking on it for obvious reasons. This is a great step in between.

I appreciate the help.

How would I call the code inside the pre() method if I can’t use the pa.registerMethod method with a papplet reference? It’s not going to work in the iOS compiler.

I have never encountered the registerMethod and pre() before but I have spent a bit of time reading about it and I have a basic understanding of why to use it.

But if I don’t know how it is called. I tried putting pre(); in draw() but then the hands just spin around really fast.

Can’t help you there sorry.

try this version ( only tested Win 10 )


// works nice here ( Win 10 )
// try a NON registered class version for @ddownn IOS compiler ( untested )


Time t;
float clockRadius;

void setup() {
  size(400, 400);
//  t = new Time(this, 13, 48, 21);
  t = new Time(13, 48, 21);
  clockRadius = min(width, height)/ 2 - 10;
  textSize(30);
  // Create a calendar using local time zone
}

void draw() {
  background(255);
  t.pre();                        // if not registered must call it manually
  drawDigital24(t);
  translate(width/2, height/2);
  drawTicks();
  drawHands(t);
}

void drawDigital24(Time t) {
  String dt = nf(t.getHour(), 2) + ":" 
    + nf(t.getMinute(), 2) + ":"
    + nf( (t.getSecond() * 1000 + t.getMilli()) / 1000f, 2, 3);
  textSize(16);
  textAlign(CENTER, CENTER);
  fill(20, 20, 192);
  text(dt, 2, 2, 100, 20);
}

void drawTicks() {
  float da = TWO_PI / 12;
  strokeWeight(10);
  stroke(140);
  pushMatrix();
  for (int i = 0; i < 12; i++) {
    line(clockRadius - 20, 0, clockRadius, 0);
    rotate(da);
  }
  popMatrix();
}


void drawHands(Time t) {
  // Hour hand
  pushMatrix();
  stroke(0);
  strokeWeight(8);
  rotate(t.getHourHandAngle());
  line(-5, 0, clockRadius * 0.5, 0);
  popMatrix();

  // Minute hand
  pushMatrix();
  stroke(0);
  strokeWeight(4);
  rotate(t.getMinuteHandAngle());
  line(-10, 0, clockRadius * 0.95, 0);
  popMatrix();

  // Second hand
  pushMatrix();
  stroke(255, 0, 0);
  strokeWeight(2);
  rotate(t.getSecondHandAngle());
  line(-5, 0, clockRadius * 0.9, 0);
  popMatrix();
}

public class Time {
  private int lastT;
  private int deltaT;
  private int hour, minute, second, milli;

  private final float ANGLE_OFFSET = - PI /2;

  private final float ANGLE_HOUR = TWO_PI / 12;
  private final float ANGLE_MINUTE = TWO_PI /  60;
  private final float ANGLE_SECOND = TWO_PI /  60;

//  Time(PApplet pa, int h, int m, int s) {
  Time( int h, int m, int s) {
    hour = h;
    minute = m;
    second = s;
    milli = 0;
    lastT = millis();
    deltaT = 0;
//    pa.registerMethod("pre", this);
  }

  public void pre() {
    // Get the elapsed time since the last draw method
    int carry = 0;
    int now = millis();
    deltaT = now - lastT; 
    lastT = now;
    milli += deltaT;
    carry = milli / 1000; // integer division
    milli %= 1000;

    second += carry;
    carry = second / 60; // integer division
    second %= 60;

    minute += carry;
    carry = minute / 60; // integer division
    minute %= 60;

    hour += carry;
    hour %= 24;
  }

  int getHour() {
    return hour;
  }

  float getHourHandAngle() {
    float t = (hour % 12) 
      + (minute /  60f) 
      + (second / 3600f)
      + (milli/ 3600000);
    return t * ANGLE_HOUR + ANGLE_OFFSET;
  }

  int getMinute() {
    return minute;
  }

  float getMinuteHandAngle() {
    float t = minute  
      + (second / 60f)
      + (milli / 60000f);
    return t * ANGLE_MINUTE + ANGLE_OFFSET;
  }

  int getSecond() {
    return second;
  }

  float getSecondHandAngle() {
    float t = second + (milli / 1000f);
    return t * ANGLE_SECOND + ANGLE_OFFSET;
  }

  int getMilli() {
    return milli;
  }

  public String toString() {
    return getHour() + ":" + getMinute() + ":" + getSecond() + "." + milli;
  }
}

Thanks for showing me how to work around the pa.registerMethod thing. It does work here too in the Processing IDE. Still weird behavior in the iOS compiler but I’ll figure that part out.

Is this basically to synchronize the mill to the moment that a new second() begins?