Skip to content

Commit

Permalink
tweaks to placing of things
Browse files Browse the repository at this point in the history
  • Loading branch information
thyttan committed Feb 22, 2023
1 parent cd001d8 commit 3ee5c23
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 66 deletions.
2 changes: 1 addition & 1 deletion apps/run/ChangeLog
Expand Up @@ -13,5 +13,5 @@
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
0.15: (beta) Swipe to intensity interface a la Korvonnen (curtesy of
0.15: (beta) Swipe to intensity interface a la Karvonnen (curtesy of
FTeacher at https://github.com/f-teacher)
164 changes: 99 additions & 65 deletions apps/run/karvonnen.js
@@ -1,20 +1,27 @@
// Korvonnen pasted inside a function
exports.show = function karvonnen(hrmSettings, exsHrmStats) {
//This app is an extra feature implementation for the Run.app of the bangle.js. It's called run+
//The calculation of the Heart Rate Zones is based on the Karvonnen method. It requires to know maximum and minimum heart rates. More precise calculation methods require a lab.
//Other methods are even more approximative.
wu = require("widget_utils");
let wu = require("widget_utils");
wu.hide();
let R = Bangle.appRect;


g.reset().clearRect(R);
g.reset().clearRect(R).setFontAlign(0,0,0);

const x = "x"; const y = "y";
function Rdiv(xOrY, divisor) { // Used when placing things on the screen
return xOrY=="x" ? (R.x + (R.w-1)/divisor):(R.y + (R.h-1)/divisor);
}

function drawLines() {
g.drawLine(40,64,88,52,136,64);
g.drawLine(88,52,136,64);
g.drawLine(40,112,88,124);
g.drawLine(88,124,132,112);
g.setFont("Vector",20);
g.setColor(g.theme.fg);
// Upper
g.drawLine(Rdiv(x,175/40), Rdiv(y,175/64), Rdiv(x,2), Rdiv(y,175/52));
g.drawLine(Rdiv(x,2), Rdiv(y,175/52), Rdiv(x,175/135), Rdiv(y,175/64));
// Lower
g.drawLine(Rdiv(x,175/40), Rdiv(y,175/110), Rdiv(x,2), Rdiv(y,175/122));
g.drawLine(Rdiv(x,2), Rdiv(y,175/122), Rdiv(x,175/135), Rdiv(y,175/110));
}

//To calculate Heart rate zones, we need to know the heart rate reserve (HRR)
Expand Down Expand Up @@ -42,34 +49,51 @@ exports.show = function karvonnen(hrmSettings, exsHrmStats) {
let maxzone3 = hrr * 0.8 + minhr;
let maxzone4 = hrr * 0.9 + minhr;
let maxzone5 = hrr * 0.99 + minhr;

// HR data: large, readable, in the middle of the screen
function drawHR() {
g.clearRect(62,66,62+90,70+40);
g.setColor(1,1,1);
g.setFontAlign(-1,0,0);
g.clearRect(Rdiv(x,11/4),Rdiv(y,2)-25,Rdiv(x,11/4)+50*2,Rdiv(y,2)+25);
g.setColor(g.theme.fg);
g.setFont("Vector",50);
g.drawString(hr, 62,66);
g.drawString(hr, Rdiv(x,11/4), Rdiv(y,2)+4);
}

function drawWaitHR() {
g.setFontAlign(0,0,0);
//g.clearRect(62,66,62+90,70+40);
g.setColor(g.theme.fg);
g.setFont("Vector",50);
g.drawString("--", Rdiv(x,2), Rdiv(y,2)+4);

// draw values of upper and lower limit of current zone
g.setFont("Vector",20);
g.setColor(g.theme.fg);
g.drawString("--", Rdiv(x,2), Rdiv(y,9/2));
g.drawString("--", Rdiv(x,2), Rdiv(y,9/7));

g.setFont("Vector",24);
g.drawString("Z-", Rdiv(x,4.3), Rdiv(y,2)+2);
}
drawHR();

//These functions call arcs to show different HR zones.

//To shorten the code, I'll reference some letiables and reuse them.
let centreX = R.x + 0.5 * R.w; //g.getWidth();
let centreY = R.y + 0.5 * R.h; //g.getWidth();
let minRadius = 0.38 * R.h; //g.getWidth();
let maxRadius = 0.50 * R.h; //g.getWidth();
let centreX = R.x + 0.5 * R.w;
let centreY = R.y + 0.5 * R.h;
let minRadius = 0.38 * R.h;
let maxRadius = 0.50 * R.h;

//draw background image (dithered green zones)(I should draw different zones in different dithered colors)
const HRzones= require("graphics_utils");
let minRadiusz = 0.44 * R.h;//g.getWidth();
let minRadiusz = 0.44 * R.h;
let startAngle = HRzones.degreesToRadians(-88.5);
let endAngle = HRzones.degreesToRadians(268.5);

function drawBackGround() {
g.setColor("#002200");
HRzones.fillArc(g, centreX, centreY, minRadiusz, maxRadius, startAngle, endAngle);
}
drawBackGround();

const zones = require("graphics_utils");
//####### A function to simplify a bit the code ######
Expand All @@ -79,39 +103,42 @@ exports.show = function karvonnen(hrmSettings, exsHrmStats) {
if (currentZone == lastZone) zones.fillArc(g, centreX, centreY, minRadius, maxRadius, startAngle, endAngle);
else zones.fillArc(g, centreX, centreY, minRadiusz, maxRadius, startAngle, endAngle);
g.setFont("Vector",24);
g.clearRect(29,80,29+26,80+24);
g.drawString(Z, 29,80);
g.clearRect(Rdiv(x,4.3)-12, Rdiv(y,2)+2-12,Rdiv(x,4.3)+12, Rdiv(y,2)+2+12);
g.setFontAlign(0,0,0);
g.drawString(Z, Rdiv(x,4.3), Rdiv(y,2)+2);
}

//####### A function to simplify next&previous zones ######
function zoning (max, min) {
function zoning (max, min) { // draw values of upper and lower limit of current zone
g.setFont("Vector",20);
g.setColor(1,1,1);
g.clearRect(56,28,56+20*3,28+20).clearRect(60,128,60+20*3,128+20);
g.drawString(max, 56,28);g.drawString(min, 60,128);
g.setColor(g.theme.fg);
g.clearRect(Rdiv(x,2)-20*2, Rdiv(y,9/2)-10,Rdiv(x,2)+20*2, Rdiv(y,9/2)+10);
g.clearRect(Rdiv(x,2)-20*2, Rdiv(y,9/7)-10,Rdiv(x,2)+20*2, Rdiv(y,9/7)+10);
g.setFontAlign(0,0,0);
g.drawString(max, Rdiv(x,2), Rdiv(y,9/2));
g.drawString(min, Rdiv(x,2), Rdiv(y,9/7));
}

function clearInnerRing() {
g.setColor(0,0,0);
function clearCurrentZone() { // Clears the extension of the current zone by painting the extension area in background color
g.setColor(g.theme.bg);
HRzones.fillArc(g, centreX, centreY, minRadius-1, minRadiusz, startAngle, endAngle);
}

function getZone(zone) {
drawBackGround();
clearInnerRing();
if (zone >= 0) {g.setColor("#00ffff");{(simplify(-88.5, -45, "Z1", 0, zone));}zoning(minzone2, minhr);}
if (zone >= 1) {g.setColor("#00ff00");{(simplify(-43.5, -21.5, "Z2", 1, zone));}zoning(maxzone2, minzone2);}
if (zone >= 2) {g.setColor("#00ff00");{(simplify(-20, 1.5, "Z2", 2, zone));}zoning(maxzone2, minzone2);}
if (zone >= 3) {g.setColor("#00ff00");{(simplify(3, 24, "Z2", 3, zone));}zoning(maxzone2, minzone2);}
if (zone >= 4) {g.setColor("#ffff00");{(simplify(25.5, 46.5, "Z3", 4, zone));}zoning(maxzone3, maxzone2);}
if (zone >= 5) {g.setColor("#ffff00");{(simplify(48, 69, "Z3", 5, zone));}zoning(maxzone3, maxzone2);}
if (zone >= 6) {g.setColor("#ffff00");{(simplify(70.5, 91.5, "Z3", 6, zone));}zoning(maxzone3, maxzone2);}
if (zone >= 7) {g.setColor("#ff8000");{(simplify(93, 114.5, "Z4", 7, zone));}zoning(maxzone4, maxzone3);}
if (zone >= 8) {g.setColor("#ff8000");{(simplify(116, 137.5, "Z4", 8, zone));}zoning(maxzone4, maxzone3);}
if (zone >= 9) {g.setColor("#ff8000");{(simplify(139, 160, "Z4", 9, zone));}zoning(maxzone4, maxzone3);}
if (zone >= 10) {g.setColor("#ff0000");{(simplify(161.5, 182.5, "Z5", 10, zone));}zoning(maxzone5, maxzone4);}
if (zone >= 11) {g.setColor("#ff0000");{(simplify(184, 205, "Z5", 11, zone));}zoning(maxzone5, maxzone4);}
if (zone == 12) {g.setColor("#ff0000");{(simplify(206.5, 227.5, "Z5", 12, zone));}zoning(maxzone5, maxzone4);}
clearCurrentZone();
if (zone >= 0) {zoning(minzone2, minhr);g.setColor("#00ffff");simplify(-88.5, -45, "Z1", 0, zone);}
if (zone >= 1) {zoning(maxzone2, minzone2);g.setColor("#00ff00");simplify(-43.5, -21.5, "Z2", 1, zone);}
if (zone >= 2) {zoning(maxzone2, minzone2);g.setColor("#00ff00");simplify(-20, 1.5, "Z2", 2, zone);}
if (zone >= 3) {zoning(maxzone2, minzone2);g.setColor("#00ff00");simplify(3, 24, "Z2", 3, zone);}
if (zone >= 4) {zoning(maxzone3, maxzone2);g.setColor("#ffff00");simplify(25.5, 46.5, "Z3", 4, zone);}
if (zone >= 5) {zoning(maxzone3, maxzone2);g.setColor("#ffff00");simplify(48, 69, "Z3", 5, zone);}
if (zone >= 6) {zoning(maxzone3, maxzone2);g.setColor("#ffff00");simplify(70.5, 91.5, "Z3", 6, zone);}
if (zone >= 7) {zoning(maxzone4, maxzone3);g.setColor("#ff8000");simplify(93, 114.5, "Z4", 7, zone);}
if (zone >= 8) {zoning(maxzone4, maxzone3);g.setColor("#ff8000");simplify(116, 137.5, "Z4", 8, zone);}
if (zone >= 9) {zoning(maxzone4, maxzone3);g.setColor("#ff8000");simplify(139, 160, "Z4", 9, zone);}
if (zone >= 10) {zoning(maxzone5, maxzone4);g.setColor("#ff0000");simplify(161.5, 182.5, "Z5", 10, zone);}
if (zone >= 11) {zoning(maxzone5, maxzone4);g.setColor("#ff0000");simplify(184, 205, "Z5", 11, zone);}
if (zone == 12) {zoning(maxzone5, maxzone4);g.setColor("#ff0000");simplify(206.5, 227.5, "Z5", 12, zone);}
}

function getZoneAlert() {
Expand All @@ -128,39 +155,46 @@ exports.show = function karvonnen(hrmSettings, exsHrmStats) {
//Subdivided zones for better readability of zones when calling the images. //Changing HR zones will trigger the function with the image and previous&next HR zones.
let subZoneLast;
function drawZones() {
if ((hr < maxhr - 2) && subZoneLast==13) {g.clear(); drawLines(); drawHR();}

if (hr <= hrr * 0.6 + minhr) {if (subZoneLast!=0) {subZoneLast=0; getZone(subZoneLast);}}
else if (hr <= hrr * 0.64 + minhr) {if (subZoneLast!=1) {subZoneLast=1; getZone(subZoneLast);}}
else if (hr <= hrr * 0.67 + minhr) {if (subZoneLast!=2) {subZoneLast=2; getZone(subZoneLast);}}
else if (hr <= hrr * 0.70 + minhr) {if (subZoneLast!=3) {subZoneLast=3; getZone(subZoneLast);}}
else if (hr <= hrr * 0.74 + minhr) {if (subZoneLast!=4) {subZoneLast=4; getZone(subZoneLast);}}
else if (hr <= hrr * 0.77 + minhr) {if (subZoneLast!=5) {subZoneLast=5; getZone(subZoneLast);}}
else if (hr <= hrr * 0.80 + minhr) {if (subZoneLast!=6) {subZoneLast=6; getZone(subZoneLast);}}
else if (hr <= hrr * 0.84 + minhr) {if (subZoneLast!=7) {subZoneLast=7; getZone(subZoneLast);}}
else if (hr <= hrr * 0.87 + minhr) {if (subZoneLast!=8) {subZoneLast=8; getZone(subZoneLast);}}
else if (hr <= hrr * 0.90 + minhr) {if (subZoneLast!=9) {subZoneLast=9; getZone(subZoneLast);}}
else if (hr <= hrr * 0.94 + minhr) {if (subZoneLast!=10) {subZoneLast=10; getZone(subZoneLast);}}
else if (hr <= hrr * 0.96 + minhr) {if (subZoneLast!=11) {subZoneLast=11; getZone(subZoneLast);}}
else if (hr <= hrr * 0.98 + minhr) {if (subZoneLast!=12) {subZoneLast=12; getZone(subZoneLast);}}
else if (hr >= maxhr - 2) {if (subZoneLast!=13) {subZoneLast=13; g.clear();getZoneAlert();}}
if ((hr < maxhr - 2) && subZoneLast==13) {g.clear(); drawLines(); drawHR();} // Reset UI when coming down from zone alert.
if (hr <= hrr * 0.6 + minhr) {if (subZoneLast!=0) {subZoneLast=0; getZone(subZoneLast);}} // Z1
else if (hr <= hrr * 0.64 + minhr) {if (subZoneLast!=1) {subZoneLast=1; getZone(subZoneLast);}} // Z2a
else if (hr <= hrr * 0.67 + minhr) {if (subZoneLast!=2) {subZoneLast=2; getZone(subZoneLast);}} // Z2b
else if (hr <= hrr * 0.70 + minhr) {if (subZoneLast!=3) {subZoneLast=3; getZone(subZoneLast);}} // Z2c
else if (hr <= hrr * 0.74 + minhr) {if (subZoneLast!=4) {subZoneLast=4; getZone(subZoneLast);}} // Z3a
else if (hr <= hrr * 0.77 + minhr) {if (subZoneLast!=5) {subZoneLast=5; getZone(subZoneLast);}} // Z3b
else if (hr <= hrr * 0.80 + minhr) {if (subZoneLast!=6) {subZoneLast=6; getZone(subZoneLast);}} // Z3c
else if (hr <= hrr * 0.84 + minhr) {if (subZoneLast!=7) {subZoneLast=7; getZone(subZoneLast);}} // Z4a
else if (hr <= hrr * 0.87 + minhr) {if (subZoneLast!=8) {subZoneLast=8; getZone(subZoneLast);}} // Z4b
else if (hr <= hrr * 0.90 + minhr) {if (subZoneLast!=9) {subZoneLast=9; getZone(subZoneLast);}} // Z4c
else if (hr <= hrr * 0.94 + minhr) {if (subZoneLast!=10) {subZoneLast=10; getZone(subZoneLast);}} // Z5a
else if (hr <= hrr * 0.96 + minhr) {if (subZoneLast!=11) {subZoneLast=11; getZone(subZoneLast);}} // Z5b
else if (hr <= hrr * 0.98 + minhr) {if (subZoneLast!=12) {subZoneLast=12; getZone(subZoneLast);}} // Z5c
else if (hr >= maxhr - 2) {subZoneLast=13; g.clear();getZoneAlert();} // Alert
}
drawZones();

function initDraw() {
drawLines();
drawWaitHR();
drawBackGround();
//drawZones();
}

let hrLast;
function updateUI() {
function updateUI() { // Update UI, only draw if warranted by change in HR
hrLast = hr;
hr = exsHrmStats.getValue();
if (hr!=hrLast) {
drawHR();
drawZones();
}
}
updateUI();

karvonnenInterval = setInterval(function() {
if (!isMenuDisplayed && karvonnenActive) updateUI();
}, 1000);

initDraw();

// check for updates every second.
karvonnenInterval = setInterval(function() {
if (!isMenuDisplayed && karvonnenActive) updateUI();
}, 1000);

return karvonnenInterval;
};

0 comments on commit 3ee5c23

Please sign in to comment.