Skip to content

Instantly share code, notes, and snippets.

@dribnet
Forked from uwcc/.block
Last active March 1, 2020 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dribnet/1e09fe3f3e3294fa49f0ef699628d056 to your computer and use it in GitHub Desktop.
Save dribnet/1e09fe3f3e3294fa49f0ef699628d056 to your computer and use it in GitHub Desktop.
2020 MDDN242 Assignment 1: Software Clock
license: mit

Clock Drawing Test

Drawing a clock is often a diagnotic in evaluating dementia. Here are some examples from tests. My idea is to use some of these inconsistent clocks as starting points, but to then add additional logic so that the clocks can actually tell time.

<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/addons/p5.dom.js"></script>
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="clock.js"></script>
<script language="javascript" type="text/javascript" src="runner.js"></script>
</head>
<body style="background-color:white">
<div class="outer">
<div class="inner">
<div id="canvasContainer"></div>
</div>
</div>
</table>
<br>
<a href="clock.js">(source code)</a>
<a href="part1.html">part1</a>
<a href="part2.html">part2</a>
<a href="clock.html">clock</a>
<a href="debug.html">debug</a>
</body>
/*
* use p5.js to draw a clock on a 960x500 canvas
*/
function draw_clock(obj) {
// draw your own clock here based on the values of obj:
// obj.hours goes from 0-23
// obj.minutes goes from 0-59
// obj.seconds goes from 0-59
// obj.millis goes from 0-999
// obj.seconds_until_alarm is:
// < 0 if no alarm is set
// = 0 if the alarm is currently going off
// > 0 --> the number of seconds until alarm should go off
// YOUR MAIN CLOCK CODE GOES HERE
background(50); // beige
fill(200); // dark grey
textSize(40);
textAlign(CENTER, CENTER);
text("YOUR MAIN CLOCK CODE GOES HERE", width/2, height/2);
}
/*
* use p5.js to draw a clock on a 960x500 canvas
*/
function draw_clock(obj) {
// draw your own clock here based on the values of obj:
// obj.hours goes from 0-23
// obj.minutes goes from 0-59
// obj.seconds goes from 0-59
// obj.millis goes from 0-999
// obj.seconds_until_alarm is:
// < 0 if no alarm is set
// = 0 if the alarm is currently going off
// > 0 --> the number of seconds until alarm should go off
background(0);
const ballWidth = 100;
fill(255, 255, 0);
let secs = obj.seconds;
let millis = obj.millis;
let exactSeconds = secs + millis / 1000.0;
posX = map(exactSeconds, 0, 60, ballWidth/2, width-ballWidth/2);
posY = map(60, 0, 100, 0, height);
ellipse(posX, posY, ballWidth);
fill(255);
posX = map(obj.minutes, 0, 59, ballWidth/2, width-ballWidth/2);
posY = map(40, 0, 100, 0, height);
ellipse(posX, posY, ballWidth);
posX = map(obj.hours, 0, 23, ballWidth/2, width-ballWidth/2);
posY = map(20, 0, 100, 0, height);
ellipse(posX, posY, ballWidth);
}
/*
* us p5.js to draw a clock on a 960x500 canvas
*/
function draw_clock(obj) {
// draw your own clock here based on the values of obj:
// obj.hours goes from 0-23
// obj.minutes goes from 0-59
// obj.seconds goes from 0-59
// obj.millis goes from 0-999
// obj.seconds_until_alarm is:
// < 0 if no alarm is set
// = 0 if the alarm is currently going off
// > 0 --> the number of seconds until alarm should go off
let hours = obj.hours;
let minutes = obj.minutes;
let seconds = obj.seconds;
let millis = obj.millis;
let alarm = obj.seconds_until_alarm;
background(255,255,200); // beige
fill(128,100,100); // dark grey
text("Hour: " + hours, 10, 22);
text("Minute: " + minutes, 10, 42);
text("Second: " + seconds, 10, 62);
text("Millis: " + millis, 10, 82);
let hourBarWidth = map(hours, 0, 23, 0, width);
let minuteBarWidth = map(minutes, 0, 59, 0, width);
let secondBarWidth = map(seconds, 0, 59, 0, width);
let millisBarWidth = map(millis, 0, 1000, 0, width);
noStroke();
fill(40);
rect(0, 100, hourBarWidth, 50);
fill(80);
rect(0, 150, minuteBarWidth, 50);
fill(120)
rect(0, 200, secondBarWidth, 50);
fill(160)
rect(0, 250, millisBarWidth, 50);
// Make a bar which *smoothly* interpolates across 1 minute.
// We calculate a version that goes from 0...60,
// but with a fractional remainder:
let secondBarWidthChunky = map(seconds, 0, 59, 0, width);
let secondsWithFraction = seconds + (millis / 1000.0);
let secondBarWidthSmooth = map(secondsWithFraction, 0, 60, 0, width);
fill(100, 100, 200)
rect(0, 350, secondBarWidthChunky, 50);
fill(120, 120, 240)
rect(0, 400, secondBarWidthSmooth, 50);
text("Minute: " + secondsWithFraction, 200, 42);
fill(200, 100, 100)
let alarm_message = ""
if(alarm < 0) {
alarm_message = "Alarm: Not Set"
}
else if(alarm == 0) {
alarm_message = "Alarm: GOING OFF"
}
else {
let seconds_remaining = int(alarm);
alarm_message = "Alarm: will go off in " + seconds_remaining + " seconds"
}
text(alarm_message, 400, 42);
}
/*
* us p5.js to draw a clock on a 960x500 canvas
*/
/* size of square */
const s = 24
let numbers = [
// 0
[
[2, 1],
[3, 1],
[1, 2],
[4, 2],
[1, 3],
[4, 3],
[1, 4],
[4, 4],
[2, 5],
[3, 5],
],
// 1
[
[2, 2],
[3, 1],
[3, 2],
[3, 3],
[3, 4],
[3, 5]
],
// 2
[
[1, 1],
[2, 1],
[3, 1],
[4, 2],
[4, 3],
[3, 3],
[2, 4],
[1, 5],
[2, 5],
[3, 5],
[4, 5]
],
[ // 3
[1, 1],
[2, 1],
[3, 1],
[4, 2],
[3, 3],
[2, 3],
[4, 4],
[3, 5],
[2, 5],
[1, 5]
],
[ // 4
[1, 1],
[3, 1],
[1, 2],
[3, 2],
[1, 3],
[2, 3],
[3, 3],
[4, 3],
[3, 4],
[3, 5]
],
[ // 5
[1, 1],
[2, 1],
[3, 1],
[4, 1],
[1, 2],
[1, 3],
[2, 3],
[3, 3],
[4, 4],
[3, 5],
[2, 5],
[1, 5]
],
[ // 6
[2, 1],
[3, 1],
[4, 1],
[1, 2],
[1, 3],
[2, 3],
[3, 3],
[1, 4],
[4, 4],
[2, 5],
[3, 5],
], //7
[
[1, 1],
[2, 1],
[3, 1],
[4, 1],
[4, 2],
[3, 3],
[3, 4],
[3, 5],
],
[ // 8
[2, 1],
[3, 1],
[1, 2],
[4, 2],
[2, 3],
[3, 3],
[1, 4],
[4, 4],
[2, 5],
[3, 5],
],
[ // 9
[2, 1],
[3, 1],
[4, 1],
[1, 2],
[4, 2],
[2, 3],
[3, 3],
[4, 3],
[4, 4],
[4, 5]
],
]
function draw_number(num, x, y) {
/* this resets any previous translations */
resetMatrix();
translate(x, y);
var pixels = numbers[num%numbers.length];
for(var i=0; i<13; i++) {
var cur_pixel = pixels[i%pixels.length];
rect(cur_pixel[0] * s, cur_pixel[1] * s, s, s);
}
}
function draw_number_interp(frac, num1, num2, x, y) {
/* this resets any previous translations */
resetMatrix();
translate(x, y);
var pixels1 = numbers[num1%numbers.length];
var pixels2 = numbers[num2%numbers.length];
for(var i=0; i<13; i++) {
var cur_pixel1 = pixels1[i%pixels1.length];
var cur_pixel2 = pixels2[i%pixels2.length];
var pos_x = map(frac, 0.0, 1.0, cur_pixel1[0], cur_pixel2[0]);
var pos_y = map(frac, 0.0, 1.0, cur_pixel1[1], cur_pixel2[1]);
rect(pos_x * s, pos_y * s, s, s);
}
}
function digits_from_num(num) {
let digits = []
if (num < 10) {
digits.push(0);
}
else {
n1 = Math.floor(num / 10);
digits.push(n1);
}
n2 = Math.floor(num % 10);
digits.push(n2);
return digits;
}
function draw_clock(obj) {
let hour = obj.hours;
let minute = obj.minutes;
let second = obj.seconds;
let millis = obj.millis;
let alarm = obj.seconds_until_alarm;
var hour_pos = [40, height/2 - 3.5 * s];
// DEBUG CODE TO TEST HOUR ROLLOVER
// minute = minute + 35; // change based on current time
// if (minute == 60) {
// minute = 0;
// hour = 0;
// }
// is alarm going off?
if (alarm == 0) {
if (second % 2 == 0) {
background(0,0,100);
}
else {
background(100,100,0);
}
}
else {
background(50);
}
noStroke();
// is alarm going off in next 15 seconds
if (alarm > 0) {
fill(100);
rect(width-50, height-50, 40, 40);
if (alarm < 15.0) {
var box_w = map(alarm, 0, 15, width, 0);
var box_h = map(alarm, 0, 15, height, 0);
rect(width/2-box_w/2, height/2-box_h/2, box_w, box_h);
}
}
fill(255);
// HOURS
next_hour = (hour + 1) % 24;
digits1 = digits_from_num(hour);
digits2 = digits_from_num(next_hour);
if(second >= 50 && minute == 59 &&
(hour == 9 || hour == 19 || hour == 23)) {
// minute_fraction_tens = millis / 1000.0;
seconds_left = (second - 50) + millis / 1000.0;
hour_fraction_tens = seconds_left / 10.0;
}
else {
hour_fraction_tens = 0;
}
draw_number_interp(hour_fraction_tens, digits1[0], digits2[0], hour_pos[0] + 0.0 * 5 * s, hour_pos[1]);
// draw_number(digits1[0], hour_pos[0], hour_pos[1]);
if(second >= 55 && minute == 59) {
// minute_fraction_tens = millis / 1000.0;
seconds_left = (second - 55) + millis / 1000.0;
hour_fraction_ones = seconds_left / 5.0;
}
else {
hour_fraction_ones = 0;
}
draw_number_interp(hour_fraction_ones, digits1[1], digits2[1], hour_pos[0] + 1.0 * 5 * s, hour_pos[1]);
// draw_number(digits1[1], hour_pos[0] + 1.0 * 5 * s, hour_pos[1]);
// MINUTES
next_minute = (minute + 1) % 60;
digits1 = digits_from_num(minute);
digits2 = digits_from_num(next_minute);
if(second >= 58 && digits1[1] === 9) {
// minute_fraction_tens = millis / 1000.0;
seconds_left = (second - 58) + millis / 1000.0;
minute_fraction_tens = seconds_left / 2.0;
}
else {
minute_fraction_tens = 0;
}
draw_number_interp(minute_fraction_tens, digits1[0], digits2[0], hour_pos[0] + 2.5 * 5 * s, hour_pos[1]);
if(second === 59) {
minute_fraction_ones = millis / 1000.0;
}
else {
minute_fraction_ones = 0;
}
draw_number_interp(minute_fraction_ones, digits1[1], digits2[1], hour_pos[0] + 3.5 * 5 * s, hour_pos[1]);
// draw_number(digits1[1], hour_pos[0] + 3.5 * 5 * s, hour_pos[1]);
// SECONDS
next_second = (second + 1) % 60;
second_fraction = millis / 1000.0;
digits1 = digits_from_num(second);
digits2 = digits_from_num(next_second);
// print(digits1);
if(digits1[1] === 9 && millis > 500) {
second_fraction_tens = (millis - 500) / 500.0;
}
else {
second_fraction_tens = 0;
}
// draw the 10 second position
draw_number_interp(second_fraction_tens, digits1[0], digits2[0], hour_pos[0] + 5.0 * 5 * s, hour_pos[1]);
if(millis > 900) {
second_fraction_ones = (millis-900) / 100.0;
}
else {
second_fraction_ones = 0;
}
// draw the 1 second position
draw_number_interp(second_fraction_ones, digits1[1], digits2[1], hour_pos[0] + 6.0 * 5 * s, hour_pos[1]);
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/addons/p5.dom.js"></script>
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="debug.js"></script>
<script language="javascript" type="text/javascript" src="clock.js"></script>
<script language="javascript" type="text/javascript" src="runner.js"></script>
</head>
<body style="background-color:white">
<div class="outer">
<div class="inner">
<div id="canvasContainer"></div>
</div>
</div>
</table>
<body style="background-color:white">
<div class="outer">
<div class="inner">
<div id="canvasContainer"></div>
</div>
<div class="inner" id="controls">
<table>
<tr>
<td>debug</td>
<td id="checkboxDebug"></td>
</tr>
<tr>
<td>hours</td>
<td id="sliderHours"></td>
<td>minutes</td>
<td id="sliderMinutes"></td>
<td>seconds</td>
<td id="sliderSeconds"></td>
<td>millis</td>
<td id="sliderMillis"></td>
</tr>
<tr>
<td>alarm</td>
<td id="checkboxAlarm"></td>
<td>alarm_secs</td>
<td id="sliderAlarm"></td>
</tr>
</table>
</div>
</div>
</table>
</body>
<br>
<a href="part1.html">part1</a>
<a href="part2.html">part2</a>
<a href="clock.html">clock</a>
<a href="debug.html">debug</a>
</body>
var DEBUG=true;
var debugCheckbox;
var hourSlider;
var minSlider;
var secSlider;
var millisSlider;
var alarmSlider;
function debug_setup() {
debugCheckbox = createCheckbox('', false);
debugCheckbox.parent("checkboxDebug")
hourSlider = createSlider(0, 23, 12);
hourSlider.parent("sliderHours")
minSlider = createSlider(0, 59, 0);
minSlider.parent("sliderMinutes")
secSlider = createSlider(0, 59, 0);
secSlider.parent("sliderSeconds")
millisSlider = createSlider(0, 999, 0);
millisSlider.parent("sliderMillis")
alarmCheckbox = createCheckbox('', false);
alarmCheckbox.parent("checkboxAlarm")
alarmSlider = createSlider(0, 60, 0);
alarmSlider.parent("sliderAlarm")
}
<head>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="sketch.jpg" width="960" height="500"/>
<br>
<a href="part1.html">part1</a>
<a href="part2.html">part2</a>
<a href="clock.html">clock</a>
<a href="debug.html">debug</a>
</body>
// Update this function to draw you own maeda clock on a 960x500 canvas
function draw_clock(obj) {
// YOUR MAIN CLOCK CODE GOES HERE
background(50); // beige
fill(200); // dark grey
textSize(40);
textAlign(CENTER, CENTER);
text("YOUR MAEDA CLOCK CODE GOES HERE", width/2, height/2);
}
<head>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="sketch.jpg" width="960" height="500"/>
<br>
<a href="part1.html">part1</a>
<a href="part2.html">part2</a>
<a href="clock.html">clock</a>
<a href="debug.html">debug</a>
</body>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/addons/p5.dom.js"></script>
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="maeda_clock.js"></script>
<script language="javascript" type="text/javascript" src="runner.js"></script>
</head>
<body style="background-color:white">
<div class="outer">
<div class="inner">
<div id="canvasContainer"></div>
</div>
</div>
</table>
<br>
<a href="maeda_clock.js">(source code)</a>
<a href="part1.html">part1</a>
<a href="part2.html">part2</a>
<a href="clock.html">clock</a>
<a href="debug.html">debug</a>
</body>
var canvasWidth = 960;
var canvasHeight = 500;
var prevSec;
var millisRolloverTime;
var nextAlarm;
var debug_is_on = (typeof DEBUG !== 'undefined');
function setup () {
// create the drawing canvas, save the canvas element
var main_canvas = createCanvas(canvasWidth, canvasHeight);
main_canvas.parent('canvasContainer');
// this is true if debug.js is included
if(debug_is_on) {
debug_setup();
}
turn_off_alarm();
}
function turn_on_alarm() {
nextAlarm = millis() + 20000;
print("Alarm on: T minus 20 seconds");
}
function turn_off_alarm() {
nextAlarm = -1;
print("Alarm turned off");
}
function mouseClicked() {
if (debug_is_on && debugCheckbox.checked()) {
return;
}
if (nextAlarm > 0) {
turn_off_alarm();
}
else {
turn_on_alarm();
}
}
// taking ideas from http://cmuems.com/2016/60212/deliverables/deliverables-02/
function draw () {
var H, M, S, mils, alarm;
if (debug_is_on && debugCheckbox.checked()) {
hourSlider.removeAttribute('disabled');
minSlider.removeAttribute('disabled');
secSlider.removeAttribute('disabled');
millisSlider.removeAttribute('disabled');
alarmCheckbox.removeAttribute('disabled');
alarmSlider.removeAttribute('disabled');
H = hourSlider.value();
M = minSlider.value();
S = secSlider.value();
mils = millisSlider.value();
if (alarmCheckbox.checked()) {
alarm = alarmSlider.value();
}
else {
alarm = -1;
}
}
else {
// Fetch the current time
H = hour();
M = minute();
S = second();
if (nextAlarm > 0) {
now = millis();
var millis_offset = nextAlarm - now;
if (millis_offset < -10000 ){
// turn off alarm
nextAlarm = -1;
alarm = -1;
}
else if (millis_offset < 0) {
alarm = 0;
}
else {
alarm = millis_offset / 1000.0;
}
}
else {
alarm = -1;
}
// Reckon the current millisecond,
// particularly if the second has rolled over.
// Note that this is more correct than using millis()%1000;
if (prevSec != S) {
millisRolloverTime = millis();
}
prevSec = S;
mils = floor(millis() - millisRolloverTime);
if (debug_is_on) {
hourSlider.attribute('disabled','');
minSlider.attribute('disabled','');
secSlider.attribute('disabled','');
millisSlider.attribute('disabled','');
alarmCheckbox.attribute('disabled','');
alarmSlider.attribute('disabled','');
hourSlider.value(H);
minSlider.value(M);
secSlider.value(S);
millisSlider.value(mils);
alarmCheckbox.checked(alarm >= 0);
alarmSlider.value(alarm);
}
}
obj = {};
obj.hours = H;
obj.minutes = M;
obj.seconds = S;
obj.millis = mils;
obj.seconds_until_alarm = alarm;
draw_clock(obj);
}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
// note: this file is poorly named - it can generally be ignored.
// helper functions below for supporting blocks/purview
function saveBlocksImages(doZoom) {
if(doZoom == null) {
doZoom = false;
}
// generate 960x500 preview.jpg of entire canvas
// TODO: should this be recycled?
var offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 960;
offscreenCanvas.height = 500;
var context = offscreenCanvas.getContext('2d');
// background is flat white
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 960, 500);
context.drawImage(this.canvas, 0, 0, 960, 500);
// save to browser
var downloadMime = 'image/octet-stream';
var imageData = offscreenCanvas.toDataURL('image/jpeg');
imageData = imageData.replace('image/jpeg', downloadMime);
p5.prototype.downloadFile(imageData, 'preview.jpg', 'jpg');
// generate 230x120 thumbnail.png centered on mouse
offscreenCanvas.width = 230;
offscreenCanvas.height = 120;
// background is flat white
context = offscreenCanvas.getContext('2d');
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 230, 120);
if(doZoom) {
// pixelDensity does the right thing on retina displays
var pd = this._pixelDensity;
var sx = pd * mouseX - pd * 230/2;
var sy = pd * mouseY - pd * 120/2;
var sw = pd * 230;
var sh = pd * 120;
// bounds checking - just displace if necessary
if (sx < 0) {
sx = 0;
}
if (sx > this.canvas.width - sw) {
sx = this.canvas.width - sw;
}
if (sy < 0) {
sy = 0;
}
if (sy > this.canvas.height - sh) {
sy = this.canvas.height - sh;
}
// save to browser
context.drawImage(this.canvas, sx, sy, sw, sh, 0, 0, 230, 120);
}
else {
// now scaledown
var full_width = this.canvas.width;
var full_height = this.canvas.height;
context.drawImage(this.canvas, 0, 0, full_width, full_height, 0, 0, 230, 120);
}
imageData = offscreenCanvas.toDataURL('image/png');
imageData = imageData.replace('image/png', downloadMime);
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment