Last active
March 9, 2020 23:40
-
-
Save dukuo/29f8a97a81ba2b4cce832eb410025e77 to your computer and use it in GitHub Desktop.
Spark Face Tracker Timer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// padStart polyfill | |
if (!String.prototype.padStart) { | |
String.prototype.padStart = function (n,str){ | |
return Array(n-String(this).length+1).join(str||'0')+this; | |
} | |
} | |
const states = { | |
PAUSE: 1, | |
START: 2, | |
RESET: 4, | |
STOP: 8 | |
} | |
const Patches = require ('Patches'); | |
const FT = require('FaceTracking'); | |
const Diagnostics = require('Diagnostics') | |
const Time = require('Time'); | |
const face = FT.face(0); | |
const ms = 30; // run each 30 milliseconds | |
const openSensitivity = 0.1; // to trigger a timer if the mouth is X open. Set to a relatively low value to avoid flickering. | |
let elapsedTime = 0; | |
let pausedTime = 0; | |
const currentInterval = { | |
isPausedTimer: false, | |
interval: undefined | |
} | |
function setElapsedTime(time) { | |
elapsedTime = time; | |
} | |
function getElapsedTime() { | |
return elapsedTime; | |
} | |
function getPausedTime(){ | |
return pausedTime; | |
} | |
function setPauseTime(time) { | |
pausedTime = time; | |
} | |
function setCurrentInterval(state, interval) { | |
const current = getCurrentInterval() | |
if(!!!interval && !!current.interval) { // remove the current interval from Time. | |
Time.clearInterval(current.interval) | |
} | |
Diagnostics.log(`Current interval ${state} ${interval}`) | |
currentInterval.state = state; | |
currentInterval.interval = interval; | |
} | |
function getCurrentInterval(){ | |
return currentInterval; | |
} | |
function buildFormattedTimer(delta) { | |
const format = function (value, scale, modulo, padding) { | |
value = Math.floor(value / scale) % modulo; | |
return value.toString().padStart(padding, 0); | |
} | |
const s = format(delta, 1000, 60, 2); | |
const m = format(delta, 60000, 60, 2); | |
const ms = format(delta, 1, 1000, 3); | |
const _s = ':'; | |
return [m, _s, s, _s, ms]; | |
} | |
function handleTimer(state){ | |
const currInterval = getCurrentInterval() | |
if(!!(state & states.STOP)) { | |
Diagnostics.log("Stopping timer.") | |
setCurrentInterval( states.STOP, undefined ) | |
} else | |
if(!!(state & states.RESET)) { | |
// resetTimer() | |
if( !!currentInterval.interval ) { // If timer exists, remove it and reset the overall state of the timer. | |
Diagnostics.log("resetting timer.") | |
setCurrentInterval( states.STOP, undefined); | |
setElapsedTime(0) | |
const _s = ':' | |
const [m, s, ms] = ['00', '00', '000']; | |
printTimer([m, _s, s, _s, ms]) | |
return; | |
} | |
} else if (!!(state & states.START | state & states.PAUSE) && !!!currInterval.interval) { | |
Diagnostics.log("Setting new timer interval") | |
const start = Date.now(); | |
const interval = Time.setInterval(function (){ | |
let delta = Date.now() - start; | |
if(!!(state & states.PAUSE)) { | |
setPauseTime(getPausedTime() + delta) | |
} else { | |
const elapsedTime = getElapsedTime(); | |
if( getPausedTime() > 0) { | |
delta = Math.abs(Date.now() - (start - getPausedTime() )); | |
setPauseTime(0) | |
} | |
const timer = buildFormattedTimer(delta); | |
printTimer(timer); | |
setElapsedTime (elapsedTime + delta); | |
} | |
}, ms); | |
setCurrentInterval(states.STOP, undefined) // Always clear the previous timer. | |
setCurrentInterval(state, interval) | |
} | |
} | |
// function resetTimer() { | |
// const currentInterval = getCurrentInterval(); | |
// } | |
function printTimer(timer){ | |
Patches.setStringValue('timer', timer.join('')) | |
} | |
face.mouth.openness.monitor().subscribe((e) => { | |
const currentInterval = getCurrentInterval(); | |
let intervalState = states.START; // Start by default. | |
if(e.newValue < openSensitivity && !!currentInterval.interval && !!(states.PAUSE & currentInterval.state | states.START & currentInterval.state) ) // mouth is closing / closed | |
{ | |
Diagnostics.log("Mouth is closing") | |
intervalState = states.STOP // states.[STOP | RESET | PAUSE] | |
handleTimer(intervalState) | |
} | |
if( e.newValue > openSensitivity && (!!!currentInterval.interval || !!(states.PAUSE & currentInterval.state | states.STOP & currentInterval.state))){ | |
handleTimer(intervalState) | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment