diff --git a/index.html b/index.html index 66b4fb0..2a86aed 100644 --- a/index.html +++ b/index.html @@ -199,24 +199,57 @@ if scale < 1: Translation & Rotation
- - - 0 +
-
- - - 0 +
+
+ + + 0 +
+ +
+ + + 0 +
+ +
+ + + 0 +
+ +
-
- - - 0 -
+
diff --git a/script.js b/script.js index 66772f5..9b021b7 100644 --- a/script.js +++ b/script.js @@ -288,6 +288,15 @@ const generateInputsBtn = document.getElementById('generate-inputs-btn'); const clearInputsBtn = document.getElementById('delete-inputs-btn'); const applyCustomBtn = document.getElementById('apply-custom-btn'); +// Get control mode elements +const controlModeToggle = document.getElementById('control-mode-toggle'); +const sliderControls = document.getElementById('slider-controls'); +const keyboardControls = document.getElementById('keyboard-controls'); +const keyboardMaxSpeed = document.getElementById('keyboard-max-speed'); +const keyboardMaxSpeedOutput = document.getElementById('keyboard-max-speed-value'); +const keyboardMaxRotation = document.getElementById('keyboard-max-rotation'); +const keyboardMaxRotationOutput = document.getElementById('keyboard-max-rotation-value'); + // Preset buttons const preset2WheelBtn = document.getElementById('preset-2wheel'); const preset3WheelBtn = document.getElementById('preset-3wheel'); @@ -301,6 +310,29 @@ const preset16OctBtn = document.getElementById('preset-16oct'); /* * END DOM VARIABLES +* BEGIN CONTROL MODE AND INPUT DEVICE VARIABLES +*/ + +// Control mode state +let isManualInputMode = false; // true = keyboard/gamepad mode, false = slider mode + +// Keyboard state tracking +const keyState = { + w: false, // Forward + a: false, // Left + s: false, // Backward + d: false, // Right + q: false, // Counter-clockwise + e: false // Clockwise +}; + +// Current manual input velocities (from keyboard or gamepad) +let manualInputVelX = 0; +let manualInputVelY = 0; +let manualInputOmega = 0; + +/* +* END CONTROL MODE AND INPUT DEVICE VARIABLES * BEGIN LISTENER CODE */ @@ -319,6 +351,93 @@ resetBtn.addEventListener('click', (e) => { omegaOutput.textContent = parseFloat(omegaSlider.value); }); +// Keyboard control sliders +keyboardMaxSpeed.addEventListener('input', (e) => { + keyboardMaxSpeedOutput.textContent = parseFloat(e.target.value); +}); +keyboardMaxSpeedOutput.textContent = parseFloat(keyboardMaxSpeed.value); + +keyboardMaxRotation.addEventListener('input', (e) => { + keyboardMaxRotationOutput.textContent = parseFloat(e.target.value); +}); +keyboardMaxRotationOutput.textContent = parseFloat(keyboardMaxRotation.value); + +// Control mode toggle +controlModeToggle.addEventListener('click', () => { + isManualInputMode = !isManualInputMode; + + if (isManualInputMode) { + // Switch to manual input mode (keyboard/gamepad) + sliderControls.style.display = 'none'; + keyboardControls.style.display = 'block'; + controlModeToggle.textContent = 'Switch to Slider Controls'; + + // Reset slider values when switching to manual input + vxSlider.value = 0; + vySlider.value = 0; + omegaSlider.value = 0; + vxOutput.textContent = '0'; + vyOutput.textContent = '0'; + omegaOutput.textContent = '0'; + } else { + // Switch to slider mode + sliderControls.style.display = 'block'; + keyboardControls.style.display = 'none'; + controlModeToggle.textContent = 'Switch to Keyboard Controls (WASD + QE)'; + + // Reset manual input state + Object.keys(keyState).forEach(key => keyState[key] = false); + manualInputVelX = 0; + manualInputVelY = 0; + manualInputOmega = 0; + } +}); + +// Keyboard event listeners +document.addEventListener('keydown', (e) => { + if (!isManualInputMode) return; + + const key = e.key.toLowerCase(); + if (key in keyState) { + keyState[key] = true; + e.preventDefault(); + updateManualInputVelocities(); + } +}); + +document.addEventListener('keyup', (e) => { + if (!isManualInputMode) return; + + const key = e.key.toLowerCase(); + if (key in keyState) { + keyState[key] = false; + e.preventDefault(); + updateManualInputVelocities(); + } +}); + +// Function to update velocities based on manual input devices (keyboard/gamepad) +function updateManualInputVelocities() { + const maxSpeed = parseFloat(keyboardMaxSpeed.value); + const maxRotation = parseFloat(keyboardMaxRotation.value); + + // Calculate translation velocities from keyboard input + manualInputVelX = 0; + manualInputVelY = 0; + + if (keyState.d) manualInputVelX += maxSpeed; // Right + if (keyState.a) manualInputVelX -= maxSpeed; // Left + if (keyState.w) manualInputVelY += maxSpeed; // Forward + if (keyState.s) manualInputVelY -= maxSpeed; // Backward + + // Calculate rotation velocity from keyboard input + manualInputOmega = 0; + if (keyState.e) manualInputOmega += maxRotation; // Clockwise + if (keyState.q) manualInputOmega -= maxRotation; // Counter-clockwise + + // TODO: Add gamepad input processing here +} + // Preset button event listeners preset2WheelBtn.addEventListener('click', () => { const positions = PresetConfigs.twoWheel(robotSize); @@ -652,10 +771,16 @@ function animate() { ctx.save(); ctx.translate(canvas.width / 2, canvas.height / 2); - // Update speeds based on sliders - xSpeed = parseFloat(vxSlider.value); - ySpeed = -parseFloat(vySlider.value); - turnSpeed = parseFloat(omegaSlider.value); + // Update speeds based on control mode + if (isManualInputMode) { + xSpeed = manualInputVelX; + ySpeed = -manualInputVelY; // Negative because canvas Y axis is inverted + turnSpeed = manualInputOmega; + } else { + xSpeed = parseFloat(vxSlider.value); + ySpeed = -parseFloat(vySlider.value); + turnSpeed = parseFloat(omegaSlider.value); + } // Update module states before drawing the robot // The drive() method will update the gyroHeading internally @@ -665,11 +790,17 @@ function animate() { // Get the actual robot velocity (after scaling to max module speed) for grid animation const actualVelocity = robot.getActualVelocity(); - // Update control outputs with actual speeds - vxOutput.textContent = `Requested: ${vxSlider.value} | Actual: ${actualVelocity.x.toFixed(2)}`; - vyOutput.textContent = `Requested: ${vySlider.value} | Actual: ${-actualVelocity.y.toFixed(2)}`; - omegaOutput.textContent = `Requested: ${omegaSlider.value} | Actual: ${robot.actualTurnSpeed.toFixed(2)}`; + if (isManualInputMode) { + // In manual input mode (keyboard/gamepad), show the current values + keyboardMaxSpeedOutput.textContent = `Max: ${keyboardMaxSpeed.value} | Current: ${Math.max(Math.abs(actualVelocity.x), Math.abs(actualVelocity.y)).toFixed(1)}`; + keyboardMaxRotationOutput.textContent = `Max: ${keyboardMaxRotation.value} | Current: ${Math.abs(robot.actualTurnSpeed || 0).toFixed(2)}`; + } else { + // In slider mode, show requested vs actual + vxOutput.textContent = `Requested: ${vxSlider.value} | Actual: ${actualVelocity.x.toFixed(2)}`; + vyOutput.textContent = `Requested: ${vySlider.value} | Actual: ${-actualVelocity.y.toFixed(2)}`; + omegaOutput.textContent = `Requested: ${omegaSlider.value} | Actual: ${(robot.actualTurnSpeed || 0).toFixed(2)}`; + } // Animate the grid let offsetSpeedDivisor = (100 - gridSquareSize <= 0 ? 1 : 100 - gridSquareSize);