diff --git a/apps/clockbg/ChangeLog b/apps/clockbg/ChangeLog index 026dc1aa0..dd8e5dc94 100644 --- a/apps/clockbg/ChangeLog +++ b/apps/clockbg/ChangeLog @@ -1,3 +1,6 @@ 0.01: New App! 0.02: Moved settings into 'Settings->Apps' -0.03: Add 'Squares' option for random squares background \ No newline at end of file +0.03: Add 'Squares' option for random squares background +0.04: More options for different background colors + 'Plasma' generative background + Add a 'view' option in settings menu to view the current background \ No newline at end of file diff --git a/apps/clockbg/README.md b/apps/clockbg/README.md index 14bbeb7a7..6573ae165 100644 --- a/apps/clockbg/README.md +++ b/apps/clockbg/README.md @@ -15,6 +15,7 @@ You can either: * `Random Color` - a new color every time the clock starts * `Image` - choose from a previously uploaded image * `Squares` - a randomly generated pattern of squares in the selected color palette + * `Plasma` - a randomly generated 'plasma' pattern of squares in the selected color palette (random noise with a gaussian filter applied) ## Usage in code @@ -30,6 +31,9 @@ background.fillRect(Bangle.appRect); // to fill just one part of the screen background.fillRect(x1, y1, x2, y2); + +// if you ever need to reload to a new background (this could take ~100ms) +background.reload(); ``` You should also add `"dependencies" : { "clockbg":"module" },` to your app's metadata to @@ -39,8 +43,9 @@ ensure that the clock background library is automatically loaded. A few features could be added that would really improve functionality: -* When 'fast loading', 'random' backgrounds don't update at the moment +* When 'fast loading', 'random' backgrounds don't update at the moment (calling `.reload` can fix this now, but it slows things down) * Support for >1 image to be uploaded (requires some image management in `interface.html`), and choose randomly between them * Support for gradients (random colors) * More types of auto-generated pattern (as long as they can be generated quickly or in the background) -* Storing 'clear' areas of uploaded images so clocks can easily position themselves \ No newline at end of file +* Storing 'clear' areas of uploaded images so clocks can easily position themselves +* Some backgrounds could update themselves in the background (eg a mandelbrot could calculate the one it should display next time while the watch is running) \ No newline at end of file diff --git a/apps/clockbg/lib.js b/apps/clockbg/lib.js index c9b1fb1d2..f3161fa31 100644 --- a/apps/clockbg/lib.js +++ b/apps/clockbg/lib.js @@ -1,25 +1,46 @@ -let settings = Object.assign({ - style : "randomcolor", - colors : ["#F00","#0F0","#00F"] -},require("Storage").readJSON("clockbg.json")||{}); -if (settings.style=="image") - settings.img = require("Storage").read(settings.fn); -else if (settings.style=="randomcolor") { - settings.style = "color"; - let n = (0|(Math.random()*settings.colors.length)) % settings.colors.length; - settings.color = settings.colors[n]; - delete settings.colors; -} else if (settings.style=="squares") { - settings.style = "image"; - let bpp = (settings.colors.length>4)?4:2; - let bg = Graphics.createArrayBuffer(11,11,bpp,{msb:true}); - E.mapInPlace(bg.buffer, bg.buffer, ()=>Math.random()*256); // random pixels - bg.palette = new Uint16Array(1<g.toColor(c))); - settings.img = bg.asImage("string"); - settings.imgOpt = {scale:16}; - delete settings.colors; -} +let settings; + +exports.reload = function() { + settings = Object.assign({ + style : "randomcolor", + colors : ["#F00","#0F0","#00F"] + },require("Storage").readJSON("clockbg.json")||{}); + if (settings.style=="image") + settings.img = require("Storage").read(settings.fn); + else if (settings.style=="randomcolor") { + settings.style = "color"; + let n = (0|(Math.random()*settings.colors.length)) % settings.colors.length; + settings.color = settings.colors[n]; + delete settings.colors; + } else if (settings.style=="squares") { // 50ms + settings.style = "image"; + let bpp = (settings.colors.length>4)?4:2; + let bg = Graphics.createArrayBuffer(11,11,bpp,{msb:true}); + E.mapInPlace(bg.buffer, bg.buffer, ()=>Math.random()*256); // random pixels + bg.palette = new Uint16Array(1<g.toColor(c))); + settings.img = bg.asImage("string"); + settings.imgOpt = {scale:16}; + delete settings.colors; + } else if (settings.style=="plasma") { // ~100ms + settings.style = "image"; + let bg = Graphics.createArrayBuffer(16,16,4,{msb:true}); + E.mapInPlace(bg.buffer, bg.buffer, ()=>Math.random()*256); // random pixels + bg.filter([ // a gaussian filter to smooth out + 1, 4, 7, 4, 1, + 4,16,26,16, 4, + 7,26,41,26, 7, + 4,16,26,16, 4, + 1, 4, 7, 4, 1 + ], { w:5, h:5, div:120, offset:-800 }); + bg.palette = new Uint16Array(16); + bg.palette.set(settings.colors.map(c=>g.toColor(c))); + settings.img = bg.asImage("string"); + settings.imgOpt = {scale:11}; + delete settings.colors; + } +}; +exports.reload(); // Fill a rectangle with the current background style, rect = {x,y,w,h} // eg require("clockbg").fillRect({x:10,y:10,w:50,h:50}) diff --git a/apps/clockbg/metadata.json b/apps/clockbg/metadata.json index 2221e99bd..744411191 100644 --- a/apps/clockbg/metadata.json +++ b/apps/clockbg/metadata.json @@ -1,10 +1,10 @@ { "id": "clockbg", "name": "Clock Backgrounds", "shortName":"Backgrounds", - "version": "0.03", + "version": "0.04", "description": "Library that allows clocks to include a custom background (generated on demand or uploaded).", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"}], + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}], "type": "module", "readme": "README.md", "provides_modules" : ["clockbg"], diff --git a/apps/clockbg/screenshot3.png b/apps/clockbg/screenshot3.png new file mode 100644 index 000000000..c1aaae219 Binary files /dev/null and b/apps/clockbg/screenshot3.png differ diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js index c39017262..22256478e 100644 --- a/apps/clockbg/settings.js +++ b/apps/clockbg/settings.js @@ -1,122 +1,171 @@ (function(back) { -let settings = Object.assign({ - style : "randomcolor", - colors : ["#F00","#0F0","#00F"] -},require("Storage").readJSON("clockbg.json")||{}); + let settings = Object.assign({ + style : "randomcolor", + colors : ["#F00","#0F0","#00F"] + },require("Storage").readJSON("clockbg.json")||{}); -function saveSettings() { - if (settings.style!="image") - delete settings.fn; - if (settings.style!="color") - delete settings.color; - if (settings.style!="randomcolor" && settings.style!="squares") - delete settings.colors; - require("Storage").writeJSON("clockbg.json", settings); -} + function saveSettings() { + if (settings.style!="image") + delete settings.fn; + if (settings.style!="color") + delete settings.color; + if (!["randomcolor","squares","plasma"].includes(settings.style)) + delete settings.colors; + require("Storage").writeJSON("clockbg.json", settings); + } -function getColorsImage(cols) { - var bpp = 1; - if (cols.length>4) bpp=4; - else if (cols.length>2) bpp=2; - var w = (cols.length>8)?8:16; - var b = Graphics.createArrayBuffer(w*cols.length,16,bpp); - b.palette = new Uint16Array(1<{ - b.setColor(i).fillRect(i*w,0,i*w+w-1,15); - b.palette[i] = g.toColor(c); - }); - return "\0"+b.asImage("string"); -} + function getColorsImage(cols) { + var bpp = 1; + if (cols.length>4) bpp=4; + else if (cols.length>2) bpp=2; + var w = (cols.length>8)?8:16; + var b = Graphics.createArrayBuffer(w*cols.length,16,bpp); + b.palette = new Uint16Array(1<{ + b.setColor(i).fillRect(i*w,0,i*w+w-1,15); + b.palette[i] = g.toColor(c); + }); + return "\0"+b.asImage("string"); + } -function showModeMenu() { - E.showMenu({ - "" : {title:/*LANG*/"Background", back:showMainMenu}, - /*LANG*/"Solid Color" : function() { - var cols = ["#F00","#0F0","#FF0", - "#00F","#F0F","#0FF", - "#000","#888","#fff",]; - var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; - cols.forEach(col => { - menu["-"+getColorsImage([col])] = () => { - settings.style = "color"; - settings.color = col; - saveSettings(); - showMainMenu(); - }; - }); - E.showMenu(menu); - }, - /*LANG*/"Random Color" : function() { - var cols = [ - ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], - ["#F00","#0F0","#00F"], - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "randomcolor"; - settings.colors = col; - saveSettings(); - showMainMenu(); - }; - }); - E.showMenu(menu); - }, - /*LANG*/"Image" : function() { - let images = require("Storage").list(/clockbg\..*\.img/); - if (images.length) { - var menu = {"":{title:/*LANG*/"Images", back:showModeMenu}}; - images.forEach(im => { - menu[im.slice(8,-4)] = () => { - settings.style = "image"; - settings.fn = im; + function showModeMenu() { + E.showMenu({ + "" : {title:/*LANG*/"Background", back:showMainMenu}, + /*LANG*/"Solid Color" : function() { + var cols = ["#F00","#0F0","#FF0", + "#00F","#F0F","#0FF", + "#000","#888","#fff",]; + var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; + cols.forEach(col => { + menu["-"+getColorsImage([col])] = () => { + settings.style = "color"; + settings.color = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + }, + /*LANG*/"Random Color" : function() { + var cols = [ + ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], + ["#F00","#0F0","#00F"], + ["#FF0","#F0F","#0FF"], + ["#00f","#0bf","#0f7","#3f0","#ff0","#f30","#f07","#b0f"], + ["#66f","#6df","#6fb","#8f6","#ff6","#f86","#f6b","#d6f"], + ["#007","#057","#073","#170","#770","#710","#703","#507"] + // Please add some more! + ]; + var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "randomcolor"; + settings.colors = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + }, + /*LANG*/"Image" : function() { + let images = require("Storage").list(/clockbg\..*\.img/); + if (images.length) { + var menu = {"":{title:/*LANG*/"Images", back:showModeMenu}}; + images.forEach(im => { + menu[im.slice(8,-4)] = () => { + settings.style = "image"; + settings.fn = im; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + } else { + E.showAlert("Please use App Loader to upload images").then(showModeMenu); + } + }, + /*LANG*/"Squares" : function() { + var cols = [ // list of color palettes used as possible square colours - either 4 or 16 entries + ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], + ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], + ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], + ["#0FF","#0CC","#088","#044"], + ["#FFF","#FBB","#F66","#F44"], + ["#FFF","#BBB","#666","#000"], + ["#fff","#bbf","#77f","#33f"], + ["#fff","#bff","#7fe","#3fd"] + // Please add some more! 4 or 16 only! + ]; + var menu = {"":{title:/*LANG*/"Squares", back:showModeMenu}}; + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "squares"; + settings.colors = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + }, + /*LANG*/"Plasma" : function() { + var cols = [ // list of color palettes used as possible square colours - 16 entries + ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], + ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], + ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], + ["#fff","#fef","#fdf","#fcf","#fbf","#fae","#f9e","#f8e","#f7e","#f6e","#f5d","#f4d","#f3d","#f2d","#f1d","#f0c"], + ["#fff","#eff","#dff","#cef","#bef","#adf","#9df","#8df","#7cf","#6cf","#5bf","#4bf","#3bf","#2af","#1af","#09f"], + ["#000","#010","#020","#130","#140","#250","#260","#270","#380","#390","#4a0","#4b0","#5c0","#5d0","#5e0","#6f0"] + // Please add some more! + ]; + var menu = {"":{title:/*LANG*/"Plasma", back:showModeMenu}}; + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "plasma"; + settings.colors = col; saveSettings(); showMainMenu(); }; }); E.showMenu(menu); - } else { - E.showAlert("Please use App Loader to upload images").then(showModeMenu); } - }, - /*LANG*/"Squares" : function() { - /* - a = new Array(16); - a.fill(0); - print(a.map((n,i)=>E.HSBtoRGB(0 + i/16,1,1,24).toString(16).padStart(6,0).replace(/(.).(.).(.)./,"\"#$1$2$3\"")).join(",")) - */ - var cols = [ // list of color palettes used as possible square colours - either 4 or 16 entries - ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], - ["#0FF","#0CC","#088","#044"], - ["#FFF","#FBB","#F66","#F44"], - ["#FFF","#BBB","#666","#000"] - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Squares", back:showModeMenu}}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "squares"; - settings.colors = col; - console.log(settings); - saveSettings(); - showMainMenu(); - }; - }); - E.showMenu(menu); - } - }); -} + }); + } -function showMainMenu() { - E.showMenu({ - "" : {title:/*LANG*/"Clock Background", back:back}, - /*LANG*/"Mode" : { - value : settings.style, - onchange : showModeMenu - } - }); -} + function showMainMenu() { + E.showMenu({ + "" : {title:/*LANG*/"Clock Background", back:back}, + /*LANG*/"Mode" : { + value : settings.style, + onchange : showModeMenu + }, + /*LANG*/"View" : () => { + Bangle.setUI({mode:"custom",touch:showMainMenu,btn:showMainMenu}); + require("clockbg").reload(); + require("clockbg").fillRect(Bangle.appRect); + } + }); + } -showMainMenu(); -}) \ No newline at end of file + /* Scripts for generating colors. Change the values in HSBtoRGB to generate different effects + + + a = new Array(16); + a.fill(0); + g.clear(); + w = Math.floor(g.getWidth()/a.length); + print(a.map((n,i)=>{ + var j = i/(a.length-1); // 0..1 + var c = E.HSBtoRGB(j,1,1,24); // rainbow + var c = E.HSBtoRGB(j,0.6,1,24); // faded rainbow + var c = E.HSBtoRGB(0.8, j,1,24); // purple->white + var c = E.HSBtoRGB(0.1, j,1,24); // blue->white + var c = E.HSBtoRGB(0.4, 1,j,24); // black->green + var col = c.toString(16).padStart(6,0).replace(/(.).(.).(.)./,"\"#$1$2$3\""); + g.setColor(eval(col)).fillRect(i*w,0, i*w+w-1,31); + return col; + }).join(",")) + + */ + + showMainMenu(); + }) \ No newline at end of file