diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog new file mode 100644 index 000000000..759f68777 --- /dev/null +++ b/apps/aiclock/ChangeLog @@ -0,0 +1 @@ +0.01: New app! \ No newline at end of file diff --git a/apps/aiclock/README.md b/apps/aiclock/README.md new file mode 100644 index 000000000..42e8242ab --- /dev/null +++ b/apps/aiclock/README.md @@ -0,0 +1,21 @@ +# AI Clock +This clock was designed by stable diffusion ([paper](https://arxiv.org/abs/2112.10752)) using the following prompt: + +`A rectangle digital watchface` + + +The original output of stable diffusion is shown here: + +![](orig.png) + +And my implementation is shown here: + +![](impl.png) + + +# Thanks to +The great open source community: I used an open source diffusion model (https://github.com/CompVis/stable-diffusion) +to generate a watch face for the open source smartwatch BangleJs. + +## Creator +- [David Peer](https://github.com/peerdavid). \ No newline at end of file diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js new file mode 100644 index 000000000..5a3081029 --- /dev/null +++ b/apps/aiclock/aiclock.app.js @@ -0,0 +1,194 @@ +/** + * AI Clock + */ +require("Font7x11Numeric7Seg").add(Graphics); +Graphics.prototype.setFontGochiHand = function(scale) { + // Actual height 27 (29 - 3) + this.setFontCustom( + atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAA8AAAAADwAAAAAPAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAA/+AAAB//4AAH///gAH///gAAf//AAAB/+AAAAH8AAAAAAAAAAAAAAAAAAAAH8AAAAB/8AAAAP/4AAAB//wAAAPx/AAAB8B+AAAHgD4AAA+AHgAADwAeAAAPAB4AAA8AHgAAD4AeAAAPgB4AAAeAPgAAB8A8AAAH4HwAAAP/+AAAAf/wAAAA/+AAAAB/wAAAAB8AAAAAAAAAAADgAAAAAfAAAAAB4AAAAAPAAAAAB8AAAAAHgAAAAA8AAAAADwAAAAAf4AAAAB//8AAAD//4AAAH//gAAAD/+AAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AHgAAHgA+AAA/AD4AAD4AfgAAfAD+AAB4Af4AAHgD/gAAeAfeAAB4D54AAHw/HgAAf/4fAAA//B8AAD/4DwAAH+APAAAHgA8AAAAADwAAAAAOAAAAAAAAABgAAAAAPAAAAAB8AAAAAHwAYAAAeAD4AAD4APwAAPA4fgAA8Hw+AADwfB4AAPh4HwAA+HgPAAB/+A8AAH/4DwAAP/weAAAf/j4AAAc//gAAAB/8AAAAD/gAAAAD8AAAAAAAAAAAAAAAAAADAAAAAA+AAAAAP4AAAAB/wAAAAP/AAAAD+8AAAAfzwAAAf8HAAAB/gcAAAH/hwAAAf//gAAA//+AAAAf//gAAAP//gAAAD/+AAAAB/4AAAAH/AAAAAeAAAAAAgAAAAAAAAAAAAcAAAB8H8AAAP4f4AAA/x/wAAD/H/gAAf+A+AAB74B4AAHnwHgAAefAfAAB58A8AAHj4DwAAePgPAAB4fA8AAHh+HgAAeD8+AAB4P/4AAHgf/AAAeA/4AAAAA+AAAAAAAAAAAAAAAAAAHgAAAAD/wAAAA//gAAAH//AAAA//+AAAD4H8AAAfA/wAAB4D/AAAHgP+AAAeB54AAB4HngAAHweeAAAfB54AAA4HngAAAAeeAAAAB/4AAAAH/AAAAAP4AAAAAfAAADwAAAAAPAAAAAA8HgAAADweAAAAPB4AAAA8HgAAADweAAAAPh4AAAA+HgAAAB4eAAAAHx4AAAAf//8AAA///wAAD//+AAAH//4AAAAeAAAAAB4AAAAAHgAAAAAeAAAAAB4AAAAAHgAAAAAAAAAAAAAAAAAAD+AAAA+f+AAAH//8AAA///wAAH/4fgAAePgeAAB4+B4AAHj4HwAAePgPAAB4+A8AAHz4DwAAfngeAAA//B4AAD/+HgAAH//8AAAP//wAAAAf+AAAAA/wAAAAAYAAAAAAAAAAA/gAAAAH/AAAAA/8AAAAD34AAAAeHgAAAB4eAAAAHh4AAAA8HgAAADweAAAAPDwAAAA8PAAAADx4AAAAPvgAAAAf///AAB///8AAH///wAAP///AAA/wA4AABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOA4AAAA8DwAAADwPAAAAPA8AAAAYBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='), + 46, + atob("DQoXEBQVExUUFRYUDQ=="), + 40+(scale<<8)+(1<<16) + ); + return this; +} + +/* + * Set some important constants such as width, height and center + */ +var W = g.getWidth(),R=W/2; +var H = g.getHeight(); +var cx = W/2; +var cy = H/2; +var drawTimeout; + +/* + * Based on the great multi clock from https://github.com/jeffmer/BangleApps/ + */ +Graphics.prototype.drawRotRect = function(w, r1, r2, angle) { + angle = angle % 360; + var w2=w/2, h=r2-r1, theta=angle*Math.PI/180; + return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0], + {x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta})); +}; + + +function drawBackground() { + g.setFontAlign(0,0); + g.setColor(g.theme.fg); + + y = 0; + while(y < H){ + y += 3 + Math.floor(Math.random() * 10); + g.drawLine(0, y, W, y); + g.drawLine(0, y+1, W, y+1); + g.drawLine(0, y+2, W, y+2); + y += 2; + } +} + + +function drawCircle(isLocked){ + g.setColor(g.theme.fg); + g.fillCircle(cx, cy, 12); + + var c = isLocked ? "#f00" : g.theme.bg; + g.setColor(c); + g.fillCircle(cx, cy, 6); +} + + +function drawTime(){ + var drawHourHand = g.drawRotRect.bind(g,8,12,R-38); + var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 ); + + g.setFontAlign(0,0); + + // Compute angles + var date = new Date(); + var m = parseInt(date.getMinutes() * 360 / 60); + var h = date.getHours(); + h = h > 12 ? h-12 : h; + h = parseInt(h*360/12); + + // Draw minute and hour bg + g.setColor(g.theme.bg); + drawHourHand(Math.max(0, h - 2)); + drawHourHand(Math.min(360, h + 2)); + drawMinuteHand(Math.max(0, m - 2)); + drawMinuteHand(Math.min(360, m + 2)); + + // Draw minute and hour fg + g.setColor(g.theme.fg); + drawHourHand(h); + drawMinuteHand(m); +} + + + +function drawDate(){ + var date = new Date(); + g.setFontAlign(0,0); + g.setFontGochiHand(); + + var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+date.getMonth()).substr(-2); + var w = g.stringWidth(text); + g.setColor(g.theme.bg); + g.fillRect(cx-w/2-4, 20, cx+w/2+2, 40+12); + + g.setColor(g.theme.fg); + g.drawLine(cx+w/2+1, 20, cx+w/2+1, 40+12); + g.drawLine(cx+w/2+2, 20, cx+w/2+2, 40+12); + g.drawLine(cx+w/2+3, 20, cx+w/2+3, 40+12); + g.drawLine(cx+w/2+4, 20, cx+w/2+4, 40+12); + g.drawString(text, cx, 40); +} + + +function drawSteps(){ + g.setFontAlign(0,0); + g.setFont("7x11Numeric7Seg",3); + + var text = "4234"; + var w = g.stringWidth(text); + g.setColor(g.theme.bg); + g.fillRect(cx-w/2-4, 120, cx+w/2+2, 140+20); + + g.setColor(g.theme.fg); + g.drawLine(cx+w/2+1, 120, cx+w/2+1, 140+20); + g.drawLine(cx+w/2+2, 120, cx+w/2+2, 140+20); + g.drawLine(cx+w/2+3, 120, cx+w/2+3, 140+20); + g.drawLine(cx+w/2+4, 120, cx+w/2+4, 140+20); + g.drawString(text, cx, 140); + g.drawString(text, cx+1, 140); + g.drawString(text, cx, 141); +} + + + +function draw(){ + // Queue draw in one minute + queueDraw(); + + + g.reset(); + g.clearRect(0, 0, g.getWidth(), g.getHeight()); + + g.setColor(1,1,1); + drawBackground(); + drawDate(); + drawSteps(); + drawTime(); + drawCircle(Bangle.isLocked()); +} + + +/* + * Listeners + */ +Bangle.on('lcdPower',on=>{ + if (on) { + draw(true); + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.on('lock', function(isLocked) { + drawCircle(isLocked); +}); + + +/* + * Some helpers + */ +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + + + +/* + * Lets start widgets, listen for btn etc. + */ +// Show launcher when middle button pressed +Bangle.setUI("clock"); +Bangle.loadWidgets(); +/* + * we are not drawing the widgets as we are taking over the whole screen + * so we will blank out the draw() functions of each widget and change the + * area to the top bar doesn't get cleared. + */ +for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} + +// Clear the screen once, at startup and draw clock +g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); +draw(); + +// After drawing the watch face, we can draw the widgets +// Bangle.drawWidgets(); diff --git a/apps/aiclock/aiclock.icon.js b/apps/aiclock/aiclock.icon.js new file mode 100644 index 000000000..06565b50f --- /dev/null +++ b/apps/aiclock/aiclock.icon.js @@ -0,0 +1 @@ +atob("ADAwhAT///////////////////////////////////////////////////////////////8RERERERERERERERERERERERERERERERH///////////////////////////////////////////////////////////////8RERERERMzMzMzMzMzMzMzMzMhERERERH////////yH/8zP/I///////8h///////////////yAjAAA/A/Mz/zEBMh//////////////8TEDAT//DzAQEwIxAh//////8RERERESMvMBMQHzDwPzAgIQEgERERERH/////////IQPzAiEw//EiEhIh////////////////EgH/EhLwEyA//xIh//////8zMzMzM///PyAgHwPzERP//xIhMzMzMzMRERERES////AD/z///////yIgERERERH///////////IC//////////8z//////8zMzMzMzMzMzMQMzMzMzMzMzMzMzMzMzMRERERERESEREgERERERERERERERERERH///////8QE//xA/////////////////////////8QAC/zAf////////////////8RERERERESEAAiEBEREREREREREREREREzMzMzMzMz8gABIAIjMzMzMzMzMzMzMzP///////////IAAQAAL/////////////8zMzMzMzMzMzMgAAXAAjMzMzMzMzMzMzMiIiIiIiIiIiIiIFzMASIiIiIiIiIiIiIiIiIiIiIiIiIiIMzMASIiIiIiIiIiIiIiIiIiIiIiIiIiIAzFAiIiIiIiIiIiIiIzMzMzMzMzMzMzMQAAAzMzMzMzMzMzMzMiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIRERERERERERERERERERERERERERERERH///////////////////////////////8iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiL///////////////////////8///////8iIiIiIiP////zM//zM/////MCIiIiIiIiIiIiIiMz//PxABMwACM//zMCIiIiIiIzMzMzMz8h/zH//xL//wIf8wMDMzMzMzMiIiIiIiMh/zH//xL//wIf8wMCIiIiIiIRERERERIh/yH//xL//wIf8wMBERERERH////////xAAMwAB8wAB8gACMf/////////////////zES/////wP/8wMf//////8RERERERL//zES/////wP/8wMBERERERH//////////zES/////wP/8wMf//////8RERERERP///MwAC8wAC///zMBERERERH///////////////////////Mf//////8iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzP///////////////////////////////8RERERERERERERERERERERERERERERERE=") \ No newline at end of file diff --git a/apps/aiclock/aiclock.png b/apps/aiclock/aiclock.png new file mode 100644 index 000000000..184abbb4f Binary files /dev/null and b/apps/aiclock/aiclock.png differ diff --git a/apps/aiclock/impl.png b/apps/aiclock/impl.png new file mode 100644 index 000000000..76bf8455e Binary files /dev/null and b/apps/aiclock/impl.png differ diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json new file mode 100644 index 000000000..8a22a4050 --- /dev/null +++ b/apps/aiclock/metadata.json @@ -0,0 +1,20 @@ +{ + "id": "aiclock", + "name": "AI Clock", + "shortName":"AI Clock", + "icon": "aiclock.png", + "version":"0.01", + "readme": "README.md", + "supports": ["BANGLEJS2"], + "description": "A watch face generated by an AI (stable diffusion) and implemented by a human.", + "type": "clock", + "tags": "clock", + "screenshots": [ + {"url":"orig.png"}, + {"url":"impl.png"} + ], + "storage": [ + {"name":"aiclock.app.js","url":"aiclock.app.js"}, + {"name":"aiclock.img","url":"aiclock.icon.js","evaluate":true} + ] +} diff --git a/apps/aiclock/orig.png b/apps/aiclock/orig.png new file mode 100644 index 000000000..009826454 Binary files /dev/null and b/apps/aiclock/orig.png differ