xxxxxxxxxx
709
/*
to do list:
- "CONFIRM TO RECORD" WHEN ALL RACERS ARE RECORDED???
- update some button tags (more icons maybe)
- clicking the marking again lets the message appear again??
bug fixes:
other notes:
- have it scale to the phone screen?
- have post-race mode?
- undo pop-ups / history?
*/
// colors, fontsizes
const orange=[232,94,2], navy=[35,53,65], presscol=[55,73,85], gridpress=[135,148,157], butcol = [50,68,80], lgrey = 180, dgrey=140, grey3 = 229;
const numpadnumsize = 26, gridnumsize = 24, buttontextsize = 14, timernumsize = 12, timesnumsize = 24, popuptextsize = 12;
// declare for preload
let regular, medium, bold, black, recordsvg, backspcsvg;
// orange[0],orange[1],orange[2] // navy[0],navy[1],navy[2] // presscol[0],presscol[1],presscol[2] // gridpress[0],gridpress[1],gridpress[2] // butcol[0],butcol[1],butcol[2], lgrey[0],lgrey[1],lgrey[2], dgrey[0],dgrey[1],dgrey[2], grey3[0],grey3[1],grey3[2] //
// define mode, true = chatmode, false = gridmode
let mode = true;
let times = [];
let grid = [];
const cwidth = 375, cheight = 685;
const chatmarg = 8,chatheight=45,chatY=cheight-365, chatbufferwidth=55;
const buttonmarg = 8;
const swapY = chatY + chatheight + chatmarg + buttonmarg, swapheight = 50;
const plusY = swapY + swapheight + buttonmarg, plusheight = 50;
const recordY = plusY + plusheight + buttonmarg, recordwidth = 115;
const topheight = 69, changemodebutwidth = 87;
const dashY = chatY-chatmarg, numpadY = dashY + chatmarg*2 + chatheight;
const butwidth = (cwidth-recordwidth-buttonmarg*3)/3, butheight = (cheight-numpadY-buttonmarg*2)/4;
const butx1 = buttonmarg,butx2=butx1+butwidth,butx3=butx2+butwidth,buty1=numpadY+buttonmarg, buty2=buty1+butheight,buty3=buty2+butheight,buty4=buty3+butheight;
const numbers = [[butx2,buty4],[butx1,buty1],[butx2,buty1],[butx3,buty1],[butx1,buty2],[butx2,buty2],[butx3,buty2],[butx1,buty3],[butx2,buty3],[butx3,buty3]];
const timeswidth = 130, timesheight = 34, timesmarg = timesheight+10, timesX = cwidth - timeswidth - buttonmarg, timesY = chatY, bufferheight = 0.75*timesheight;
const gridbutwidth = (cwidth-timeswidth-4*buttonmarg)/3;
let gridbutheight, gridboty;
const gridbotx = 15;
const popupheight = 34, popupwidth = cwidth - buttonmarg*3 - timeswidth - 40;
let press = -1;
let selection = -1;
let typing = "";
let queue = [];
let swapping = false;
let popupmessage,popupopac,fadeout, fadeouttimer, popupX, popupXint;
const duplicatemessage = "entered duplicate racer", unregmessage = "bib number is not registered";
function preload() {
regular = loadFont('Roboto-Regular.ttf');
medium = loadFont('Roboto-Medium.ttf');
bold = loadFont('Roboto-Bold.ttf');
black = loadFont('Roboto-Black.ttf');
recordsvg = loadImage('img/stopwatch2.svg');
backspcsvg = loadImage('img/backspace.svg');
}
function setup() {
createCanvas(cwidth, cheight);
// createButton("switch mode").mousePressed(switchMode);
createButton("export time").mousePressed(exportTimes);
for(i=101;i<132;i++){
grid.push([i,true])
}
gridbutheight = (cheight-topheight-buttonmarg*2)/(Math.ceil(grid.length/3))
gridboty = cheight - buttonmarg - gridbutheight
}
function draw() {
background(255);
noStroke();
// fill(220);
// rect(0,dashY,width,height-dashY);
fill(navy[0],navy[1],navy[2]);
if(mode){
rect(0,numpadY,width,height-numpadY);
}
else{
rect(width-timeswidth-buttonmarg*2,numpadY,timeswidth + buttonmarg*2,height-numpadY);
fill(grey3); stroke(dgrey); strokeWeight(1);
rect(0,0,width-timeswidth-buttonmarg*2,height)
noStroke();
}
// draw buttons
textFont(bold);
if(mode){
textSize(numpadnumsize);
noStroke();
for(i=0;i<numbers.length;i++){
fill(presscol[0],presscol[1],presscol[2]);
if(press==i){
rect(numbers[i][0],numbers[i][1],butwidth,butheight);
}
fill(255);
text(i,numbers[i][0]+butwidth/2-8,numbers[i][1]+butheight/2+8)
}
}
else{
textSize(gridnumsize)
for(i=0;i<grid.length;i++){
if(grid[i][1]){fill(butcol[0], butcol[1], butcol[2]);}
else{fill(gridpress[0],gridpress[1],gridpress[2])}
rect(2*buttonmarg + (i%3) * gridbutwidth - buttonmarg/2, topheight + Math.trunc(i/3) * gridbutheight + buttonmarg, gridbutwidth - buttonmarg, gridbutheight - buttonmarg,15);
fill(255);
text(grid[i][0], 2*buttonmarg + (i%3) * gridbutwidth + 8, topheight + Math.trunc(i/3) * gridbutheight + 40)
}
}
// draw other buttons
textFont(medium); textSize(buttontextsize);
if(mode){
fill(butcol[0],butcol[1],butcol[2]);
if(press=="<-"){fill(presscol[0],presscol[1],presscol[2]);}
rect(butx3+buttonmarg/2,buty4+buttonmarg/2,butwidth-buttonmarg,butheight-buttonmarg,15);
fill(butcol[0],butcol[1],butcol[2]);
if(press=="mark"){fill(presscol[0],presscol[1],presscol[2]);}
rect(butx1+buttonmarg/2,buty4+buttonmarg/2,butwidth-buttonmarg,butheight-buttonmarg,15);
}
fill(butcol[0],butcol[1],butcol[2]);
if(press=="clear"){fill(presscol[0],presscol[1],presscol[2]);}
rect(width-buttonmarg-recordwidth,swapY,recordwidth/2-buttonmarg/2,swapheight,15);
fill(butcol[0],butcol[1],butcol[2]);
if(press=="swap"){fill(presscol[0],presscol[1],presscol[2]);}
rect(width-buttonmarg-recordwidth/2+buttonmarg/2,swapY,recordwidth/2-buttonmarg/2,swapheight,15);
fill(butcol[0],butcol[1],butcol[2]);
if(press=="+"){fill(presscol[0],presscol[1],presscol[2]);}
rect(width-buttonmarg-recordwidth,plusY,recordwidth,plusheight,15);
fill(orange[0],orange[1],orange[2]);
if(press=="record"){fill(orange[0]+20,orange[1]+20,orange[2]+20);}
rect(width-buttonmarg-recordwidth,recordY,recordwidth,height-recordY-buttonmarg,15);
fill(255);
if(mode){
image(backspcsvg,butx3+butwidth/2-18,buty4+butheight/2-12,32,24)
// text("<-",);
text("mark",butx1+butwidth/2-14,buty4+butheight/2+3);
}
text("clear", width-buttonmarg-recordwidth+12,swapY+30);
text("swap", width-buttonmarg-recordwidth/2+12,swapY+30);
text("+ racer", width-buttonmarg-recordwidth+20,plusY+30);
image(recordsvg, width-buttonmarg-recordwidth+34,recordY+60,42,48);
// text("record", width-buttonmarg-recordwidth+35,recordY+80);
// draw chatbox
if(selection == -1){
strokeWeight(4);
stroke(orange[0],orange[1],orange[2]);
}
else{
strokeWeight(2);
stroke(180);
}
fill(255);
if(mode){
rect(chatmarg,chatY,width-2*chatmarg,chatheight,5);}
else{rect(width-buttonmarg-timeswidth, chatY, timeswidth, chatheight,5);}
noStroke()
fill(100);textFont(regular); textSize(timernumsize);
text(hour()+":"+minute()+":"+second()+"."+Math.round(millis()/100)%10,width-chatmarg-70,chatY+30);
fill(0);textFont(medium);textSize(timesnumsize);
let chatbuffer = 0;
for(i=0;i<queue.length;i++){
stroke(180);
strokeWeight(2);
line(chatmarg*2+(chatbuffer+1)*chatbufferwidth+1, chatY+10, chatmarg*2+(chatbuffer+1)*chatbufferwidth+1, chatY+chatheight-10);
noStroke();
text(queue[i],chatmarg*3 + chatbuffer*chatbufferwidth, chatY+30);
chatbuffer++
}
text(typing,chatmarg*3 + chatbuffer*chatbufferwidth,chatY+30);
if(selection == -1 && millis()%1500 < 750 && mode){
stroke(orange[0],orange[1],orange[2]);
strokeWeight(2);
let typingx = chatmarg*3 + chatbuffer*chatbufferwidth + textWidth(typing);
line(typingx,chatY+35, typingx,chatY+10);
noStroke();
}
// draw times
let buffer = 0;
for(i=times.length-1;i>-1;i--){
fill(0);textFont(medium);textSize(timesnumsize);
let curry = timesY - timesmarg * (times.length-i) - buffer*bufferheight;
let curheight = timesheight;
for(j=3;j<times[i].length;j++){
buffer++;
curheight += bufferheight;
curry -= bufferheight;
}
// console.log(buffer);
fill(255);
let currx = timesX;
if(selection == i){
strokeWeight(4);
stroke(orange[0],orange[1],orange[2]);
if(swapping){
currx -= 50;
}
}
else{
strokeWeight(1);
stroke(180);
}
rect(currx, curry, timeswidth, curheight,5);
noStroke();
for(j=3;j<times[i].length;j++){
fill(180);
text(times[i][j], currx + 10, curry + timesnumsize);
stroke(180);
strokeWeight(2);
line(currx+10, curry + timesnumsize - 2, currx + 52, curry + 10)
noStroke();
curry += bufferheight;
}
if(selection==i && millis()%1500 < 750){
stroke(orange[0],orange[1],orange[2]);
strokeWeight(2);
let typingx = currx + 12
if(times[i][0].length<3){
typingx += textWidth(times[i][0]);}
line(typingx, curry+6, typingx, curry+timesnumsize+2);
noStroke();
}
if(selection==i && times[i][0].length==3){
fill(180);
}
else{
fill(0);
}
text(times[i][0], currx + 10, curry + timesnumsize);
if(times[i][2] && mode){
if(times[i][2]=="marked by user"){fill(orange[0],orange[1],orange[2]);} else{fill(200,0,0);}
circle(currx-20,curry+16,16);
fill(255); textFont(black); textSize(14);
text("!",currx-22,curry+21)
}
fill(100);textFont(regular); textSize(timernumsize);
text(times[i][1], currx + timeswidth - textWidth(times[i][1])-10, curry + 22);
}
// draw top bar
if(times.length < grid.length){fill(orange[0],orange[1],orange[2]);}
else{fill(navy[0],navy[1],navy[2])}
rect(0,0,width,topheight);
stroke(255); fill(255);
strokeWeight(4);
const arrowmarg= 20, caplength = 13;
line(arrowmarg,topheight/2,45,topheight/2); line(arrowmarg,topheight/2,arrowmarg+caplength,topheight/2 - caplength); line(arrowmarg,topheight/2,arrowmarg+caplength,topheight/2 + caplength);
strokeWeight(2);
const lineX=78, lineY=28, linelength=178;
line(lineX,lineY,lineX+linelength,lineY);
line(lineX+linelength/2,lineY,lineX+linelength/2,lineY-15);
circle(lineX,lineY,8); circle(lineX+linelength/2,lineY,8); circle(lineX+linelength,lineY,8);
triangle(lineX+linelength/2,lineY-15,lineX+linelength/2,lineY-11,lineX+linelength/2+4,lineY-13)
noStroke();
const textY = 48;
textFont(regular); textSize(12);
text("incoming",lineX-5, textY); text("passed",lineX+linelength-textWidth("passed")+5,textY);
textFont(bold); textSize(18);
text(grid.length-times.length + " / " + times.length,lineX+linelength/2-textWidth(grid.length-times.length + " /"),textY);
if(press=="grid"){fill(lgrey);} else{fill(grey3);}
rect(width-changemodebutwidth,0,changemodebutwidth,topheight);
fill(navy[0],navy[1],navy[2]);
let textheight=16;
textFont(regular); textSize(14);
text("Change to",width-changemodebutwidth+13,5+textheight);
textFont(bold);
if(mode && press!="grid" || mode==false&&press=="grid"){text("Grid",width-changemodebutwidth+30,8+textheight*2);} else{text("Chat",width-changemodebutwidth+29,8+textheight*2);}
text("Mode",width-changemodebutwidth+26,8+textheight*3);
//draw popup
if(popupmessage){
if(fadein){
popupopac += 50;
if(popupopac > 255){
fadein = false;
}
}
textFont(regular); textSize(popuptextsize);
fill(navy[0],navy[1],navy[2],popupopac);
rect(popupX,chatY-buttonmarg-popupheight, textWidth(popupmessage) + popupheight,popupheight,popupheight/2);
fill(255);
text(popupmessage,popupX+popupheight/2,chatY-buttonmarg-12);
if(fadeout){
popupopac -= 15;
popupXint += 0.2;
popupX -= popupXint;
if(popupopac<0){
popupmessage = false;
fadeout = false;
}
}
}
}
function mousePressed(){
// uncomment this line for chrome on android:
if (event.type == 'touchstart'){
// rect(2*buttonmarg + (i%3) * gridbutwidth - buttonmarg/2, topheight + Math.trunc(i/3) * gridbutheight + buttonmarg, gridbutwidth - buttonmarg, gridbutheight - buttonmarg,15);
// GRID FUNCTION
if(mode == false){
for(i=0;i<grid.length;i++){
let xx = 2*buttonmarg + (i%3) * gridbutwidth - buttonmarg/2, yy = topheight + Math.trunc(i/3) * gridbutheight + buttonmarg;
if(mouseX>xx && mouseX<xx + gridbutwidth && mouseY>yy && mouseY<yy + gridbutheight && grid[i][1]){
press = i;
if(selection==-1){
times.push([grid[i][0], hour()+":"+minute()+":"+second()+"."+Math.round(millis()/100)%10,false])
}
else{
if(times[selection][0]!=""){
times[selection].push(times[selection][0]);
}
times[selection][0] = grid[i][0];
}
grid[i][1] = false;
selection = -1;
}
}
}
// NUMPAD FUNCTION
if(mode){
for(i=0;i<numbers.length;i++){
if(mouseX>numbers[i][0] && mouseX<numbers[i][0] + butwidth && mouseY>numbers[i][1] && mouseY<numbers[i][1] + butheight && typing.length < 3){
press = i;
if(selection==-1){
typing = typing.concat(i);
}
else if(times[selection][0].length < 3){
times[selection][0] = times[selection][0].concat(i);
if(times[selection][0].length>2){
//check for duplicates
for(j=times.length-1;j>=0;j--){
if(times[j][0]==times[selection][0] && j != selection){
times[j][2] = duplicatemessage;
times[selection][2] = duplicatemessage;
showPopup(duplicatemessage);
}
}
// check if registered
let unregistered = true;
for(i=0;i<grid.length;i++){
if(times[selection][0] == grid[i][0]){
unregistered = false
}
}
if(unregistered){
times[selection][2] = unregmessage;
showPopup(unregmessage);
}
selection = -1;
}
}
else{
//check if no longer duplicate
checkNoDuplicate();
//remove unregistered
if(times[selection][2] == unregmessage){
times[selection][2] = false;
}
times[selection].push(times[selection][0]);
times[selection][0] = "".concat(i);
}
}
}
}
// BACKSPACE
if(mouseX > butx3 && mouseX < butx3 + butwidth && mouseY > buty4 && mouseY < buty4 + butheight && mode){
press = "<-";
if(selection==-1){
if(typing.length > 0){
typing = typing.substring(0, typing.length - 1);
}
else if(queue.length > 0){
typing = queue.pop();
}
}
else{
//check if no longer duplicate
checkNoDuplicate();
//remove unregistered
if(times[selection][2] == unregmessage){
times[selection][2] = false;
}
times[selection][0] = times[selection][0].substring(0,times[selection][0].length - 1);
}
}
// CLEAR
if(mouseX > width-buttonmarg-recordwidth && mouseX < width-buttonmarg-recordwidth/2 && mouseY > swapY && mouseY < swapY+swapheight){
press = "clear";
if(selection==-1){
typing = "";
queue = [];
}
else if(times[selection][0] != ""){
//check if no longer duplicate
checkNoDuplicate();
//remove unregistered
if(times[selection][2] == unregmessage){
times[selection][2] = false;
}
times[selection].push(times[selection][0]);
times[selection][0] = "";
selection = -1;
}
}
// SWAP
if(mouseX > width-buttonmarg-recordwidth/2 && mouseX < width-buttonmarg && mouseY > swapY && mouseY < swapY+swapheight){
press = "swap";
if(selection>-1){
if(swapping){
swapping = false;
}
else{
swapping = true;
}
}
}
// MARK
if(mouseX > butx1 && mouseX < butx2 && mouseY > buty4 && mouseY < buty4 + butheight && mode){
press = "mark";
if(selection!=-1){
if(times[selection][2] == false){
times[selection][2] = "marked by user";
}
else if(times[selection][2] == "marked by user"){
times[selection][2] = false;
}
selection = -1;
}
}
// SWITCH MODE
if(mouseX > width-changemodebutwidth && mouseX < width && mouseY > 0 && mouseY < topheight){
press = "grid";
if(mode){
mode = false;
typing = "";
}
else{
mode=true;
}
}
if(mouseX > width-buttonmarg-recordwidth && mouseX < width-buttonmarg){
// RECORD BUTTON
if(mouseY > recordY && mouseY < height-buttonmarg){
press = "record";
if(queue.length > 0){
times.push([queue[0], hour()+":"+minute()+":"+second()+"."+Math.round(millis()/100)%10,false]);
queue.shift();
}
else{
times.push([typing, hour()+":"+minute()+":"+second()+"."+Math.round(millis()/100)%10,false]);
typing = "";
}
// check for duplicates
for(i=times.length-2;i>=0;i--){
if(times[i][0]==times[times.length-1][0] && times[i][0]!=""){
times[i][2] = duplicatemessage;
times[times.length-1][2] = duplicatemessage;
showPopup(duplicatemessage);
}
}
// check if registered
let unregistered = true;
for(i=0;i<grid.length;i++){
if(times[times.length-1][0] == grid[i][0]){
unregistered = false
}
}
if(unregistered && times[times.length-1][0] != ""){
times[times.length-1][2] = unregmessage;
showPopup(unregmessage);
}
}
// PLUS BUTTON
if(mouseY > plusY && mouseY < plusY + plusheight && mode){
press = "+";
if(selection == -1){
queue.push(typing);
typing = "";
}
else{
times.splice(selection+1, 0, ["",times[selection][1]]);
selection++;
}
}
}
// SELECT TIME
if(mouseX > timesX && mouseX < timesX + timeswidth){
let buffer = 0;
for(i=times.length-1;i>-1;i--){
let curry = timesY - timesmarg * (times.length-i) - buffer*bufferheight;
for(j=3;j<times[i].length;j++){
buffer++;
}
if(mouseY>curry - (times[i].length - 2) * timesheight*0.5 && mouseY<curry + timesheight && mouseY>topheight){
// SWAPPING ACTUAL FUNCTION
if(swapping){
showPopup("swapped "+times[selection][0]+" with "+times[i][0]+".")
if(times[selection][2]==duplicatemessage && times[i][2]!=duplicatemessage){
times[i][2]=duplicatemessage;
times[selection][2] = false;
}
else if(times[i][2]==duplicatemessage && times[selection][2]!=duplicatemessage){
times[selection][2]=duplicatemessage;
times[i][2] = false;
}
if(times[selection][2]==unregmessage && times[i][2]!=unregmessage){
times[i][2]=unregmessage;
times[selection][2] = false;
}
else if(times[selection][2]!=unregmessage && times[i][2]==unregmessage){
times[selection][2]=unregmessage;
times[i][2] = false;
}
if(times[selection][0]!=""){
times[selection].push(times[selection][0]);
}
if(times[i][0]!=""){
times[i].push(times[i][0]);
}
let bibswap = times[selection][0];
times[selection][0] = times[i][0];
times[i][0] = bibswap;
selection = -1;
swapping = false;
}
else{
selection = i;
}
}
}
}
// DESELECT TIME
if(mouseX > chatmarg && mouseX < width - chatmarg && mouseY > chatY && mouseY < chatY+chatheight && selection!=-1 || mouseX < timesX && mouseY < chatY && selection!=-1){
typing = "";
selection = -1;
swapping = false;
}
// uncomment this bracket for chrome on android:
}
}
function showPopup(msgtxt){
clearTimeout(fadeouttimer);
popupmessage = msgtxt;
popupopac = 0;
popupX = buttonmarg;
popupXint = 0;
fadein = true;
fadeout = false;
fadeouttimer = setTimeout(function(){ fadeout=true; }, 2000);
}
function checkNoDuplicate(){
if(times[selection][2] == duplicatemessage){
times[selection][2] = false;
let duplicatelist = [];
for(j=times.length-1;j>=0;j--){
if(times[j][0]==times[selection][0] && j != selection){
duplicatelist.push(j);
}
}
if(duplicatelist.length==1){
times[duplicatelist[0]][2] = false;
}
}
}
function mouseReleased(){
press = -1;
}
function switchMode(){
if(mode){mode = false;}
else{mode=true;}
}
function exportTimes(){
timetable = new p5.Table();
timetable.addColumn('bib');
timetable.addColumn('time');
for(i=0;i<times.length;i++){
let newRow = timetable.addRow();
newRow.setString('bib', times[i][0]);
newRow.setString('time', times[i][1]);
}
saveTable(timetable, 'results.csv');
}