Data will confess
"If you torture the data for long enough, in the end they will confess". Ronald H. Coase (Nobel Prize in Economics in 1991)
martes, 1 de marzo de 2016
Open source Connect IQ face for Garmin watches - Analog & Skies (lunar phase and sunlight terminator)
Garmin empowers their watches by providing the Connect IQ SDK so that we can program our own apps, faces, etc. I wrote this open source (under GNU GPL v3.0) watch face which includes the calculations for the lunar phase and a sunlight terminator. It is space efficient and energy-efficient, because many of the calculations are only recomputed once a day (as opposed to the default every-minute), like the lunar phase or the sun declination. The sun declination is calculated in order to know the exact shape of the sunlight terminator, which is different, depending on the date. See, for example, the difference between summer and winter in the next two images:
The rest of the information displayed is explained in the following scheme:
The lunar phase and the sunlight terminator code are in two separate classes. They should be declared as class members, and initialized only once:
class AnalogBarsView extends Ui.WatchFace {
var moon, earth;
function initialize() {
WatchFace.initialize();
moon = new Moon(Ui.loadResource(Rez.Drawables.moon), 48, 18, 3);
earth = new SunlightTerminator(Ui.loadResource(Rez.Drawables.earth), 100, 50, 86, 20);
}
function onUpdate(dc) {
earth.updateable_sunlight_terminator(dc, time_sec, dateinfo, clockTime);
moon.updateable_calcmoonphase(dc, dateinfo, clockTime.hour);
// ...
}
}
These two calls in onUpdate will not recompute all their values on every call. Instead, they will conserve the old values and recalculate them every so often.
Here's Moon.mc:
using Toybox.Math as Math;
using Toybox.System as Sys;
using Toybox.WatchUi as Ui;
using Toybox.Graphics as Gfx;
// By using updateable_calcmoonphase, the moon phase picture will be drawn,
// but it will be only recomputed once a day.
class Moon {
var moon_width;
var moon_bitmap;
var moonx, moony;
var c_phase, t_phase; // day of month when last updated
var c_moon_label, c_moon_y; // y1, y2, y1, y2, ... for the moon shadow
function initialize(bitmap, width, x, y) {
moon_bitmap = bitmap; // Ui.loadResource(Rez.Drawables.moon);
moon_width = width;
moonx = x;
moony = y;
t_phase = -1;
}
function calcmoonphase(day, month, year) {
var r = (year % 100);
r = (r % 19);
if (r>9) {
r = r - 19;
}
r = ((r * 11) % 30) + month + day;
if (month<3) {
r = r + 2;
}
r = 1.0*r - 8.3 + 0.5;
r = (r.toNumber() % 30);
if (r < 0) {
r = r + 30;
}
return r;
}
function updateable_calcmoonphase(dc, dateinfo, hour) {
if (t_phase != dateinfo.day) {
t_phase = dateinfo.day;
c_phase = calcmoonphase(dateinfo.day, dateinfo.month, dateinfo.year);
if (hour > 12) { // change it at noon
c_phase = (c_phase + 1) % 30;
}
calc_drawmoon(c_phase);// updates c_moon_label and c_moon_y
}
drawmoon(dc, moonx, moony); // uses c_moon_y
return c_phase;
}
function drawmoon(dc, moonx, moony) {
dc.drawBitmap(moonx, moony, moon_bitmap);
var x, xby2;
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE);
for (x=1; x<moon_width; x++) {
xby2 = x*2;
if (c_moon_y[xby2] >= 0) {
dc.drawLine(moonx+x, moony+c_moon_y[xby2], moonx+x, moony+c_moon_y[xby2+1]);
} else {
dc.drawLine(moonx+x, moony+1, moonx+x, moony-c_moon_y[xby2]);
dc.drawLine(moonx+x, moony-c_moon_y[xby2+1], moonx+x, moony+moon_width);
}
}
}
function calc_drawmoon(moonphase) {
var mw = moon_width; // image width
var c = mw/2; // image center
var intc = c.toNumber();
var r = (mw-2)*0.5-0.5; // radius depends on image
c_moon_label = "";
var step = 1;
var r1edge= -1;
var rSedge= 0;
var r1rest= -1;
var rSrest= 0;
var edgelight = false;
if (moonphase <= 8) {
c_moon_label = "wax.";
r1edge = intc; rSedge = step;
r1rest = intc; rSrest = -step;
edgelight = true;
if (moonphase == 8) {
r1edge = -1; rSedge = 0;
} else {
if (moonphase == 0) {
c_moon_label = "new";
}
}
} else {
if (moonphase <=16) {
c_moon_label = "wax.";
r1rest = -1; rSrest = 0;
r1edge = intc; rSedge = -step;
edgelight = false;
if (moonphase == 16) {
c_moon_label = "full";
r1edge = -1; rSedge = 0;
}
} else {
c_moon_label = "wan.";
if (moonphase <=23) {
r1rest = -1; rSrest = 0;
r1edge = intc; rSedge = step;
edgelight = false;
if (moonphase == 23) {
r1edge = -1; rSedge = 0;
r1rest = intc; rSrest = step;
}
} else {
r1edge = intc; rSedge = -step;
r1rest = intc; rSrest = step;
edgelight = true;
}
}
}
var a;
if (moonphase > 16) {
a = 1.0 - (moonphase - 16.0) / 7.0;
} else {
a = 1.0 - moonphase/8.0;
}
c_moon_y = new [mw*2+2];
var i;
for (i = 0; i<mw*2; i++) {
c_moon_y[i] = 0;
}
var x, xx, ra, sq, y1, y2, xby2;
for (x=r1rest; x<=mw && x>=1; x+=rSrest) {
//dc.drawLine(moonx+x, moony+1, moonx+x, moony+mw);
xby2 = 2*x;
c_moon_y[xby2] = 1;
c_moon_y[xby2+1] = mw;
}
for (x=r1edge; x<=mw && x>=1; x+=rSedge) {
xx = (x-c)/a;
ra = r*r - xx*xx;
xby2 = 2*x;
if (ra > 0) {
sq = Math.sqrt(ra);
y1 = c - sq + 0.5;
y1 = y1.toNumber();
y2 = c + sq + 0.5;
y2 = y2.toNumber();
if (edgelight) {
// dc.drawLine(moonx+x, moony+y1, moonx+x, moony+y2);
c_moon_y[xby2] = y1;
c_moon_y[xby2+1] = y2;
} else {
//dc.drawLine(moonx+x, moony+1, moonx+x, moony+y1);
//dc.drawLine(moonx+x, moony+y2, moonx+x, moony+mw);
c_moon_y[xby2] = -y1;
c_moon_y[xby2+1] = -y2;
}
} else {
if (!edgelight) {
// dc.drawLine(moonx+x, moony+1, moonx+x, moony+mw);
c_moon_y[xby2] = 1;
c_moon_y[xby2+1] = mw;
}
}
}
return c_moon_label;
}
}
And here's SunlightTerminator.mc:
using Toybox.Math as Math;
using Toybox.System as Sys;
using Toybox.WatchUi as Ui;
using Toybox.Graphics as Gfx;
// By using updateable_sunlight_terminator the terminator will be drawn, but it will
// only be recalculated every 300 seconds. The declination will be recalculated daily.
class SunlightTerminator {
var earthx, earthy, width, height;
var c_declination, t_declination; // day of month when last updated
var c_sunlight, t_sunlight; // time in seconds of next update.
var image;
function initialize(earth_bitmap, w, h, x, y) {
image = earth_bitmap;
t_declination = -1;
t_sunlight = -1;
earthx = x;
earthy = y;
width = w;
height = h;
}
function sun_declination(dateinfo) {
var jul = julian( dateinfo.year, dateinfo.month, dateinfo.day);
var radians = Math.PI/180.0;
var lambda = ecliptic_longitude(jul, radians);
var obliquity = 23.439 * radians - 0.0000004 * radians * jul;
var delta = Math.asin(Math.sin(obliquity) * Math.sin(lambda));
if (delta == 0) {
return 0.000001;
} else {
return delta;
}
}
function updateable_sun_declination(dateinfo) {
if (t_declination != dateinfo.day) {
c_declination = sun_declination(dateinfo);
t_declination = dateinfo.day;
}
return c_declination;
}
function ecliptic_longitude(jul, radians) {
var meanlongitude = getAngle(280.461 * radians + 0.9856474 * radians * jul);
var meananomaly = getAngle(357.528 * radians + .9856003 * radians * jul);
return getAngle(meanlongitude + 1.915 * radians * Math.sin(meananomaly)
+ 0.02 * radians * Math.sin(2.0 * meananomaly));
}
// returns a floating point angle in the range 0 .. 2*pi
function getAngle(x) {
var b = 0.5*x / Math.PI;
var a = 2.0*Math.PI * (b - b.toNumber());
if (a < 0) {
a = 2.0*pi + a;
}
return a;
}
// between 1901 to 2099
function julian( y, m, d) {
var a = (m + 9)/12.0;
var b = (y + a.toNumber())/4.0;
var c = 275*m/9.0;
var l = -7 * b.toNumber() + c.toNumber() + d;
l = l.toNumber() + y*367;
return l - 730531;
}
function updateable_sunlight_terminator(dc, time_sec, dateinfo, time) {
if (time_sec.value() > t_sunlight) {
t_sunlight = time_sec.value()+300; // update interval of 300 seconds
updateable_sun_declination(dateinfo);
calc_sunlight_terminator(dc, c_declination, time, earthx, earthy, width, height);
// updates c_sunlight array
}
// uses c_sunlight array
draw_sunlight_terminator(dc, c_declination, time, earthx, earthy, width, height);
}
function draw_sunlight_terminator(dc, declination, time, earthx, earthy, width, height){
dc.drawBitmap(earthx, earthy, image);
//hide some unnecessary part of the map:
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE);
dc.drawLine(earthx,earthy,earthx+100,earthy);
dc.fillRectangle(earthx,earthy+47,earthx+100,earthy+50);
var x, x2;
if (declination < 0) {
for (x=1; x<=c_sunlight.size(); x+=1) {
x2 = earthx + x*2 - 1;
dc.drawLine(x2, earthy, x2, earthy+c_sunlight[x-1]);
}
} else {
for (x=1; x<=c_sunlight.size(); x+=1) {
x2 = earthx + x*2 - 1;
dc.drawLine(x2, earthy+c_sunlight[x-1], x2, earthy+height);
}
}
}
function calc_sunlight_terminator(dc, declination, time, earthx, earthy, width, height) {
var num_el = width / 2;
c_sunlight = new [num_el.toNumber()];
var hour = (((time.hour*3600-time.timeZoneOffset)%86400) - 43200 + time.min*60.0) / 3600.0;
//var lat1 = -1.5708; // -pi/2
//var lat2 = 1.5708; // pi/2
//var latrange = lat2 - lat1;
//lat1 = (-lat1+1.5708)/3.1416;
var latrange = 3.1416;
var lat1 = 1.0;
var x, y, longitude, latitude, x2;
x2 = 0;
if (declination < 0) {
for (x=2; x<=width; x+=2) {
longitude = (x-1.0)/width*6.2832-3.1416+hour/24*6.2832;
latitude = Math.atan(-Math.cos(longitude)/Math.tan(declination));
y = (-latitude + 1.5708) / latrange - (1-lat1);
y = y * height + 0.5;
y = y.toNumber();
//x2 = earthx + x - 1;
//dc.drawLine(x2, earthy, x2, earthy+y);
c_sunlight[x2] = y;
x2 += 1;
}
} else {
for (x=2; x<=width; x+=2) {
longitude = (x-1.0)/width*6.2832-3.1416+hour/24*6.2832;
latitude = Math.atan(-Math.cos(longitude)/Math.tan(declination));
y = (-latitude + 1.5708) / latrange - (1-lat1);
y = y * height + 0.5;
y = y.toNumber();
//x2 = earthx + x - 1;
//dc.drawLine(x2, earthy+y, x2, earthy+height);
c_sunlight[x2] = y;
x2 +=1;
}
}
}
}
domingo, 17 de enero de 2016
Open source Connect IQ face for Garmin watches - Analog & Bars
Garmin empowers their watches by providing the Connect IQ SDK so that we can program our own apps, faces, etc. However, there's little documentation, and even almost no Open Source apps. So, let me publish (under GNU GPL v3.0) a watch face I wrote: Analog & Bars [source code | prg for vivoactive | Garmin store]. Maybe you can improve it and publish your own.
- Note on the Garmin Vivoactive device: It is a thin and comfortable watch designed for tracking sport activities and displaying some basic information. Do not expect anything similar to an Android operating system. It is very basic and very well optimized for its purposes. The battery lasts for weeks. The only drawback it might have is the bluetooth connection with your phone. I never managed to connect it to my own Galaxy Note 3 phone, which is among the supported ones (I factory reset it to make sure, plus it works with other bluetooth devices). The Garmin tech service was very patient but all they could say is they don't know why a supported phone doesn't work with it. I asked them what happens when Android phones get their OS updated: does Garmin check if the connection to that device continues to work? They don't. I have to say, my Garmin watch did connect to many other phones, including some which are not officially supported by Garmin. If your phone is an unlucky one, each time you charge it, it will transmit the tracking data to your Garmin account. Unless you only have Linux at home, for which Garmin does not provide support.
The app may be available on the Garmin Connect Apps site, uploaded by myself or by someone else with their own modifications. I will not maintain it and I will not fix bugs, I will not add new functionalities, and I will not compile it for different devices.
I tested it on Garmin Vivoactive. You should be able to compile it for other square watches but the background is stored as a PNG with the Vivoactive screen size . This should not be a big issue, as I provide Matlab/Octave code for generating the background image. It is transparent, which is pretty convenient if you want to display something behind the minute ticks. Just display the background in last place, as I do in my app.
% vivoactive watch face background generation. Matlab / Octave
% Boyan Bonev, January 2016
function watchface()
im = uint8(zeros(148,205));
%hours
cx = 205/2; cy = 148/2+0.5;
r1 = 67.5; r2 = 150;
%im = ticks(im,12, r1, r2, cy, cx, [-0.01 -0.005 0 0.005 0.01 ]);
im = ticks(im,12, r1, r2, [cy cy-1 cy+1], [cx cx-1 cx+1], 0);
im(3:end-2,cx-1:cx+1) = 0;
m=16;
im(1+m:148-m,1+m:205-m) = 0;
imhours = im;
%minutes
r1 = 66.5; r2 = 150;
im = uint8(zeros(148,205));
im = ticks(im,12*5, r1, r2, cy, cx, [-0.006 -0.005 0 0.005 0.006]);
m=10;
im(1+m:148-m,1+m:205-m) = 0;
r1 = 100; r2 = 150;
im = ticks(im,2, r1, r2, cy, cx, [-0.005, 0, 0.005]+pi/6/5*9);
im = ticks(im,2, r1, r2, cy, cx, [-0.005, 0, 0.005]+pi/6/5*21);
im(3:end-2,cx-1:cx+1) = 0;
immins = im;
imr = imhours;
img = imhours;
imb = imhours;
imr(immins==255) = 255;
rgb = cat(3,imr,img,imb);
imwrite(rgb,'face.png');
im2 = immins/255;
im2(imhours==255) = 2;
%imagesc(im2);
cmap = [0 0 0; 1 0 0; 1 1 1];
imwrite(im2, cmap, 'face2.png', 'png', 'Transparency', 0);
end % of function
function imout = ticks(im,n,r1,r2, cys, cxs, offsets)
for offset = offsets
for i=0:n-1
angle = 2*pi/n*i + offset-pi/2;
for r=r1:r2
x0 = cos(angle)*r;
y0 = sin(angle)*r;
for cy = cys
for cx = cxs
x = round(x0+cx);
y = round(y0+cy);
if y>0 && x>0 && y<=148 && x<=205
im(y,x) = 255;
end
end
end
end
end
end
imout = im;
end % of functionYou don't need to run this code if you are going to use the Vivoactive device. Just download the result:
Due to the transparency, you may not see the white hour ticks. Fill a black rectangle of the size of the watch screen before you display it, and you'll see the hours and minutes ticks like in the app's screenshot on the top.
In this watch face, the bars represent the steps history for the last week. The ones in red are weekend days. The bar in gray is the goal, and the gray digits on its right are the number of steps the goal bar represents. The blue symbols represent the memory, battery, sound, vibration, phone connection, alarms, and the ones you don't see here are the ones for notifications and for sleep mode. They were all included in a font which I generated for the purpose. See the Connect IQ documentation for generating fonts. Here is how the app's fonts look like:
They come with a text file indicating the coordinates of each character. Bitmap Font Generator is a freeware that generates them, recommended by Garmin's Connect IQ guide. My antivirus complained about it, but I believe that was a false positive. I was able to run it in Linux, with Wine. (I didn't try to run the Connect IQ SDK on Linux). Generate the bitmaps with 128x128 size. You don't need to smooth them, as the Garmin device will display only a few colors anyway. Generate their descriptor as a text file. Finally, don't forget to include the fonts in the resources.xml file of your project. I used the filter option because I don't use all the symbols.
A good programming practice would have been to extract all the strings to the strings.xml file. It makes it easier to keep the app multilanguage.
And here goes the code. I've zipped the whole project here: source code. By the way, code is code both in singular and plural. Unless you mean "access codes".
// Boyan Bonev, January 2016
//
// This app needs the Connect IQ SDK
to be compiled.
// It is designed for the Vivoactive
watch by Garmin.
// It may work for other platforms,
but you need to
// compile it yourself.
// I will NOT maintain it and I will
NOT respond to inquiries
// for upgrades or bug fixes.
However, feel free to
// modify it and distribute it, as
long as you keep it Free Software.
// You may also acknowledge the
original author if you feel like doing it.
//
// The present code is licensed
under the GNU GPL V3.0 license http://www.gnu.org/licenses/gpl.txt
// This doesn't apply to the fonts
and other resources.
//
// If you have questions about
Connect IQ and C Monkey, please,
// make them public via the Garmin
Forum.
//
using Toybox.WatchUi as Ui;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.Lang as Lang;
using Toybox.Sensor as Sens;
using Toybox.ActivityMonitor as Act;
using Toybox.Attention as Att;
using Toybox.Math as Math;
using Toybox.Time as Time;
using Toybox.Time.Gregorian as Greg;
class AnalogBarsView extends Ui.WatchFace {
var font, font2,
fontnumbers, fsyms;
var h, w, h2, w2;
function initialize() {
WatchFace.initialize();
font2 = Ui.loadResource(Rez.Fonts.sawade20);
fontnumbers = Ui.loadResource(Rez.Fonts.sawade35);
fsyms = Ui.loadResource(Rez.Fonts.typicons22);
}
//!
Load your resources here
function onLayout(dc) {
//
setLayout(Rez.Layouts.WatchFace(dc));
}
//!
Called when this View is brought to the foreground. Restore
//!
the state of this View and prepare it to be shown. This includes
//!
loading resources into memory.
function onShow() {
}
//!
Update the view
function onUpdate(dc) {
w = dc.getWidth();
h = dc.getHeight();
h2 = h/2;
w2 = w/2;
var n;
var dateinfo =
Greg.info(Time.now(), Time.FORMAT_SHORT);
//Sys.println(dateinfo.day_of_week);
var daynames = ["U","M","T","W","R","F","S"];
//
Clear the screen
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE);
dc.fillRectangle(0,0,w,h);
var settings =
Sys.getDeviceSettings();
var systemStats =
Sys.getSystemStats();
var actinfo =
Act.getInfo();
// Plot Bars
var stepsnorm =
actinfo.stepGoal * 3;
if (stepsnorm <= 0){
stepsnorm
= 10000;
}
var steps =
actinfo.steps;
if (steps >
stepsnorm) {
stepsnorm
= steps;
}
// steps history
var hist =
Act.getHistory();
var color;
var dn;
for
(n=0;n<hist.size();n++){
dn
= (dateinfo.day_of_week+5-n) % 7;
if (dn == 6 || dn == 0)
{ color = Gfx.COLOR_RED;}
else { color =
Gfx.COLOR_GREEN;}
bar(dc,
13-n, 1.0*hist[n].steps / stepsnorm,
color,
daynames[dn]);
}
// steps today
dn
= dateinfo.day_of_week-1;
if (dn == 6 || dn == 0)
{ color = Gfx.COLOR_RED;}
else { color =
Gfx.COLOR_GREEN;}
bar(dc,
14, 1.0*steps / stepsnorm,
color,
daynames[dn], true);
// goal
bar(dc,
15, 1.0*actinfo.stepGoal / stepsnorm,
Gfx.COLOR_DK_GRAY,
"g");
var s =
actinfo.stepGoal;
var sn;
var pos = -1;
for (n=10000;n>0;n=n/10)
{
sn
= s/n;
if (pos == -1) {
if (sn > 0) { // start printing
pos
= 82;
}
else {
continue;
}
}
dc.drawText(w-23,
pos, font2, ""+sn, Gfx.TEXT_JUSTIFY_CENTER);
s
= s - sn*n;
pos
= pos + 11;
}
// write the date
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var weekdays = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
dc.setColor(Gfx.COLOR_GREEN,
Gfx.COLOR_TRANSPARENT);
dc.drawText(35,
20, font2,
weekdays[dateinfo.day_of_week-1],
Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(35,
32, font2,
months[dateinfo.month-1]+
"
"+
dateinfo.day,
Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(35,
44, font2,
""+ dateinfo.year,
Gfx.TEXT_JUSTIFY_CENTER);
// Plot Symbols
//a:sun b:battery
c:battery d:battery e:battery f:bell g:vibrate h:vibrate
//i:bulb j:gps
k:bubble l:bubbles m:notes n:notes o:phone p:phone q:chrono
//r:thumbsdown
s:thumbsup t:crossed u:user v:sound w:nosound
//Check the
pictures in resources/fonts/typicons22_0.png
//Be aware that the
characters loading filter is set in resource.xml
var flags = "";
if (settings.tonesOn)
{flags = flags + "v";} else {flags = flags + "w";}
if (settings.vibrateOn)
{flags = flags + "h";}
if
(settings.phoneConnected) {flags = flags + "p";}
if
(settings.notificationCount>0) {flags = flags + "l";}
if (settings.alarmCount>0)
{flags = flags + "f";}
if
(actinfo.moveBarLevel>Act.MOVE_BAR_LEVEL_MIN) {flags = flags + "q";}
if
(actinfo.isSleepMode) {flags = flags + "i";}
dc.setColor(Gfx.COLOR_BLUE, Gfx.COLOR_TRANSPARENT);
dc.drawText(17,
h-37, fsyms, flags,
Gfx.TEXT_JUSTIFY_LEFT);
// Battery
var battery =
systemStats.battery;
dc.drawText(17,
h-53, fsyms, "d",
Gfx.TEXT_JUSTIFY_LEFT);
dc.fillRectangle(17+3,h-44,
11.0*battery/100, 7);
dc.drawText(17+19,
h-52, font2, battery.format("%d") + "%", Gfx.TEXT_JUSTIFY_LEFT);
// Memory
//memory usage
var mem =
100.0*systemStats.usedMemory/systemStats.totalMemory;
dc.drawText(17,
h-68, fsyms, "e",
Gfx.TEXT_JUSTIFY_LEFT);
dc.drawText(17+19,
h-66, font2, mem.format("%d") + "%", Gfx.TEXT_JUSTIFY_LEFT);
//
Write calories, distance
var dist1 =
actinfo.distance/160934.0;
var dist2 =
actinfo.distance/100000.0;
dc.drawText(w-20,
20, font2, steps + " steps", Gfx.TEXT_JUSTIFY_RIGHT);
dc.drawText(w-20,
32, font2, dist1.format("%0.1f") +" mi / "+ dist2.format("%0.1f") + " km",
Gfx.TEXT_JUSTIFY_RIGHT);
dc.drawText(w-20,
44, font2, actinfo.calories + " kcal", Gfx.TEXT_JUSTIFY_RIGHT);
//
plot the background with transparency
dc.drawBitmap(0, 0, Ui.loadResource(Rez.Drawables.background));
// write the clock
numbers
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
dc.drawText(w2+1, -6, fontnumbers, ""+12, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(w2, h-30, fontnumbers, ""+6, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(17, h2-17, fontnumbers, ""+9, Gfx.TEXT_JUSTIFY_LEFT);
dc.drawText(w-17, h2-17, fontnumbers, ""+3,
Gfx.TEXT_JUSTIFY_RIGHT);
//
Get and show the current time
var clockTime =
Sys.getClockTime();
// Draw hands
var alpha, r, r2, hand;
// hours
alpha
= Math.PI/6*(1.0*clockTime.hour+clockTime.min/60.0);
r
= 50;
r2
= 12;
hand
= [[w2,h2],
[w2+r2*Math.sin(alpha-0.4),h2-r2*Math.cos(alpha-0.4)],
[w2+r*Math.sin(alpha),h2-r*Math.cos(alpha)],
[w2+r2*Math.sin(alpha+0.4),h2-r2*Math.cos(alpha+0.4)] ];
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
dc.fillPolygon(hand);
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_TRANSPARENT);
for (n=0; n<3; n++) {
dc.drawLine(hand[n][0],
hand[n][1], hand[n+1][0], hand[n+1][1]);
}
dc.drawLine(hand[n][0],
hand[n][1], hand[0][0], hand[0][1]);
//
minutes
alpha
= Math.PI/30.0*clockTime.min;
r
= 90;
r2
= 20;
hand
= [[w2,h2],
[w2+r2*Math.sin(alpha-0.15),h2-r2*Math.cos(alpha-0.15)],
[w2+r*Math.sin(alpha),h2-r*Math.cos(alpha)],
[w2+r2*Math.sin(alpha+0.15),h2-r2*Math.cos(alpha+0.15)] ];
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
dc.fillPolygon(hand);
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_TRANSPARENT);
for (n=0; n<3; n++) {
dc.drawLine(hand[n][0],
hand[n][1], hand[n+1][0], hand[n+1][1]);
}
dc.drawLine(hand[n][0],
hand[n][1], hand[0][0], hand[0][1]);
}
function bar(dc, position,
percentage, color1, text, strong) {
var len = (h - 21) *
(percentage);
len = len.toNumber();
dc.setColor(color1,
Gfx.COLOR_TRANSPARENT);
dc.fillRectangle(18+position*10,
h-len-10, 8, len+1);
dc.drawText(22+position*10,
h-len-28, font2, text, Gfx.TEXT_JUSTIFY_CENTER);
if(strong){
dc.drawText(22+position*10-1,
h-len-28-1, font2, text, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(22+position*10-1,
h-len-28, font2, text, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(22+position*10,
h-len-28-1, font2, text, Gfx.TEXT_JUSTIFY_CENTER);
}
}
//!
Called when this View is removed from the screen. Save the
//!
state of this View here. This includes freeing resources from
//!
memory.
function onHide() {
}
//!
The user has just looked at their watch. Timers and animations may be started
here.
function onExitSleep() {
}
//!
Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
}
}
Suscribirse a:
Entradas (Atom)