├── screenshot.jpg
├── resources
├── strings
│ └── strings.xml
├── bitmaps
│ └── launcher_icon.png
├── settings
│ └── settings.xml
└── resources.xml
├── README.md
├── .gitignore
├── source
├── BasicApp.mc
├── DrawAA.mc
└── BasicView.mc
└── manifest.xml
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sunpazed/garmin-drawaa/HEAD/screenshot.jpg
--------------------------------------------------------------------------------
/resources/strings/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DrawLineAA
3 |
4 |
--------------------------------------------------------------------------------
/resources/bitmaps/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sunpazed/garmin-drawaa/HEAD/resources/bitmaps/launcher_icon.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DrawLineAA
2 |
3 | A quick proof-of-concept to draw anti-aliased lines and polygons on a Garmin wearable.
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/resources/settings/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | build.sh
14 | release.sh
15 | .DS_Store
16 | .env
17 | old
18 | npm-debug.log
19 |
--------------------------------------------------------------------------------
/resources/resources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DrawLineAA
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/source/BasicApp.mc:
--------------------------------------------------------------------------------
1 | using Toybox.Application as App;
2 | using Toybox.System as Sys;
3 | using Toybox.Lang as Lang;
4 | using Toybox.WatchUi as Ui;
5 |
6 | class BasicApp extends App.AppBase {
7 |
8 |
9 | function initialize() {
10 | App.AppBase.initialize();
11 | }
12 |
13 | function onSettingsChanged() {
14 | Ui.requestUpdate();
15 | }
16 |
17 | //! onStart() is called on application start up
18 | function onStart(state) {
19 | }
20 |
21 | //! onStop() is called when your application is exiting
22 | function onStop(state) {
23 | }
24 |
25 | //! Return the initial view of your application here
26 | function getInitialView() {
27 | return [ new BasicView() ];
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | eng
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/source/DrawAA.mc:
--------------------------------------------------------------------------------
1 | // DrawAA
2 | // --------------------------------
3 |
4 | // This is a (poorly optimised) implementation of Xiaolin Wu's line algorithm:
5 | // https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
6 | //
7 | // Currently only a proof-of-concept, and as such, only works
8 | // for grayscale lines on dark backgrounds
9 | //
10 | // drawAA = new DrawAA();
11 | // drawAA.drawLine(dc, x0, y0, x1, y1);
12 | // drawAA.drawPoly(dc, poly[], offsetx, offsety);
13 |
14 | using Toybox.WatchUi as Ui;
15 | using Toybox.Graphics as Gfx;
16 | using Toybox.System as Sys;
17 | using Toybox.Lang as Lang;
18 | using Toybox.Math as Math;
19 |
20 | class DrawAA {
21 |
22 | // draw AA line from (x0,y0) to (x1,y1) on bitmap (dc)
23 | function drawLine(bitmap, x0, y0, x1, y1) {
24 |
25 | var temp;
26 |
27 | // line direction
28 | var steep = ((y1-y0).toLong().abs()) > ((x1-x0).toLong().abs());
29 |
30 | // swap x->y co-ords if non-positive direction
31 | if (steep) {
32 | temp = x0;
33 | x0 = y0;
34 | y0 = temp;
35 | temp = x1;
36 | x1 = y1;
37 | y1 = temp;
38 | }
39 |
40 | // swap 0->1 co-ords to draw in the right order
41 | if (x0>x1) {
42 | temp = x0;
43 | x0 = x1;
44 | x1 = temp;
45 | temp = y0;
46 | y0 = y1;
47 | y1 = temp;
48 | }
49 |
50 | var dx = x1-x0;
51 | var dy = y1-y0;
52 | var gradient = (dy.toFloat() + 0.0001) / (dx.toFloat() + 0.0001);
53 |
54 | // draw first endpoint
55 | var xEnd = round(x0);
56 | var yEnd = y0+gradient*(xEnd-x0);
57 | var xGap = rfpart(x0+0.5);
58 | var xPixel1 = xEnd;
59 | var yPixel1 = ipart(yEnd);
60 | if (steep) {
61 | drawPointAA(bitmap, yPixel1, xPixel1, rfpart(yEnd)*xGap);
62 | drawPointAA(bitmap, yPixel1+1, xPixel1, fpart(yEnd)*xGap);
63 | } else {
64 | drawPointAA(bitmap, xPixel1,yPixel1, rfpart(yEnd)*xGap);
65 | drawPointAA(bitmap, xPixel1, yPixel1+1, fpart(yEnd)*xGap);
66 | }
67 | var intery = yEnd+gradient;
68 |
69 | // draw second endpoint
70 | xEnd = round(x1);
71 | yEnd = y1 + gradient * (xEnd-x1);
72 | xGap = fpart(x1+0.5);
73 | var xPixel2 = xEnd;
74 | var yPixel2 = ipart(yEnd);
75 | if (steep) {
76 | drawPointAA(bitmap, yPixel2, xPixel2, rfpart(yEnd)*xGap);
77 | drawPointAA(bitmap, yPixel2+1, xPixel2, fpart(yEnd)*xGap);
78 | } else {
79 | drawPointAA(bitmap, xPixel2, yPixel2, rfpart(yEnd)*xGap);
80 | drawPointAA(bitmap, xPixel2, yPixel2+1, fpart(yEnd)*xGap);
81 | }
82 |
83 | // draw the remainder of the line
84 | if (steep) {
85 | for(var x=xPixel1+1; x<=xPixel2-1; x++){
86 | drawPointAA(bitmap, ipart(intery), x, rfpart(intery));
87 | drawPointAA(bitmap, ipart(intery)+1, x, fpart(intery));
88 | intery+=gradient;
89 | }
90 | } else {
91 | for(var x=xPixel1+1; x<=xPixel2-1; x++){
92 | drawPointAA(bitmap, x,ipart(intery), rfpart(intery));
93 | drawPointAA(bitmap, x, ipart(intery)+1, fpart(intery));
94 | intery+=gradient;
95 | }
96 | }
97 | }
98 |
99 | // draw AA polygon (unfilled) from an array of (points) from offset (x,y) on bitmap (dc)
100 | function drawPoly(dc, points, x, y) {
101 |
102 | for(var p=0; p < points.size()-1; p++) {
103 | drawLine(dc, points[p][0]+x, points[p][1] + y, points[p+1][0] + x, points[p+1][1] + y);
104 | }
105 |
106 | }
107 |
108 |
109 | // helper functions for drawLine
110 | function ipart(x) {
111 | return Math.floor(x).toFloat();
112 | }
113 |
114 | function round(x) {
115 | return ipart(x + 0.5);
116 | }
117 |
118 | function fpart(x) {
119 | return x - ipart(x);
120 | }
121 |
122 | function rfpart(x) {
123 | return 1 - fpart(x);
124 | }
125 |
126 | // draw a pixel at x,y with intensity between 0 and 1
127 | function drawPointAA(dc, x, y, intensity) {
128 |
129 | if (intensity >= 0.75) {
130 | dc.setColor(Gfx.COLOR_WHITE,0);
131 | } else if (intensity >= 0.5) {
132 | dc.setColor(Gfx.COLOR_LT_GRAY,0);
133 | } else {
134 | dc.setColor(Gfx.COLOR_DK_GRAY,0);
135 | }
136 |
137 | dc.drawPoint(x, y);
138 |
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/source/BasicView.mc:
--------------------------------------------------------------------------------
1 | using Toybox.WatchUi as Ui;
2 | using Toybox.Graphics as Gfx;
3 | using Toybox.System as Sys;
4 | using Toybox.Lang as Lang;
5 | using Toybox.Math as Math;
6 | using Toybox.Application as App;
7 | using Toybox.ActivityMonitor as ActivityMonitor;
8 | using Toybox.Timer as Timer;
9 |
10 | enum {
11 | SCREEN_SHAPE_CIRC = 0x000001,
12 | SCREEN_SHAPE_SEMICIRC = 0x000002,
13 | SCREEN_SHAPE_RECT = 0x000003
14 | }
15 |
16 | class BasicView extends Ui.WatchFace {
17 |
18 | // globals
19 | var debug = false;
20 | var timer1;
21 | var timer_timeout = 80;
22 | var timer_steps = timer_timeout;
23 | var angle = 0;
24 | var isAwake = false;
25 | var drawAA = null;
26 |
27 | // sensors / status
28 | var battery = 0;
29 | var bluetooth = true;
30 |
31 | // time
32 | var hour = null;
33 | var minute = null;
34 | var day = null;
35 | var day_of_week = null;
36 | var month_str = null;
37 | var month = null;
38 |
39 | // layout
40 | var vert_layout = false;
41 | var canvas_h = 0;
42 | var canvas_w = 0;
43 | var canvas_shape = 0;
44 | var canvas_rect = false;
45 | var canvas_circ = false;
46 | var canvas_semicirc = false;
47 | var canvas_tall = false;
48 | var canvas_r240 = false;
49 |
50 | // settings
51 | var set_leading_zero = false;
52 |
53 | // fonts
54 |
55 | // bitmaps
56 | var b_gauge = null;
57 | var b_gauge_over = null;
58 |
59 | // animation settings
60 | var poly_min;
61 | var poly_hour;
62 | var centerpoint;
63 |
64 | function initialize() {
65 | Ui.WatchFace.initialize();
66 | }
67 |
68 |
69 | function onLayout(dc) {
70 |
71 | drawAA = new DrawAA();
72 |
73 | // w,h of canvas
74 | canvas_w = dc.getWidth();
75 | canvas_h = dc.getHeight();
76 |
77 | // check the orientation
78 | if ( canvas_h > (canvas_w*1.2) ) {
79 | vert_layout = true;
80 | } else {
81 | vert_layout = false;
82 | }
83 |
84 | // let's grab the canvas shape
85 | var deviceSettings = Sys.getDeviceSettings();
86 | canvas_shape = deviceSettings.screenShape;
87 |
88 | if (debug) {
89 | Sys.println(Lang.format("canvas_shape: $1$", [canvas_shape]));
90 | }
91 |
92 | // find out the type of screen on the device
93 | canvas_tall = (vert_layout && canvas_shape == SCREEN_SHAPE_RECT) ? true : false;
94 | canvas_rect = (canvas_shape == SCREEN_SHAPE_RECT && !vert_layout) ? true : false;
95 | canvas_circ = (canvas_shape == SCREEN_SHAPE_CIRC) ? true : false;
96 | canvas_semicirc = (canvas_shape == SCREEN_SHAPE_SEMICIRC) ? true : false;
97 | canvas_r240 = (canvas_w == 240 && canvas_w == 240) ? true : false;
98 |
99 | // set offsets based on screen type
100 | // positioning for different screen layouts
101 | if (canvas_tall) {
102 | }
103 | if (canvas_rect) {
104 | }
105 | if (canvas_circ) {
106 | if (canvas_r240) {
107 | } else {
108 | }
109 | }
110 | if (canvas_semicirc) {
111 | }
112 |
113 |
114 | // w,h of canvas
115 | var dw = dc.getWidth();
116 | var dh = dc.getHeight();
117 |
118 | // define the polygon points for the minute and hour hands
119 | poly_min = [[dw/2,dh/2], [(dw/2)-10,(dh/2)-20], [(dw/2),5], [(dw/2)+10,(dh/2)-20], [dw/2,dh/2] ];
120 | poly_hour = [[dw/2,dh/2], [(dw/2)-10,(dh/2)-20], [(dw/2),35], [(dw/2)+10,(dh/2)-20], [dw/2,dh/2] ];
121 |
122 | // centerpoint is the middle of the canvas
123 | centerpoint = [dw/2,dh/2];
124 |
125 | }
126 |
127 |
128 |
129 | // helper function to rotate point[x,y] around origin[x,y] by (angle)
130 | function rotatePoint(origin, point, angle) {
131 |
132 | var radians = angle * Math.PI / 180.0;
133 | var cos = Math.cos(radians);
134 | var sin = Math.sin(radians);
135 | var dX = point[0] - origin[0];
136 | var dY = point[1] - origin[1];
137 |
138 | return [ cos * dX - sin * dY + origin[0], sin * dX + cos * dY + origin[1]];
139 |
140 | }
141 |
142 |
143 | //! Called when this View is brought to the foreground. Restore
144 | //! the state of this View and prepare it to be shown. This includes
145 | //! loading resources into memory.
146 | function onShow() {
147 | }
148 |
149 |
150 | //! Update the view
151 | function onUpdate(dc) {
152 |
153 |
154 | // grab time objects
155 | var clockTime = Sys.getClockTime();
156 | var date = Time.Gregorian.info(Time.now(),0);
157 |
158 | // define time, day, month variables
159 | hour = clockTime.hour;
160 | minute = clockTime.min;
161 | day = date.day;
162 | month = date.month;
163 | day_of_week = Time.Gregorian.info(Time.now(), Time.FORMAT_MEDIUM).day_of_week;
164 | month_str = Time.Gregorian.info(Time.now(), Time.FORMAT_MEDIUM).month;
165 |
166 | // grab battery
167 | var stats = Sys.getSystemStats();
168 | var batteryRaw = stats.battery;
169 | battery = batteryRaw > batteryRaw.toNumber() ? (batteryRaw + 1).toNumber() : batteryRaw.toNumber();
170 |
171 | // do we have bluetooth?
172 | var deviceSettings = Sys.getDeviceSettings();
173 | bluetooth = deviceSettings.phoneConnected;
174 |
175 | // 12-hour support
176 | if (hour > 12 || hour == 0) {
177 | if (!deviceSettings.is24Hour)
178 | {
179 | if (hour == 0)
180 | {
181 | hour = 12;
182 | }
183 | else
184 | {
185 | hour = hour - 12;
186 | }
187 | }
188 | }
189 |
190 | // add padding to units if required
191 | if( minute < 10 ) {
192 | minute = "0" + minute;
193 | }
194 |
195 | if( hour < 10 && set_leading_zero) {
196 | hour = "0" + hour;
197 | }
198 |
199 | if( day < 10 ) {
200 | day = "0" + day;
201 | }
202 |
203 | if( month < 10 ) {
204 | month = "0" + month;
205 | }
206 |
207 |
208 | // clear the screen
209 | dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
210 | dc.clear();
211 |
212 |
213 | var offsetx = 0;
214 | var offsety = 0;
215 |
216 | var poly_min_rot = [];
217 | var poly_hour_rot = [];
218 |
219 | var temp_point = [];
220 |
221 | // rotate the poly_hour[] and poly_min[] hands around the centerpoint[x,y] by (angle)
222 | for (var u=0; u