diff --git a/README.md b/README.md new file mode 100644 index 0000000..8129a3f --- /dev/null +++ b/README.md @@ -0,0 +1,166 @@ +# p5.joystick.js + +![p5.joystick Thumbnail](thumbnail.jpg) + +## Overview + +The **p5.joystick** library aims to facilitate integration with physical Joysticks. + +It also has an easy calibration system, allowing you to configure different types of joysticks with the standard button format. + +This library uses the [gamepad API](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API). + +Through them it is possible to read directly the status of each joystick. This library also make possible an event based approach. + + +## Installation + +It is necessary to insert p5.joystick.js in your html: +```js + + + +``` + +### Usage + +```js +var joystick; + +function setup() { + createCanvas(window.innerWidth, window.innerHeight); + + joystick = createJoystick(); + if(!joystick.calibrated()) + joystick.calibrate(true); + joystick.onButtonPressed(onJoystick); + joystick.onButtonReleased(onJoystick); + joystick.onAxesPressed(onJoystick); + joystick.onAxesReleased(onJoystick); +} + +function draw(){ + background(100); + joystick.draw(width/2, height/2); +} + +function onJoystick(e) { + console.log("onJoystick", e); +} +``` + +## Calibrating + +Calibration associates each button on the virtual joystick with the physical joystick. +You can make different combinations as you wish. + +The calibration is automatically saved and loaded into a cookie. + +![p5.joystick calibration process](calibration.gif) + + + +## Methods + +### createJoystick([debug]) + +Initializes built-in listeners to integrate with gamepad API. + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| debug | Boolean | No | If `true`, adds html elements to monitor all connected joysticks and the state of each button. | + +### draw(x, y, [width], [height]) + +Draws a joystick on the screen in the desired position and size. + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| x | Number | Yes | horizontal position on screen. | +| y | Number | Yes | vertical position on the screen. | +| width | Number | No | Width of the joystick. Standard value: 440. | +| height | Number | No | Height of the joystick. Standard value: 200. | + +**Notice:** The drawing axis is centered, so the joystick is drawn around the x and y coordinates. + +### calibrate(enable) + +Enable/Disable calibration mode. + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| enable | Boolean | Yes | Enable/Disable calibration mode. | + +### getButtonPressedByName(name) + +Returns a boolean button state: `true` or `false`. + + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| gamepadIndex | Integer | Yes | Joystick index. | +| name | String | Yes | Button name: sholderLeft, sholderRight, axesUp, axesDown, axesLeft, axesRight, buttonBlue, buttonYellow, buttonRed, buttonGreen, start or select. | + +**Notice:** Only works when calibrated. + +### getButtonPressedByIndex(gamepadIndex, buttonIndex) + +Returns a boolean button state: `true` or `false`. + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| gamepadIndex | Integer | Yes | Joystick index. | +| buttonIndex | Integer | Yes | Button index. | + +### getAxesValueByIndex(gamepadIndex, axesIndex) + +Returns a integer axis state: `-1`, `0`, `1`. + +| Parameter | Type | Required | Description | +| -------- | -------- | -------- | -------- | +| gamepadIndex | Integer | Yes | Joystick index. | +| axesIndex | Integer | Yes | Axes index. | + +## Events + +Events return an object with the following properties: + +| Property | Type | Description | +| -------- | -------- | -------- | +| gamepadName | String | Joystick name. | +| gamepadIndex | Integer | Joystick index. | +| index | Integer | Button index. | +| name | String | Button name: sholderLeft, sholderRight, axesUp, axesDown, axesLeft, axesRight, buttonBlue, buttonYellow, buttonRed, buttonGreen, start or select. Return empty "" if the joystick is not calibrated. | +| pressed | Boolean | Button is pressed or not. | +| type | String | Button type: axes or button. | +| value | Integer | "axes" can return directions -1, 0 or 1, and buttons can return 0 or 1. | + +There are 4 types of events: + +### onButtonPressed + +Triggered when a button is pressed. +```js +joystick.onButtonPressed(function(eventObj){}); +``` + +### onButtonReleased + +Triggered when a button is released. +```js +joystick.onButtonReleased(function(eventObj){}); +``` + +### onAxesPressed + +Triggered when an axes is pressed. +```js +joystick.onAxesPressed(function(eventObj){}); +``` + +### onAxesReleased + +Triggered when an axes is released. +```js +joystick.onAxesReleased(function(eventObj){}); +``` \ No newline at end of file diff --git a/calibration.gif b/calibration.gif new file mode 100644 index 0000000..f5e249c Binary files /dev/null and b/calibration.gif differ diff --git a/p5.joystick.js b/p5.joystick.js new file mode 100644 index 0000000..2a12906 --- /dev/null +++ b/p5.joystick.js @@ -0,0 +1,423 @@ +//Code by Vamoss +p5.prototype.createJoystick = function(debug) { + var _this = { + name: "p5Joystick" + }; + const DEBUG_CONTROL = debug; + const DEFAULT_WIDTH = 440; + const DEFAULT_HEIGHT = 200; + var calibrating = false; + + var haveEvents = 'ongamepadconnected' in window; + var controllers = {}; + + /* + Event listeners + */ + var buttonPressedHandlers = []; + var buttonReleasedHandlers = []; + var axesPressedHandlers = []; + var axesReleasedHandlers = []; + _this.onButtonPressed = function(func) { + buttonPressedHandlers.push(func); + } + _this.onButtonReleased = function(func) { + buttonReleasedHandlers.push(func); + } + _this.onAxesPressed = function(func) { + axesPressedHandlers.push(func); + } + _this.onAxesReleased = function(func) { + axesReleasedHandlers.push(func); + } + + /* + Coordinates + */ + var coordinates = {}; + var prevX, prevY, prevW, prevH; + function calculateCoordinates(x, y, w, h) { + if (x == prevX && y == prevY && w == prevW && h == prevH) + return; + + prevX = x; + prevY = y; + prevW = w; + prevH = h; + + //sholder; + const sW = 60 * h / DEFAULT_HEIGHT; //sholder width + + //axes + const aW = 40 * h / DEFAULT_HEIGHT; //axes/button width + const aS = 0.6; //space + coordinates = { + sholderLeft: {x: x - w - sW / 2, y: y - h / 2 - sW / 4, w: sW, h: sW / 3}, + sholderRight: {x: x + w - sW / 2, y: y - h / 2 - sW / 4, w: sW, h: sW / 3}, + axesUp: {x: x - w - aW / 2, y: y - aW / 2 * aS - aW, w: aW, h: aW}, + axesDown: {x: x - w - aW / 2, y: y + aW / 2 * aS, w: aW, h: aW}, + axesLeft: {x: x - w - aW / 2 * aS - aW, y: y - aW / 2, w: aW, h: aW}, + axesRight: {x: x - w + aW / 2 * aS, y: y - aW / 2, w: aW, h: aW}, + buttonBlue: {x: x + w - aW * 0.9 - aW / 2, y: y - aW / 2, w: aW, h: aW}, + buttonYellow: {x: x + w - aW / 2, y: y - aW * 0.9 - aW / 2, w: aW, h: aW}, + buttonRed: {x: x + w + aW * 0.9 - aW / 2, y: y - aW / 2, w: aW, h: aW}, + buttonGreen: {x: x + w - aW / 2, y: y + aW * 0.9 - aW / 2, w: aW, h: aW}, + start: {x: x - aW * 1.1, y: y, w: aW, h: aW / 2}, + select: {x: x + aW * .1, y: y, w: aW, h: aW / 2} + } + loadCalibration(); + } + + /* + Calibration + */ + var calibration = {}; + loadCalibration = function(){ + var savedCalibration = localStorage.getItem('p5Joystick'); + if(savedCalibration){ + calibration = JSON.parse(savedCalibration); + for (const [gamepadId, joystick] of Object.entries(calibration)){ + for (const [name, inputButton] of Object.entries(joystick)){ + if(coordinates[inputButton]) + coordinates[inputButton].button = name; + } + } + } + } + loadCalibration(); + + saveCalibration = function(name, gamepadId, inputButton) { + coordinates[name].button = inputButton; + if (!(gamepadId in calibration)) + calibration[gamepadId] = {}; + calibration[gamepadId][inputButton] = name; + localStorage.setItem('p5Joystick', JSON.stringify(calibration)); + } + + getCalibrationName = function(gamepadId, inputButton) { + if (gamepadId in calibration) + if(inputButton in calibration[gamepadId]) + return calibration[gamepadId][inputButton]; + return ""; + } + + /* + Public Methods + */ + _this.calibrated = function(){ + return Object.keys(calibration).length > 0; + } + + _this.calibrate = function(enable){ + calibrating = enable; + } + + _this.getButtonPressedByName = function(name){ + if(coordinates[name] && coordinates[name].pressed){ + return coordinates[name].pressed; + } + return false; + } + + _this.getButtonPressedByIndex = function(gamepadIndex, buttonIndex){ + if(controllers[gamepadIndex] && controllers[gamepadIndex].gamepad.buttons[buttonIndex]) + return controllers[gamepadIndex].gamepad.buttons[buttonIndex].pressed; + return false; + } + + _this.getAxesValueByIndex = function(gamepadIndex, axesIndex){ + if(controllers[gamepadIndex] && controllers[gamepadIndex].gamepad.axes[axesIndex]) + return controllers[gamepadIndex].gamepad.axes[axesIndex]; + return 0; + } + + _this.draw = function(x, y, w, h) { + if(!x || !y) console.error("p5joystick draw() function: parameters x and y are required") + + w = w || DEFAULT_WIDTH; + h = h || DEFAULT_HEIGHT; + + w -= h; + w /= 2; + + calculateCoordinates(x, y, w, h); + + with(coordinates) { + noStroke(); + //sholders + fill(sholderLeft.pressed ? 200 : 127); + rect(sholderLeft.x, sholderLeft.y, sholderLeft.w, sholderLeft.h, sholderLeft.w / 4, sholderLeft.w / 4, 0, 0); + fill(sholderRight.pressed ? 200 : 127); + rect(sholderRight.x, sholderRight.y, sholderRight.w, sholderRight.h, sholderRight.w / 4, sholderRight.w / 4, 0, 0); + + //structure + fill(255); + circle(x - w, y, h); + circle(x + w, y, h); + rect(x - w, y - h / 2, w * 2, h * 0.75); + + //axes + fill(axesUp.pressed ? 200 : 127); + rect(axesUp.x, axesUp.y, axesUp.w, axesUp.h, axesUp.w / 4, axesUp.w / 4, axesUp.w / 2, axesUp.w / 2); + fill(axesDown.pressed ? 200 : 127); + rect(axesDown.x, axesDown.y, axesDown.w, axesDown.h, axesDown.w / 2, axesDown.w / 2, axesDown.w / 4, axesDown.w / 4); + fill(axesLeft.pressed ? 200 : 127); + rect(axesLeft.x, axesLeft.y, axesLeft.w, axesLeft.h, axesLeft.w / 4, axesLeft.w / 2, axesLeft.w / 2, axesLeft.w / 4); + fill(axesRight.pressed ? 200 : 127); + rect(axesRight.x, axesRight.y, axesRight.w, axesRight.h, axesRight.w / 2, axesRight.w / 4, axesRight.w / 4, axesRight.w / 2); + + //buttons + fill(buttonYellow.pressed ? color(255, 255, 127) : color(255, 255, 0)); + circle(buttonYellow.x + buttonYellow.w / 2, buttonYellow.y + buttonYellow.h / 2, buttonYellow.w); + + fill(buttonBlue.pressed ? color(127, 127, 255) : color(0, 0, 255)); + circle(buttonBlue.x + buttonBlue.w / 2, buttonBlue.y + buttonBlue.h / 2, buttonBlue.w); + + fill(buttonRed.pressed ? color(255, 127, 127) : color(255, 0, 0)); + circle(buttonRed.x + buttonRed.w / 2, buttonRed.y + buttonRed.h / 2, buttonRed.w); + + fill(buttonGreen.pressed ? color(127, 255, 127) : color(0, 255, 0)); + circle(buttonGreen.x + buttonGreen.w / 2, buttonGreen.y + buttonGreen.h / 2, buttonGreen.w); + + fill(start.pressed ? 200 : 127); + rect(start.x, start.y, start.w, start.h, start.w / 4, start.w / 4, start.w / 4, start.w / 4); + + fill(select.pressed ? 200 : 127); + rect(select.x, select.y, select.w, select.h, select.w / 4, select.w / 4, select.w / 4, select.w / 4); + } + + if (calibrating) { + for (const [name, button] of Object.entries(coordinates)) { + if (mouseIsPressed) + coordinates[name].selected = (mouseX > button.x && mouseX < button.x + button.w && mouseY > button.y && mouseY < button.y + button.h); + + stroke(0); + if (button.selected) + fill(0, 255, 255, 100); + else + fill(0, 100); + rect(button.x, button.y, button.w, button.h); + + if (button.button) { + fill(0); + noStroke(); + text(button.button, button.x + button.w / 2 - 10, button.y + button.h / 2 + 2); + } + } + } + } + + /* + Gamepad events + */ + function connecthandler(e) { + addgamepad(e.gamepad); + } + + function addgamepad(gamepad) { + controllers[gamepad.index] = { + gamepad: gamepad, + buttonPrevState: [], + axesPrevState: [], + }; + + for (var i = 0; i < gamepad.buttons.length; i++) { + controllers[gamepad.index].buttonPrevState[i] = { + value: gamepad.buttons[i].value, + pressed: gamepad.buttons[i].pressed + } + } + + for (var i = 0; i < gamepad.axes.length; i++) { + controllers[gamepad.index].axesPrevState[i] = round(gamepad.axes[i]); + } + + if (DEBUG_CONTROL) { + var d = document.createElement("div"); + d.setAttribute("id", "controller" + gamepad.index); + d.style.position = "absolute"; + d.style.top = "0px"; + d.style.left = (gamepad.index * 190) + "px"; + d.style.width = 170 + "px"; + + var t = document.createElement("h3"); + t.appendChild(document.createTextNode("gamepad: " + gamepad.id)); + d.appendChild(t); + + var b = document.createElement("div"); + b.className = "buttons"; + b.style.margin = "10px 0px"; + for (var i = 0; i < gamepad.buttons.length; i++) { + var e = document.createElement("span"); + e.className = "button"; + e.style.float = "left"; + e.style.width = "20px"; + e.style.textAlign = "center"; + e.style.border = "1px solid blue"; + e.style.margin = "1px"; + e.innerHTML = i; + b.appendChild(e); + } + + d.appendChild(b); + + var a = document.createElement("div"); + a.className = "axes"; + + for (var i = 0; i < gamepad.axes.length; i++) { + var p = document.createElement("progress"); + p.className = "axis"; + p.setAttribute("max", "2"); + p.setAttribute("value", "1"); + p.innerHTML = i; + a.appendChild(p); + } + + d.appendChild(a); + + document.body.appendChild(d); + } + requestAnimationFrame(updateStatus); + } + + function disconnecthandler(e) { + removegamepad(e.gamepad); + } + + function removegamepad(gamepad) { + if (DEBUG_CONTROL) { + var d = document.getElementById("controller" + gamepad.index); + document.body.removeChild(d); + } + delete controllers[gamepad.index]; + } + + function updateStatus() { + if (!haveEvents) { + scangamepads(); + } + + var i = 0; + var j; + + for (j in controllers) { + var controller = controllers[j]; + var gamepad = controller.gamepad; + + var buttonPrevState = controller.buttonPrevState; + for (i = 0; i < gamepad.buttons.length; i++) { + var button = gamepad.buttons[i]; + if (buttonPrevState[i].value != button.value) { + buttonPrevState[i].value = button.value; + + var eventObj = { + gamepadName: gamepad.id, + gamepadIndex: gamepad.index, + value: button.value, + index: i, + pressed: button.pressed, + type: "button", + name: getCalibrationName(gamepad.id, "b" + i) + } + + if(eventObj.name != "" && coordinates[eventObj.name]) + coordinates[eventObj.name].pressed = eventObj.pressed; + + if (button.pressed) { + if (calibrating) + for (const [name, button] of Object.entries(coordinates)) + if (button.selected && eventObj.pressed) + saveCalibration(name, gamepad.id, "b" + i); + buttonPressedHandlers.forEach(func => func(eventObj)); + } else { + buttonReleasedHandlers.forEach(func => func(eventObj)); + } + } + } + + var axesPrevState = controller.axesPrevState; + for (i = 0; i < gamepad.axes.length; i++) { + var axesValue = round(gamepad.axes[i]); + if (axesPrevState[i] != axesValue) { + var eventObj = { + gamepadName: gamepad.id, + gamepadIndex: gamepad.index, + value: axesValue, + index: i, + pressed: abs(axesValue) > 0.1, + type: "axes" + } + if (eventObj.pressed) + eventObj.name = getCalibrationName(gamepad.id, "a" + i + ":" + axesValue); + else + eventObj.name = getCalibrationName(gamepad.id, "a" + i + ":" + axesPrevState[i]); + + if(eventObj.name != "" && coordinates[eventObj.name]) + coordinates[eventObj.name].pressed = eventObj.pressed; + + if (eventObj.pressed) { + if (calibrating) { + for (const [name, button] of Object.entries(coordinates)) { + if (button.selected) { + saveCalibration(name, gamepad.id, "a" + i + ":" + axesValue); + } + } + } + + axesPressedHandlers.forEach(func => func(eventObj)); + } else + axesReleasedHandlers.forEach(func => func(eventObj)); + axesPrevState[i] = axesValue; + } + } + + if (DEBUG_CONTROL) { + var d = document.getElementById("controller" + j); + var buttonsEl = d.getElementsByClassName("button"); + for (i = 0; i < gamepad.buttons.length; i++) { + var button = gamepad.buttons[i]; + var pct = Math.round(button.value * 100) + "%"; + buttonsEl[i].style.backgroundSize = pct + " " + pct; + if (button.pressed) { + buttonsEl[i].style.backgroundColor = "blue"; + buttonsEl[i].style.color = "white"; + } else { + buttonsEl[i].style.backgroundColor = ""; + buttonsEl[i].style.color = ""; + } + } + + var axes = d.getElementsByClassName("axis"); + for (i = 0; i < gamepad.axes.length; i++) { + var a = axes[i]; + a.innerHTML = i + ": " + gamepad.axes[i].toFixed(4); + a.setAttribute("value", gamepad.axes[i] + 1); + } + } + } + + requestAnimationFrame(updateStatus); + } + + function scangamepads() { + var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []); + for (var i = 0; i < gamepads.length; i++) { + if (gamepads[i]) { + if (gamepads[i].index in controllers) { + controllers[gamepads[i].index].gamepad = gamepads[i]; + } else { + addgamepad(gamepads[i]); + } + } + } + } + + window.addEventListener("gamepadconnected", connecthandler); + window.addEventListener("gamepaddisconnected", disconnecthandler); + + if (!haveEvents) { + setInterval(scangamepads, 500); + } + + return _this; +} \ No newline at end of file diff --git a/p5.joystick.min.js b/p5.joystick.min.js new file mode 100644 index 0000000..fd708b6 --- /dev/null +++ b/p5.joystick.min.js @@ -0,0 +1 @@ +p5.prototype.createJoystick=function(debug){var _this={name:"p5Joystick"};const DEBUG_CONTROL=debug,DEFAULT_WIDTH=440,DEFAULT_HEIGHT=200;var calibrating=!1,haveEvents="ongamepadconnected"in window,controllers={},buttonPressedHandlers=[],buttonReleasedHandlers=[],axesPressedHandlers=[],axesReleasedHandlers=[];_this.onButtonPressed=function(e){buttonPressedHandlers.push(e)},_this.onButtonReleased=function(e){buttonReleasedHandlers.push(e)},_this.onAxesPressed=function(e){axesPressedHandlers.push(e)},_this.onAxesReleased=function(e){axesReleasedHandlers.push(e)};var coordinates={},prevX,prevY,prevW,prevH;function calculateCoordinates(e,t,a,n){if(e==prevX&&t==prevY&&a==prevW&&n==prevH)return;prevX=e,prevY=t,prevW=a,prevH=n;const s=60*n/DEFAULT_HEIGHT,o=40*n/DEFAULT_HEIGHT;coordinates={sholderLeft:{x:e-a-s/2,y:t-n/2-s/4,w:s,h:s/3},sholderRight:{x:e+a-s/2,y:t-n/2-s/4,w:s,h:s/3},axesUp:{x:e-a-o/2,y:t-o/2*.6-o,w:o,h:o},axesDown:{x:e-a-o/2,y:t+o/2*.6,w:o,h:o},axesLeft:{x:e-a-o/2*.6-o,y:t-o/2,w:o,h:o},axesRight:{x:e-a+o/2*.6,y:t-o/2,w:o,h:o},buttonBlue:{x:e+a-.9*o-o/2,y:t-o/2,w:o,h:o},buttonYellow:{x:e+a-o/2,y:t-.9*o-o/2,w:o,h:o},buttonRed:{x:e+a+.9*o-o/2,y:t-o/2,w:o,h:o},buttonGreen:{x:e+a-o/2,y:t+.9*o-o/2,w:o,h:o},start:{x:e-1.1*o,y:t,w:o,h:o/2},select:{x:e+.1*o,y:t,w:o,h:o/2}},loadCalibration()}var calibration={};function connecthandler(e){addgamepad(e.gamepad)}function addgamepad(e){controllers[e.index]={gamepad:e,buttonPrevState:[],axesPrevState:[]};for(var t=0;te(i))}else buttonReleasedHandlers.forEach(e=>e(i))}var r=a.axesPrevState;for(t=0;t.1,type:"axes"}).pressed?i.name=getCalibrationName(n.id,"a"+t+":"+l):i.name=getCalibrationName(n.id,"a"+t+":"+r[t]),""!=i.name&&coordinates[i.name]&&(coordinates[i.name].pressed=i.pressed),i.pressed){if(calibrating)for(const[e,a]of Object.entries(coordinates))a.selected&&saveCalibration(e,n.id,"a"+t+":"+l);axesPressedHandlers.forEach(e=>e(i))}else axesReleasedHandlers.forEach(e=>e(i));r[t]=l}}if(DEBUG_CONTROL){var d=document.getElementById("controller"+e),c=d.getElementsByClassName("button");for(t=0;t0},_this.calibrate=function(e){calibrating=e},_this.getButtonPressedByName=function(e){return!(!coordinates[e]||!coordinates[e].pressed)&&coordinates[e].pressed},_this.getButtonPressedByIndex=function(e,t){return!(!controllers[e]||!controllers[e].gamepad.buttons[t])&&controllers[e].gamepad.buttons[t].pressed},_this.getAxesValueByIndex=function(e,t){return controllers[e]&&controllers[e].gamepad.axes[t]?controllers[e].gamepad.axes[t]:0},_this.draw=function(x,y,w,h){with(x&&y||console.error("p5joystick draw() function: parameters x and y are required"),w=w||DEFAULT_WIDTH,h=h||DEFAULT_HEIGHT,w-=h,w/=2,calculateCoordinates(x,y,w,h),coordinates)noStroke(),fill(sholderLeft.pressed?200:127),rect(sholderLeft.x,sholderLeft.y,sholderLeft.w,sholderLeft.h,sholderLeft.w/4,sholderLeft.w/4,0,0),fill(sholderRight.pressed?200:127),rect(sholderRight.x,sholderRight.y,sholderRight.w,sholderRight.h,sholderRight.w/4,sholderRight.w/4,0,0),fill(255),circle(x-w,y,h),circle(x+w,y,h),rect(x-w,y-h/2,2*w,.75*h),fill(axesUp.pressed?200:127),rect(axesUp.x,axesUp.y,axesUp.w,axesUp.h,axesUp.w/4,axesUp.w/4,axesUp.w/2,axesUp.w/2),fill(axesDown.pressed?200:127),rect(axesDown.x,axesDown.y,axesDown.w,axesDown.h,axesDown.w/2,axesDown.w/2,axesDown.w/4,axesDown.w/4),fill(axesLeft.pressed?200:127),rect(axesLeft.x,axesLeft.y,axesLeft.w,axesLeft.h,axesLeft.w/4,axesLeft.w/2,axesLeft.w/2,axesLeft.w/4),fill(axesRight.pressed?200:127),rect(axesRight.x,axesRight.y,axesRight.w,axesRight.h,axesRight.w/2,axesRight.w/4,axesRight.w/4,axesRight.w/2),fill(buttonYellow.pressed?color(255,255,127):color(255,255,0)),circle(buttonYellow.x+buttonYellow.w/2,buttonYellow.y+buttonYellow.h/2,buttonYellow.w),fill(buttonBlue.pressed?color(127,127,255):color(0,0,255)),circle(buttonBlue.x+buttonBlue.w/2,buttonBlue.y+buttonBlue.h/2,buttonBlue.w),fill(buttonRed.pressed?color(255,127,127):color(255,0,0)),circle(buttonRed.x+buttonRed.w/2,buttonRed.y+buttonRed.h/2,buttonRed.w),fill(buttonGreen.pressed?color(127,255,127):color(0,255,0)),circle(buttonGreen.x+buttonGreen.w/2,buttonGreen.y+buttonGreen.h/2,buttonGreen.w),fill(start.pressed?200:127),rect(start.x,start.y,start.w,start.h,start.w/4,start.w/4,start.w/4,start.w/4),fill(select.pressed?200:127),rect(select.x,select.y,select.w,select.h,select.w/4,select.w/4,select.w/4,select.w/4);if(calibrating)for(const[name,button]of Object.entries(coordinates))mouseIsPressed&&(coordinates[name].selected=mouseX>button.x&&mouseXbutton.y&&mouseY