diff --git a/apps/choozi/README.md b/apps/choozi/README.md index f1e4255bc..8d0aa97f4 100644 --- a/apps/choozi/README.md +++ b/apps/choozi/README.md @@ -11,16 +11,21 @@ the players seated in a circle, set the number of segments equal to the number of players, ensure that each person knows which colour represents them, and then choose a segment. After a short animation, the chosen segment will fill the screen. -You can use Choozi to randomly select an element from any set with 2 to 13 members, +You can use Choozi to randomly select an element from any set with 2 to 14 members, as long as you can define a bijection between members of the set and coloured segments on the Bangle.js display. -## Controls +## Controls Bangle 1 BTN1: increase the number of segments BTN2: choose a segment at random BTN3: decrease the number of segments +## Controls Bangle 2 + +Swipe up/down: increase/decrease the number of segments +BTN1 or tap: choose a segment at random + ## Creator James Stanley diff --git a/apps/choozi/app.js b/apps/choozi/app.js index 1a5b2f17e..8ba040aab 100644 --- a/apps/choozi/app.js +++ b/apps/choozi/app.js @@ -5,14 +5,15 @@ * James Stanley 2021 */ -var colours = ['#ff0000', '#ff8080', '#00ff00', '#80ff80', '#0000ff', '#8080ff', '#ffff00', '#00ffff', '#ff00ff', '#ff8000', '#ff0080', '#8000ff', '#0080ff']; +var colours = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff']; +var colours2 = ['#808080', '#800080', '#808000', '#008080', '#ff4040', '#40ff40', '#4040ff']; var stepAngle = 0.18; // radians - resolution of polygon var gapAngle = 0.035; // radians - gap between segments -var perimMin = 110; // px - min. radius of perimeter -var perimMax = 120; // px - max. radius of perimeter +var perimMin = g.getWidth()*0.44; // px - min. radius of perimeter +var perimMax = g.getWidth()*0.49; // px - max. radius of perimeter -var segmentMax = 106; // px - max radius of filled-in segment +var segmentMax = g.getWidth()*0.42; // px - max radius of filled-in segment var segmentStep = 5; // px - step size of segment fill animation var circleStep = 4; // px - step size of circle fill animation @@ -22,10 +23,10 @@ var minSpeed = 0.001; // rad/sec var animStartSteps = 300; // how many steps before it can start slowing? var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate var ballSize = 3; // px - ball radius -var ballTrack = 100; // px - radius of ball path +var ballTrack = g.getWidth()*0.40; // px - radius of ball path -var centreX = 120; // px - centre of screen -var centreY = 120; // px - centre of screen +var centreX = g.getWidth()*0.5; // px - centre of screen +var centreY = g.getWidth()*0.5; // px - centre of screen var fontSize = 50; // px @@ -33,7 +34,6 @@ var radians = 2*Math.PI; // radians per circle var defaultN = 3; // default value for N var minN = 2; -var maxN = colours.length; var N; var arclen; @@ -82,6 +82,7 @@ function arc(minR, maxR, minAngle, maxAngle) { // draw the arc segments around the perimeter function drawPerimeter() { + g.setBgColor('#000000'); g.clear(); for (var i = 0; i < N; i++) { g.setColor(colours[i%colours.length]); @@ -131,6 +132,7 @@ function animateChoice(target) { g.fillCircle(x, y, ballSize); oldx=x; oldy=y; + if (process.env.HWVERSION == 2) g.flip(); } } @@ -186,23 +188,23 @@ function readN() { } shuffle(colours); // is this really best? -Bangle.setLCDMode("direct"); -Bangle.setLCDTimeout(0); // keep screen on +shuffle(colours2); +colours=colours.concat(colours2); +var maxN = colours.length; +if (process.env.HWVERSION == 1){ + Bangle.setLCDMode("direct"); + Bangle.setLCDTimeout(0); // keep screen on +} readN(); drawN(); -setWatch(() => { - setN(N+1); - drawN(); -}, BTN1, {repeat:true}); - -setWatch(() => { - writeN(); - drawPerimeter(); - choose(); -}, BTN2, {repeat:true}); - -setWatch(() => { - setN(N-1); - drawN(); -}, BTN3, {repeat:true}); +Bangle.setUI("updown", (v)=>{ + if (!v){ + writeN(); + drawPerimeter(); + choose(); + } else { + setN(N+v); + drawN(); + } +}); diff --git a/apps/choozi/appb2.js b/apps/choozi/appb2.js deleted file mode 100644 index f60c16300..000000000 --- a/apps/choozi/appb2.js +++ /dev/null @@ -1,211 +0,0 @@ -/* Choozi - Choose people or things at random using Bangle.js. - * Inspired by the "Chwazi" Android app - * - * James Stanley 2021 - */ - -var colours = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff']; -var colours2 = ['#808080', '#800080', '#808000', '#008080', '#ff4040', '#40ff40', '#4040ff']; - -var stepAngle = 0.18; // radians - resolution of polygon -var gapAngle = 0.035; // radians - gap between segments -var perimMin = 80; // px - min. radius of perimeter -var perimMax = 87; // px - max. radius of perimeter - -var segmentMax = 70; // px - max radius of filled-in segment -var segmentStep = 5; // px - step size of segment fill animation -var circleStep = 4; // px - step size of circle fill animation - -// rolling ball animation: -var maxSpeed = 0.08; // rad/sec -var minSpeed = 0.001; // rad/sec -var animStartSteps = 300; // how many steps before it can start slowing? -var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate -var ballSize = 3; // px - ball radius -var ballTrack = 75; // px - radius of ball path - -var centreX = 88; // px - centre of screen -var centreY = 88; // px - centre of screen - -var fontSize = 50; // px - -var radians = 2*Math.PI; // radians per circle - -var defaultN = 3; // default value for N -var minN = 2; -var N; -var arclen; - -// https://www.frankmitchell.org/2015/01/fisher-yates/ -function shuffle(array) { - var i = 0 - , j = 0 - , temp = null; - - for (i = array.length - 1; i > 0; i -= 1) { - j = Math.floor(Math.random() * (i + 1)); - temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } -} - -// draw an arc between radii minR and maxR, and between -// angles minAngle and maxAngle -function arc(minR, maxR, minAngle, maxAngle) { - var step = stepAngle; - var angle = minAngle; - var inside = []; - var outside = []; - var c, s; - while (angle < maxAngle) { - c = Math.cos(angle); - s = Math.sin(angle); - inside.push(centreX+c*minR); // x - inside.push(centreY+s*minR); // y - // outside coordinates are built up in reverse order - outside.unshift(centreY+s*maxR); // y - outside.unshift(centreX+c*maxR); // x - angle += step; - } - c = Math.cos(maxAngle); - s = Math.sin(maxAngle); - inside.push(centreX+c*minR); - inside.push(centreY+s*minR); - outside.unshift(centreY+s*maxR); - outside.unshift(centreX+c*maxR); - - var vertices = inside.concat(outside); - g.fillPoly(vertices, true); -} - -// draw the arc segments around the perimeter -function drawPerimeter() { - g.setBgColor('#000000'); - g.clear(); - for (var i = 0; i < N; i++) { - g.setColor(colours[i%colours.length]); - var minAngle = (i/N)*radians; - arc(perimMin,perimMax,minAngle,minAngle+arclen); - } -} - -// animate a ball rolling around and settling at "target" radians -function animateChoice(target) { - var angle = 0; - var speed = 0; - var oldx = -10; - var oldy = -10; - var decelFromAngle = -1; - var allowDecel = false; - for (var i = 0; true; i++) { - angle = angle + speed; - if (angle > radians) angle -= radians; - if (i < animStartSteps || (speed < maxSpeed && !allowDecel)) { - speed = speed + accel; - if (speed > maxSpeed) { - speed = maxSpeed; - /* when we reach max speed, we know how long it takes - * to accelerate, and therefore how long to decelerate, so - * we can work out what angle to start decelerating from */ - if (decelFromAngle < 0) { - decelFromAngle = target-angle; - while (decelFromAngle < 0) decelFromAngle += radians; - while (decelFromAngle > radians) decelFromAngle -= radians; - } - } - } else { - if (!allowDecel && (angle < decelFromAngle) && (angle+speed >= decelFromAngle)) allowDecel = true; - if (allowDecel) speed = speed - accel; - if (speed < minSpeed) speed = minSpeed; - if (speed == minSpeed && angle < target && angle+speed >= target) return; - } - - var r = i/2; - if (r > ballTrack) r = ballTrack; - var x = centreX+Math.cos(angle)*r; - var y = centreY+Math.sin(angle)*r; - g.setColor('#000000'); - g.fillCircle(oldx,oldy,ballSize+1); - g.setColor('#ffffff'); - g.fillCircle(x, y, ballSize); - oldx=x; - oldy=y; - g.flip(); - } -} - -// choose a winning segment and animate its selection -function choose() { - var chosen = Math.floor(Math.random()*N); - var minAngle = (chosen/N)*radians; - var maxAngle = minAngle + arclen; - animateChoice((minAngle+maxAngle)/2); - g.setColor(colours[chosen%colours.length]); - for (var i = segmentMax-segmentStep; i >= 0; i -= segmentStep) - arc(i, perimMax, minAngle, maxAngle); - arc(0, perimMax, minAngle, maxAngle); - for (var r = 1; r < segmentMax; r += circleStep) - g.fillCircle(centreX,centreY,r); - g.fillCircle(centreX,centreY,segmentMax); -} - -// draw the current value of N in the middle of the screen, with -// up/down arrows -function drawN() { - g.setColor('#ffffff'); - g.setFont("Vector",fontSize); - g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2); - if (N < maxN) - g.fillPoly([centreX-6,centreY-fontSize/2-7, centreX+6,centreY-fontSize/2-7, centreX, centreY-fontSize/2-14]); - if (N > minN) - g.fillPoly([centreX-6,centreY+fontSize/2+5, centreX+6,centreY+fontSize/2+5, centreX, centreY+fontSize/2+12]); -} - -// update number of segments, with min/max limit, "arclen" update, -// and screen reset -function setN(n) { - N = n; - if (N < minN) N = minN; - if (N > maxN) N = maxN; - arclen = radians/N - gapAngle; - drawPerimeter(); -} - -// save N to choozi.txt -function writeN() { - var file = require("Storage").open("choozi.txt","w"); - file.write(N); -} - -// load N from choozi.txt -function readN() { - var file = require("Storage").open("choozi.txt","r"); - var n = file.readLine(); - if (n !== undefined) setN(parseInt(n)); - else setN(defaultN); -} - -shuffle(colours); // is this really best? -shuffle(colours2); -colours=colours.concat(colours2); -var maxN = colours.length; - -readN(); -drawN(); - -setWatch(() => { - writeN(); - drawPerimeter(); - choose(); -}, BTN1, {repeat:true}); - -Bangle.on('touch', function(zone,e) { - if(e.x>+88){ - setN(N-1); - drawN(); - }else{ - setN(N+1); - drawN(); - } -}); diff --git a/apps/choozi/metadata.json b/apps/choozi/metadata.json index 79af76fa2..f0d309560 100644 --- a/apps/choozi/metadata.json +++ b/apps/choozi/metadata.json @@ -10,8 +10,7 @@ "allow_emulator": true, "screenshots": [{"url":"bangle1-choozi-screenshot1.png"},{"url":"bangle1-choozi-screenshot2.png"}], "storage": [ - {"name":"choozi.app.js","url":"app.js","supports": ["BANGLEJS"]}, - {"name":"choozi.app.js","url":"appb2.js","supports": ["BANGLEJS2"]}, + {"name":"choozi.app.js","url":"app.js"}, {"name":"choozi.img","url":"app-icon.js","evaluate":true} ] }