├── .gitignore ├── Screenshot Fenix3.jpg ├── Screenshot Round Watch.jpg ├── manifest.xml ├── resources ├── images │ ├── launcher_icon.png │ └── monkey.png └── resources.xml └── source ├── HRCirclesApp.mc └── HRCirclesView.mc /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bin/HRCircles.prg.debug.xml 3 | 4 | bin/HRCircles.iq 5 | 6 | bin/HRCircles.prg 7 | -------------------------------------------------------------------------------- /Screenshot Fenix3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anderssonfilip/HRCircles/30954697b9ef85ac8ce589f28babf49e9a7266c9/Screenshot Fenix3.jpg -------------------------------------------------------------------------------- /Screenshot Round Watch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anderssonfilip/HRCircles/30954697b9ef85ac8ce589f28babf49e9a7266c9/Screenshot Round Watch.jpg -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | eng 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/images/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anderssonfilip/HRCircles/30954697b9ef85ac8ce589f28babf49e9a7266c9/resources/images/launcher_icon.png -------------------------------------------------------------------------------- /resources/images/monkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anderssonfilip/HRCircles/30954697b9ef85ac8ce589f28babf49e9a7266c9/resources/images/monkey.png -------------------------------------------------------------------------------- /resources/resources.xml: -------------------------------------------------------------------------------- 1 | 2 | HRZones 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /source/HRCirclesApp.mc: -------------------------------------------------------------------------------- 1 | using Toybox.Application as App; 2 | 3 | class HRCirclesApp extends App.AppBase { 4 | 5 | //! onStart() is called on application start up 6 | function onStart() { 7 | } 8 | 9 | //! onStop() is called when your application is exiting 10 | function onStop() { 11 | } 12 | 13 | //! Return the initial view of your application here 14 | function getInitialView() { 15 | return [ new HRCirclesView() ]; 16 | 17 | 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /source/HRCirclesView.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Graphics as Gfx; 3 | using Toybox.UserProfile as User; 4 | using Toybox.Math as Math; 5 | 6 | class HRCirclesView extends Ui.DataField { 7 | 8 | const age = Time.Gregorian.info(Time.now(), Time.FORMAT_SHORT).year - User.getProfile().birthYear.toNumber(); 9 | 10 | const maxHR = 220 - age; 11 | var hr = 0; 12 | const restingHR = User.getProfile().restingHeartRate.toNumber(); 13 | const hrr = maxHR - restingHR; 14 | 15 | var timeInZone = new [5]; 16 | var zones = [48, 58, 67, 77, 88]; // percentages 17 | var timeInZone = [0, 0, 0, 0, 0]; 18 | 19 | //! Rendering Constants 20 | const BAR_THICKNESS = 7; 21 | const ARC_MAX_ITERS = 300; 22 | 23 | var numberColor = Gfx.COLOR_WHITE; 24 | 25 | const fullCircle = Math.PI * 2; 26 | 27 | function initialize() { 28 | 29 | // convert to beats 30 | for(var i = 0; i < zones.size(); i++){ 31 | zones[i] = ((zones[i] / 100f) * hrr).toNumber(); 32 | } 33 | } 34 | 35 | //! The given info object contains all the current workout information. 36 | //! Calculate a value and return it in this method. 37 | function compute(info) { 38 | 39 | if(info.currentHeartRate == null) // no activity 40 | { 41 | return; 42 | } 43 | 44 | hr = info.currentHeartRate - restingHR; 45 | 46 | if(hr > zones[0] && hr <= zones[1]) 47 | { 48 | timeInZone[0] += 1; 49 | numberColor = Gfx.COLOR_DK_GREEN; 50 | } 51 | else if(hr > zones[1] && hr <= zones[2]) 52 | { 53 | timeInZone[1] += 1; 54 | numberColor = Gfx.COLOR_GREEN; 55 | } 56 | else if(hr > zones[2] && hr <= zones[3]) 57 | { 58 | timeInZone[2] += 1; 59 | numberColor = Gfx.COLOR_YELLOW; 60 | } 61 | else if(hr > zones[3] && hr <= zones[4]) 62 | { 63 | timeInZone[3] += 1; 64 | numberColor = Gfx.COLOR_ORANGE; 65 | } 66 | else if(hr > zones[4]) 67 | { 68 | timeInZone[4] += 1; 69 | numberColor = Gfx.COLOR_RED; 70 | } 71 | 72 | return info.currentHeartRate; 73 | } 74 | 75 | function onUpdate(dc){ 76 | 77 | if(hr <= 0.0) 78 | { 79 | return; 80 | } 81 | 82 | // normalize zones with total time 83 | var sum = 0.0; 84 | for(var i = 0; i < timeInZone.size(); i++) 85 | { 86 | sum += timeInZone[i]; 87 | } 88 | 89 | if(sum <= 0.0) 90 | { 91 | return; 92 | } 93 | 94 | dc.clear(); 95 | dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_RED); 96 | dc.fillRectangle(0, 0, dc.getWidth(), dc.getHeight()); 97 | dc.setColor(numberColor, Gfx.COLOR_TRANSPARENT); 98 | 99 | dc.drawText(dc.getWidth()/2, dc.getHeight()/2 - 48, Gfx.FONT_NUMBER_HOT, (hr + restingHR).toString(), Gfx.TEXT_JUSTIFY_CENTER); 100 | 101 | // width & height assumed equal to draw arc of a circle 102 | var c = dc.getWidth()/2; 103 | //var cy = dc.getHeight()/2; 104 | 105 | // draw arc for each zone 106 | drawArc(dc, c, c, c-BAR_THICKNESS, (fullCircle * timeInZone[0]) / sum, Gfx.COLOR_DK_GREEN); 107 | drawArc(dc, c, c, c-BAR_THICKNESS * 2, (fullCircle * timeInZone[1]) / sum, Gfx.COLOR_GREEN); 108 | drawArc(dc, c, c, c-BAR_THICKNESS * 4, (fullCircle * timeInZone[2]) / sum, Gfx.COLOR_YELLOW); 109 | drawArc(dc, c, c, c-BAR_THICKNESS * 6, (fullCircle * timeInZone[3]) / sum, Gfx.COLOR_ORANGE); 110 | drawArc(dc, c, c, c-BAR_THICKNESS * 8, (fullCircle * timeInZone[4]) / sum, Gfx.COLOR_RED); 111 | } 112 | 113 | //! Fast (but kind of bad-looking) arc drawing. 114 | //! From http://stackoverflow.com/questions/8887686/arc-subdivision-algorithm/8889666#8889666 115 | //! TODO: Once we have drawArc, use that instead. 116 | function drawArc(dc, cx, cy, radius, theta, color) { 117 | 118 | dc.setColor(color, Gfx.COLOR_BLACK); 119 | 120 | var iters = ARC_MAX_ITERS * (theta / (2 * Math.PI)); 121 | var dx = 0; 122 | var dy = -radius; 123 | var ctheta = Math.cos(theta/(iters - 1)); 124 | var stheta = Math.sin(theta/(iters - 1)); 125 | 126 | dc.fillCircle(cx + dx, cy + dy, BAR_THICKNESS); 127 | 128 | for(var i=1; i < iters; ++i) { 129 | var dxtemp = ctheta * dx - stheta * dy; 130 | dy = stheta * dx + ctheta * dy; 131 | dx = dxtemp; 132 | dc.fillCircle(cx + dx, cy + dy, BAR_THICKNESS); 133 | } 134 | } 135 | } --------------------------------------------------------------------------------