Added joystick controls
This commit is contained in:
136
script.js
136
script.js
@ -4,6 +4,76 @@
|
|||||||
|
|
||||||
import GrahamScan from "./vendor/lucio/graham-scan.mjs";
|
import GrahamScan from "./vendor/lucio/graham-scan.mjs";
|
||||||
|
|
||||||
|
class Joystick {
|
||||||
|
constructor(ctx, x, y, radius) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.radius = radius;
|
||||||
|
this.nubX = x;
|
||||||
|
this.nubY = y;
|
||||||
|
this.active = false;
|
||||||
|
this.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (!this.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ctx.save();
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
||||||
|
this.ctx.fillStyle = '#5554';
|
||||||
|
this.ctx.fill();
|
||||||
|
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(this.nubX, this.nubY, this.radius / 3, 0, Math.PI * 2);
|
||||||
|
this.ctx.fillStyle = '#445bcaff';
|
||||||
|
this.ctx.fill();
|
||||||
|
|
||||||
|
this.ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkShouldActivate(x, y) {
|
||||||
|
if (!this.touchInRange(x, y))
|
||||||
|
return;
|
||||||
|
this.active = true;
|
||||||
|
this.processTouch(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate() {
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
touchInRange(x, y) {
|
||||||
|
const deltaX = x - this.x;
|
||||||
|
const deltaY = y - this.y;
|
||||||
|
const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
return dist <= this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
processTouch(x, y) {
|
||||||
|
if (this.touchInRange(x, y) && this.active) {
|
||||||
|
this.nubX = x;
|
||||||
|
this.nubY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getX() {
|
||||||
|
return (this.nubX - this.x) / this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
getY() {
|
||||||
|
return (this.y - this.nubY) / this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsVisible(visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2D vector class to make some of the math easier
|
// 2D vector class to make some of the math easier
|
||||||
class Vec2D {
|
class Vec2D {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
@ -269,6 +339,15 @@ const PresetConfigs = {
|
|||||||
* BEGIN DOM VARIABLES
|
* BEGIN DOM VARIABLES
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Get canvas
|
||||||
|
const canvas = document.getElementById('swerve-canvas');
|
||||||
|
|
||||||
|
// Get the canvas context as constant
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Get CSS variables for use in canvas
|
||||||
|
const rootStyles = getComputedStyle(document.documentElement);
|
||||||
|
|
||||||
// Get all control elements
|
// Get all control elements
|
||||||
const vxSlider = document.getElementById('vx-slider');
|
const vxSlider = document.getElementById('vx-slider');
|
||||||
const vySlider = document.getElementById('vy-slider');
|
const vySlider = document.getElementById('vy-slider');
|
||||||
@ -379,6 +458,8 @@ controlModeToggle.addEventListener('click', () => {
|
|||||||
vxOutput.textContent = '0';
|
vxOutput.textContent = '0';
|
||||||
vyOutput.textContent = '0';
|
vyOutput.textContent = '0';
|
||||||
omegaOutput.textContent = '0';
|
omegaOutput.textContent = '0';
|
||||||
|
leftJoystick.setIsVisible(true);
|
||||||
|
rightJoystick.setIsVisible(true);
|
||||||
} else {
|
} else {
|
||||||
// Switch to slider mode
|
// Switch to slider mode
|
||||||
sliderControls.style.display = 'block';
|
sliderControls.style.display = 'block';
|
||||||
@ -390,6 +471,8 @@ controlModeToggle.addEventListener('click', () => {
|
|||||||
manualInputVelX = 0;
|
manualInputVelX = 0;
|
||||||
manualInputVelY = 0;
|
manualInputVelY = 0;
|
||||||
manualInputOmega = 0;
|
manualInputOmega = 0;
|
||||||
|
leftJoystick.setIsVisible(false);
|
||||||
|
rightJoystick.setIsVisible(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -552,6 +635,30 @@ applyCustomBtn.addEventListener('click', () => {
|
|||||||
updateModuleDisplays(robot);
|
updateModuleDisplays(robot);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function convertTouchToCanvas(inCoords) {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = inCoords.x - rect.left - canvas.width / 2;
|
||||||
|
const y = inCoords.y - rect.top - canvas.height / 2;
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.addEventListener('mousedown', (event) => {
|
||||||
|
const canvasCoords = convertTouchToCanvas({ x: event.clientX, y: event.clientY });
|
||||||
|
leftJoystick.checkShouldActivate(canvasCoords.x, canvasCoords.y);
|
||||||
|
rightJoystick.checkShouldActivate(canvasCoords.x, canvasCoords.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener('mousemove', (event) => {
|
||||||
|
const canvasCoords = convertTouchToCanvas({ x: event.clientX, y: event.clientY });
|
||||||
|
leftJoystick.processTouch(canvasCoords.x, canvasCoords.y);
|
||||||
|
rightJoystick.processTouch(canvasCoords.x, canvasCoords.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener('mouseup', (event) => {
|
||||||
|
leftJoystick.deactivate();
|
||||||
|
rightJoystick.deactivate();
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* END LISTENER CODE
|
* END LISTENER CODE
|
||||||
* BEGIN DYNAMIC DOM FUNCTIONS
|
* BEGIN DYNAMIC DOM FUNCTIONS
|
||||||
@ -696,12 +803,8 @@ function updateModuleDisplays(robot) {
|
|||||||
* BEGIN ANIMATION CODE
|
* BEGIN ANIMATION CODE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Get the canvas and context as constants
|
const leftJoystick = new Joystick(ctx, -250, 250, 100);
|
||||||
const canvas = document.getElementById('swerve-canvas');
|
const rightJoystick = new Joystick(ctx, 250, 250, 100);
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Get CSS variables for use in canvas
|
|
||||||
const rootStyles = getComputedStyle(document.documentElement);
|
|
||||||
|
|
||||||
function drawGrid(ctx, sideLength, gridSquareSize, xOffset, yOffset) {
|
function drawGrid(ctx, sideLength, gridSquareSize, xOffset, yOffset) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
@ -806,8 +909,12 @@ function drawRobot(ctx, robot, heading) {
|
|||||||
ctx.restore(); // Restore to remove rotation
|
ctx.restore(); // Restore to remove rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize Variables
|
// Initialize Variables
|
||||||
|
// Joysticks
|
||||||
|
const supportsTouch = (navigator.maxTouchPoints > 0);
|
||||||
|
|
||||||
|
|
||||||
|
// General robot
|
||||||
const robotSize = 200;
|
const robotSize = 200;
|
||||||
const defaultModulePositions = PresetConfigs.fourWheelSquare(robotSize);
|
const defaultModulePositions = PresetConfigs.fourWheelSquare(robotSize);
|
||||||
const robot = new SwerveDrive(defaultModulePositions, "4-Wheel Square");
|
const robot = new SwerveDrive(defaultModulePositions, "4-Wheel Square");
|
||||||
@ -827,11 +934,20 @@ function animate() {
|
|||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
|
||||||
|
leftJoystick.draw();
|
||||||
|
rightJoystick.draw();
|
||||||
|
|
||||||
// Update speeds based on control mode
|
// Update speeds based on control mode
|
||||||
if (isManualInputMode) {
|
if (isManualInputMode) {
|
||||||
xSpeed = manualInputVelX;
|
const maxSpeed = parseFloat(keyboardMaxSpeed.value);
|
||||||
ySpeed = -manualInputVelY; // Negative because canvas Y axis is inverted
|
const maxRotation = parseFloat(keyboardMaxRotation.value);
|
||||||
turnSpeed = manualInputOmega;
|
// xSpeed = manualInputVelX;
|
||||||
|
// ySpeed = -manualInputVelY; // Negative because canvas Y axis is inverted
|
||||||
|
// turnSpeed = manualInputOmega;
|
||||||
|
|
||||||
|
xSpeed = leftJoystick.getX() * maxSpeed;
|
||||||
|
ySpeed = -leftJoystick.getY() * maxSpeed;
|
||||||
|
turnSpeed = rightJoystick.getX() * maxRotation;
|
||||||
} else {
|
} else {
|
||||||
xSpeed = parseFloat(vxSlider.value);
|
xSpeed = parseFloat(vxSlider.value);
|
||||||
ySpeed = -parseFloat(vySlider.value);
|
ySpeed = -parseFloat(vySlider.value);
|
||||||
|
|||||||
Reference in New Issue
Block a user