class ClockMark { int fullWeight = 20; int weight; boolean released; ClockMark() { weight = fullWeight; released = false; } float alpha() { return (float)weight / fullWeight * 255; } void drawOnClock(Clock clock, int cx, int cy) { if (released || weight > 1) weight--; } void release() { released = true; } } class HourMinuteMark extends ClockMark { int hours; int minutes; HourMinuteMark(int hours, int minutes) { this.hours = hours; this.minutes = minutes; } void drawOnClock(Clock clock, int cx, int cy) { clock.drawMinutesHand(minutes, cx, cy, color(0, 255, 0, alpha())); clock.drawHoursHand(hours, minutes, cx, cy, color(0, 255, 0, alpha())); super.drawOnClock(clock, cx, cy); } } class MinuteMark extends ClockMark { int minutes; MinuteMark(int minutes) { this.minutes = minutes; } void drawOnClock(Clock clock, int cx, int cy) { clock.drawMinutesHand(minutes, cx, cy, color(0, 0, 255, alpha())); super.drawOnClock(clock, cx, cy); } } class Clock { float ticks; int minutes; int hours; float ticksPerMinute; ArrayList marks; Clock() { hours = 0; minutes = 0; ticks = 0; ticksPerMinute = 1; marks = new ArrayList(); } void advance() { ticks++; while (ticks >= ticksPerMinute) { ticks -= ticksPerMinute; minutes++; if (minutes == 60) { minutes = 0; hours++; if (hours == 12) hours = 0; } } } void draw(int x, int y) { int cx = x + 80; int cy = y + 80; stroke(0); strokeWeight(3); noFill(); ellipse(cx, cy, 160, 160); for (int i = marks.size() - 1; i >= 0; i--) { ClockMark each = (ClockMark)marks.get(i); each.drawOnClock(this, cx, cy); if (each.weight == 0) marks.remove(each); } drawMinutesHand(minutes, cx, cy, color(0)); drawHoursHand(hours, minutes, cx, cy, color(0)); } void drawHand(float timeFraction, int length, int cx, int cy, color aColor) { stroke(aColor); strokeWeight(3); noFill(); float radians = radians((1 - timeFraction) * 360 + 90); float x = length * cos(radians); float y = length * sin(radians); line(cx, cy, cx + x, cy - y); } void drawHoursHand(int hours, int minutes, int cx, int cy, color aColor) { drawHand((hours + minutes / 60.0) / 12.0, 45, cx, cy, aColor); } void drawMinutesHand(int minutes, int cx, int cy, color aColor) { drawHand(minutes / 60.0, 65, cx, cy, aColor); } int minutesToday() { return hours * 60 + minutes; } void releaseAllMarks() { for (int i = 0; i < marks.size(); i++) { ClockMark each = (ClockMark)marks.get(i); each.release(); } } } Clock clockOne; Clock clockTwo; int speedLevel; void setup() { size(460, 200); smooth(); frameRate(30); clockOne = new Clock(); clockOne.ticksPerMinute = 1; clockTwo = new Clock(); clockTwo.ticksPerMinute = 1.3; setSpeedLevel(8); } void draw() { background(255); clockOne.draw(20, 20); clockTwo.draw(220, 20); stroke(192); strokeWeight(3); noFill(); for (int i = 0; i < 11; i++) { if (i == speedLevel) stroke(0); else if (i == 5) stroke(128); else stroke(192); float y = 20 + i * ((height - 40) / 10.0); line(420, y, 440, y); } if (clockOne.minutes == clockTwo.minutes && clockOne.hours == clockTwo.hours) { clockOne.marks.add(new HourMinuteMark(clockOne.hours, clockOne.minutes)); clockTwo.marks.add(new HourMinuteMark(clockTwo.hours, clockTwo.minutes)); } else if (clockOne.minutes == clockTwo.minutes) { clockOne.marks.add(new MinuteMark(clockOne.minutes)); clockTwo.marks.add(new MinuteMark(clockTwo.minutes)); } clockOne.advance(); clockTwo.advance(); } void mousePressed() { if (mouseX < 420 || mouseX > 440 || mouseY < 10 || mouseY > (height - 10)) return; int activeHeight = (height - 10) - 10; float regionHeight = activeHeight / 11.0; int region = (int)((mouseY - 10) / regionHeight); setSpeedLevel(region); } void setSpeedLevel(int anInteger) { speedLevel = anInteger; if (speedLevel == 5) clockTwo.ticksPerMinute = 1; else if (speedLevel > 5) clockTwo.ticksPerMinute = 1.0 + (speedLevel - 5.0) / 10; else clockTwo.ticksPerMinute = 1.0 / (1.0 + (5.0 - speedLevel) / 10); clockOne.releaseAllMarks(); clockTwo.releaseAllMarks(); }