ock now has ROman Numeral support
parent
cc68a1be1c
commit
4d71953cae
|
|
@ -229,7 +229,7 @@
|
||||||
{ "id": "sweepclock",
|
{ "id": "sweepclock",
|
||||||
"name": "Sweep Clock",
|
"name": "Sweep Clock",
|
||||||
"icon": "sweepclock.png",
|
"icon": "sweepclock.png",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "Smooth sweep secondhand with single hour numeral",
|
"description": "Smooth sweep secondhand with single hour numeral",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"type":"clock",
|
"type":"clock",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Adrian Kirk 2021-03
|
||||||
|
* Simple Clock showing 1 numeral for the hour
|
||||||
|
* with a smooth sweep second.
|
||||||
|
*/
|
||||||
|
|
||||||
const screen_center_x = g.getWidth()/2;
|
const screen_center_x = g.getWidth()/2;
|
||||||
const screen_center_y = g.getHeight()/2;
|
const screen_center_y = g.getHeight()/2;
|
||||||
|
|
||||||
require("FontCopasetic40x58Numeric").add(Graphics);
|
require("FontCopasetic40x58Numeric").add(Graphics);
|
||||||
|
|
||||||
class Hand {
|
class Hand {
|
||||||
|
/**
|
||||||
|
* Pure virtual class for all Hand classes to extend.
|
||||||
|
* a hand class will have 1 main function
|
||||||
|
* moveTo which will move the hand to the given angle.
|
||||||
|
*/
|
||||||
|
moveTo(angle){}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThinHand extends Hand {
|
||||||
|
/**
|
||||||
|
* The thin hand is created from a simple line, so its easy and fast
|
||||||
|
* to draw.
|
||||||
|
*/
|
||||||
constructor(centerX,
|
constructor(centerX,
|
||||||
centerY,
|
centerY,
|
||||||
length,
|
length,
|
||||||
|
|
@ -18,23 +37,37 @@ class Hand {
|
||||||
this.red = red;
|
this.red = red;
|
||||||
this.green = green;
|
this.green = green;
|
||||||
this.blue = blue;
|
this.blue = blue;
|
||||||
|
// The last x and y coordinates (not the centre) of the last draw
|
||||||
this.last_x = centerX;
|
this.last_x = centerX;
|
||||||
this.last_y = centerY;
|
this.last_y = centerY;
|
||||||
|
// tolerance is the angle tolerance (from the last draw)
|
||||||
|
// in radians for a redraw to be called.
|
||||||
this.tolerance = tolerance;
|
this.tolerance = tolerance;
|
||||||
|
// draw test is a predicate (angle, time). This is called
|
||||||
|
// when the hand thinks that it does not have to draw (from its internal tests)
|
||||||
|
// to see if it has to draw because of another object.
|
||||||
this.draw_test = draw_test;
|
this.draw_test = draw_test;
|
||||||
|
// The current angle of the hand. Set to -1 initially
|
||||||
this.angle = -1;
|
this.angle = -1;
|
||||||
this.last_draw_time = null;
|
this.last_draw_time = null;
|
||||||
}
|
}
|
||||||
// method to move the hand to a new angle
|
// method to move the hand to a new angle
|
||||||
moveTo(angle){
|
moveTo(angle){
|
||||||
if(Math.abs(angle - this.angle) > this.tolerance || this.draw_test(this.angle,this.last_draw_time) ){
|
// first test to see of the angle called is beyond the tolerance
|
||||||
|
// for a redraw
|
||||||
|
if(Math.abs(angle - this.angle) > this.tolerance ||
|
||||||
|
// and then call the predicate to see if a redraw is needed
|
||||||
|
this.draw_test(this.angle,this.last_draw_time) ){
|
||||||
|
// rub out the old hand line
|
||||||
g.setColor(0,0,0);
|
g.setColor(0,0,0);
|
||||||
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
||||||
|
// Now draw the new hand line
|
||||||
g.setColor(this.red,this.green,this.blue);
|
g.setColor(this.red,this.green,this.blue);
|
||||||
x2 = this.centerX + this.length*Math.cos(angle);
|
x2 = this.centerX + this.length*Math.cos(angle);
|
||||||
y2 = this.centerY + this.length*Math.sin(angle);
|
y2 = this.centerY + this.length*Math.sin(angle);
|
||||||
g.setColor(this.red,this.green,this.blue);
|
g.setColor(this.red,this.green,this.blue);
|
||||||
g.drawLine(this.centerX, this.centerY, x2, y2);
|
g.drawLine(this.centerX, this.centerY, x2, y2);
|
||||||
|
// and store the last draw details for the next call
|
||||||
this.last_x = x2;
|
this.last_x = x2;
|
||||||
this.last_y = y2;
|
this.last_y = y2;
|
||||||
this.angle = angle;
|
this.angle = angle;
|
||||||
|
|
@ -46,7 +79,11 @@ class Hand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThickHand {
|
class ThickHand extends Hand {
|
||||||
|
/**
|
||||||
|
* The thick hand is created from a filled polygone, so its slower to
|
||||||
|
* draw so to be used sparingly with few redraws
|
||||||
|
*/
|
||||||
constructor(centerX,
|
constructor(centerX,
|
||||||
centerY,
|
centerY,
|
||||||
length,
|
length,
|
||||||
|
|
@ -130,28 +167,31 @@ class ThickHand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The force draw is set to true to force all objects to redraw themselves
|
||||||
let force_redraw = false;
|
let force_redraw = false;
|
||||||
let seconds_hand = new Hand(screen_center_x,
|
// The seconds hand is the main focus and is set to redraw on every cycle
|
||||||
|
let seconds_hand = new ThinHand(screen_center_x,
|
||||||
screen_center_y,
|
screen_center_y,
|
||||||
100,
|
100,
|
||||||
0,
|
0,
|
||||||
(angle, last_draw_time) => false,
|
(angle, last_draw_time) => false,
|
||||||
1.0,0.0,0.0);
|
1.0,0.0,0.0);
|
||||||
|
// The minute hand is set to redraw at a 250th of a circle,
|
||||||
|
// when the second hand is ontop or slighly overtaking
|
||||||
|
// or when a force_redraw is called
|
||||||
let minutes_hand_redraw = function(angle, last_draw_time){
|
let minutes_hand_redraw = function(angle, last_draw_time){
|
||||||
return force_redraw || (seconds_hand.angle > angle &&
|
return force_redraw || (seconds_hand.angle > angle &&
|
||||||
Math.abs(seconds_hand.angle - angle) <2*Math.PI/25 &&
|
Math.abs(seconds_hand.angle - angle) <2*Math.PI/25 &&
|
||||||
new Date().getTime() - last_draw_time.getTime() > 500);
|
new Date().getTime() - last_draw_time.getTime() > 500);
|
||||||
};
|
};
|
||||||
|
let minutes_hand = new ThinHand(screen_center_x,
|
||||||
let minutes_hand = new Hand(screen_center_x,
|
|
||||||
screen_center_y,
|
screen_center_y,
|
||||||
80,
|
80,
|
||||||
2*Math.PI/250,
|
2*Math.PI/250,
|
||||||
minutes_hand_redraw,
|
minutes_hand_redraw,
|
||||||
1.0,1.0,1.0);
|
1.0,1.0,1.0);
|
||||||
|
// The hour hand is a thick hand so we have to redraw when the minute hand
|
||||||
|
// overlaps from its behind andle coverage to its ahead angle coverage.
|
||||||
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
||||||
return force_redraw || (seconds_hand.angle >= angle_from &&
|
return force_redraw || (seconds_hand.angle >= angle_from &&
|
||||||
seconds_hand.angle <= angle_to &&
|
seconds_hand.angle <= angle_to &&
|
||||||
|
|
@ -174,15 +214,16 @@ function draw_clock(){
|
||||||
draw_hours(date);
|
draw_hours(date);
|
||||||
force_redraw = false;
|
force_redraw = false;
|
||||||
}
|
}
|
||||||
|
// drawing the second the millisecond as we need the fine gradation
|
||||||
|
// for the sweep second hand.
|
||||||
function draw_seconds(date){
|
function draw_seconds(date){
|
||||||
seconds = date.getSeconds() + date.getMilliseconds()/1000;
|
seconds = date.getSeconds() + date.getMilliseconds()/1000;
|
||||||
seconds_frac = seconds / 60;
|
seconds_frac = seconds / 60;
|
||||||
seconds_angle = 2*Math.PI*seconds_frac - (Math.PI/2.0);
|
seconds_angle = 2*Math.PI*seconds_frac - (Math.PI/2.0);
|
||||||
seconds_hand.moveTo(seconds_angle);
|
seconds_hand.moveTo(seconds_angle);
|
||||||
}
|
}
|
||||||
|
// drawing the minute includes the second and millisec to make the
|
||||||
|
// movement as continuous as possible.
|
||||||
function draw_mins(date,seconds_angle){
|
function draw_mins(date,seconds_angle){
|
||||||
mins = date.getMinutes() + date.getSeconds()/60 + date.getMilliseconds()/(60*1000);
|
mins = date.getMinutes() + date.getSeconds()/60 + date.getMilliseconds()/(60*1000);
|
||||||
mins_frac = mins / 60;
|
mins_frac = mins / 60;
|
||||||
|
|
@ -199,17 +240,159 @@ function draw_hours(date){
|
||||||
hours_angle = 2*Math.PI*hours_frac - (Math.PI/2.0);
|
hours_angle = 2*Math.PI*hours_frac - (Math.PI/2.0);
|
||||||
redraw = hours_hand.moveTo(hours_angle);
|
redraw = hours_hand.moveTo(hours_angle);
|
||||||
if(redraw){
|
if(redraw){
|
||||||
//console.log(date.getSeconds() + " redraw hours");
|
console.log(date.getSeconds() + " redraw hours");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let curr_hour_x = -1;
|
/**
|
||||||
let curr_hour_y = -1;
|
* We want to be able to change the font so we set up
|
||||||
let curr_hour_str = null;
|
* pure virtual for all fonts implementtions to use
|
||||||
const font_width = 40;
|
*/
|
||||||
const font_height = 58;
|
class NumeralFont {
|
||||||
|
/**
|
||||||
|
* The screen dimensions of what we are going to
|
||||||
|
* display for the given hour.
|
||||||
|
*/
|
||||||
|
getDimensions(hour){return [0,0];}
|
||||||
|
/**
|
||||||
|
* The characters that are going to be returned for
|
||||||
|
* the hour.
|
||||||
|
*/
|
||||||
|
hour_txt(hour){ return ""; }
|
||||||
|
/**
|
||||||
|
* method to draw text at the required coordinates
|
||||||
|
*/
|
||||||
|
draw(hour_txt,x,y){ return "";}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CopasetFont extends NumeralFont{
|
||||||
|
constructor(){
|
||||||
|
this.dimension_map = {
|
||||||
|
1 : [20,58],
|
||||||
|
2 : [30,58],
|
||||||
|
3 : [30,58],
|
||||||
|
4 : [30,58],
|
||||||
|
5 : [30,58],
|
||||||
|
6 : [40,58],
|
||||||
|
7 : [30,58],
|
||||||
|
8 : [40,58],
|
||||||
|
9 : [40,58],
|
||||||
|
10: [50,40],
|
||||||
|
11: [40,58],
|
||||||
|
12: [40,58]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
getDimensions(hour){return this.dimension_map[hour];}
|
||||||
|
hour_txt(hour){ return hour.toString(); }
|
||||||
|
draw(hour_txt,x,y){
|
||||||
|
/* dim = [20,58];
|
||||||
|
g.setColor(0.5,0,0);
|
||||||
|
g.fillPoly([x,y,
|
||||||
|
x+dim[0],y,
|
||||||
|
x+dim[0],y+dim[1],
|
||||||
|
x,y+dim[1]
|
||||||
|
]);
|
||||||
|
g.setColor(1.0,1.0,1.0);*/
|
||||||
|
g.setFontCopasetic40x58Numeric();
|
||||||
|
g.drawString(hour_txt,x,y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RomanNumeralFont extends NumeralFont{
|
||||||
|
constructor(){
|
||||||
|
this.txt_map = {
|
||||||
|
1 : 'I',
|
||||||
|
2 : 'II',
|
||||||
|
3 : 'III',
|
||||||
|
4 : 'IV',
|
||||||
|
5 : 'V',
|
||||||
|
6 : 'VI',
|
||||||
|
7 : 'VII',
|
||||||
|
8 : 'VIII',
|
||||||
|
9 : 'IX',
|
||||||
|
10: 'X',
|
||||||
|
11: 'XI',
|
||||||
|
12: 'XII'
|
||||||
|
};
|
||||||
|
this.dimension_map = {
|
||||||
|
1 : [10,40],
|
||||||
|
2 : [25,40],
|
||||||
|
3 : [40,40],
|
||||||
|
4 : [40,40],
|
||||||
|
5 : [30,40],
|
||||||
|
6 : [40,40],
|
||||||
|
7 : [60,40],
|
||||||
|
8 : [70,40],
|
||||||
|
9 : [40,40],
|
||||||
|
10: [20,40],
|
||||||
|
11: [40,40],
|
||||||
|
12: [60,40]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
getDimensions(hour){ return this.dimension_map[hour];}
|
||||||
|
hour_txt(hour){ return this.txt_map[hour]; }
|
||||||
|
draw(hour_txt,x,y){
|
||||||
|
g.setFont("Vector",40);
|
||||||
|
g.drawString(hour_txt,x,y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HourScriber {
|
||||||
|
constructor(numeral_font){
|
||||||
|
this.numeral_font = numeral_font;
|
||||||
|
this.curr_numeral_font = numeral_font;
|
||||||
|
this.curr_hour_x = -1;
|
||||||
|
this.curr_hour_y = -1;
|
||||||
|
this.curr_hour_str = null;
|
||||||
|
this.radius = 70;
|
||||||
|
}
|
||||||
|
setNumeralFont(numeral_font){
|
||||||
|
this.numeral_font = numeral_font;
|
||||||
|
}
|
||||||
|
drawHour(hours){
|
||||||
|
hours_frac = hours / 12;
|
||||||
|
angle = 2*Math.PI*hours_frac;
|
||||||
|
dimensions = this.numeral_font.getDimensions(hours);
|
||||||
|
// we set the radial coord to be in the middle
|
||||||
|
// of the drawn text.
|
||||||
|
x = screen_center_x + this.radius*Math.sin(angle) - dimensions[0]/2;
|
||||||
|
y = screen_center_y - this.radius*Math.cos(angle) - dimensions[1]/2;
|
||||||
|
txt = this.numeral_font.hour_txt(hours);
|
||||||
|
if(this.curr_hour_str != null && this.curr_hour_str != txt){
|
||||||
|
g.setColor(0,0,0);
|
||||||
|
this.curr_numeral_font.draw(this.curr_hour_str,
|
||||||
|
this.curr_hour_x,
|
||||||
|
this.curr_hour_y);
|
||||||
|
console.log("erasing old hour");
|
||||||
|
}
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
this.numeral_font.draw(txt,x,y);
|
||||||
|
this.curr_numeral_font = this.numeral_font;
|
||||||
|
this.curr_hour_x = x;
|
||||||
|
this.curr_hour_y = y;
|
||||||
|
this.curr_hour_str = txt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let numeral_fonts = [new CopasetFont(), new RomanNumeralFont()];
|
||||||
|
let numeral_fonts_index = 0;
|
||||||
|
let hour_scriber = new HourScriber(numeral_fonts[numeral_fonts_index]);
|
||||||
|
|
||||||
|
function next_font(){
|
||||||
|
numeral_fonts_index = numeral_fonts_index + 1;
|
||||||
|
if(numeral_fonts_index >= numeral_fonts.length){
|
||||||
|
numeral_fonts_index = 0;
|
||||||
|
}
|
||||||
|
hour_scriber.setNumeralFont(
|
||||||
|
numeral_fonts[numeral_fonts_index]);
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
function draw_hour_digit(date){
|
function draw_hour_digit(date){
|
||||||
hours = date.getHours() % 12;
|
hours = date.getHours() % 12;
|
||||||
|
//hours = date.getMinutes() % 12;
|
||||||
mins = date.getMinutes();
|
mins = date.getMinutes();
|
||||||
if(mins > 30){
|
if(mins > 30){
|
||||||
hours = (hours +1) % 12;
|
hours = (hours +1) % 12;
|
||||||
|
|
@ -217,25 +400,12 @@ function draw_hour_digit(date){
|
||||||
if(hours == 0){
|
if(hours == 0){
|
||||||
hours = 12;
|
hours = 12;
|
||||||
}
|
}
|
||||||
hours_frac = hours / 12;
|
//hours = 1;
|
||||||
angle = 2*Math.PI*hours_frac - (Math.PI/2.0);
|
hour_scriber.drawHour(hours);
|
||||||
x = screen_center_x + 70*Math.cos(angle) - font_width/2;
|
|
||||||
y = screen_center_y + 70*Math.sin(angle) - font_height/2;
|
|
||||||
g.setFontCopasetic40x58Numeric();
|
|
||||||
txt = hours.toString();
|
|
||||||
if(curr_hour_str != null && curr_hour_str != txt){
|
|
||||||
g.setColor(0,0,0);
|
|
||||||
g.drawString(curr_hour_str,curr_hour_x,curr_hour_y);
|
|
||||||
console.log("erasing old hour");
|
|
||||||
}
|
|
||||||
g.setColor(1,1,1);
|
|
||||||
g.drawString(txt,x,y);
|
|
||||||
curr_hour_x = x;
|
|
||||||
curr_hour_y = y;
|
|
||||||
curr_hour_str = txt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The interval reference for updating the clock
|
// Boiler plate code for setting up the clock
|
||||||
|
// below
|
||||||
let intervalRef = null;
|
let intervalRef = null;
|
||||||
|
|
||||||
function clearTimers(){
|
function clearTimers(){
|
||||||
|
|
@ -250,6 +420,9 @@ function startTimers(){
|
||||||
draw_clock();
|
draw_clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The clock redraw is set to 100ms. This is the smallest number
|
||||||
|
// that give the (my) human eye the illusion of a continious sweep
|
||||||
|
// second hand.
|
||||||
function scheduleDrawClock(){
|
function scheduleDrawClock(){
|
||||||
if(intervalRef) clearTimers();
|
if(intervalRef) clearTimers();
|
||||||
intervalRef = setInterval(draw_clock, 100);
|
intervalRef = setInterval(draw_clock, 100);
|
||||||
|
|
@ -292,7 +465,7 @@ startTimers();
|
||||||
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
||||||
|
|
||||||
function button1pressed(){
|
function button1pressed(){
|
||||||
console.log("button 1 pressed");
|
next_font();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle button 1 being pressed
|
// Handle button 1 being pressed
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue