Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
de733edf48
|
|||
|
c72b6e5f1a
|
|||
|
64bd500161
|
|||
|
77bcfd3196
|
|||
|
c1aa7d2086
|
|||
|
c225ee696a
|
58
index.html
58
index.html
@ -32,7 +32,43 @@
|
|||||||
velocities. Use the controls to experiment with different configurations and movement patterns.
|
velocities. Use the controls to experiment with different configurations and movement patterns.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Drive Controls</h3>
|
<h3>Control Modes</h3>
|
||||||
|
<p>The simulator offers two control modes. Switch between them using the <strong>Switch to
|
||||||
|
Keyboard/Joystick Controls</strong> or <strong>Switch to Slider Controls</strong> button.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Keyboard/Joystick Mode (Default)</h4>
|
||||||
|
<p>Control the robot using keyboard keys or on-screen joysticks (if on a touch enabled device):</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Keyboard Controls:</strong>
|
||||||
|
<ul>
|
||||||
|
<li><strong>W:</strong> Move Forward</li>
|
||||||
|
<li><strong>A:</strong> Strafe Left</li>
|
||||||
|
<li><strong>S:</strong> Move Backward</li>
|
||||||
|
<li><strong>D:</strong> Strafe Right</li>
|
||||||
|
<li><strong>Q:</strong> Rotate Counter-Clockwise</li>
|
||||||
|
<li><strong>E:</strong> Rotate Clockwise</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><strong>On-Screen Joysticks (Touch enabled devices only):</strong>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Left Joystick:</strong> Controls translation (movement in X and Y
|
||||||
|
directions)</li>
|
||||||
|
<li><strong>Right Joystick:</strong> Controls rotation (turning)</li>
|
||||||
|
<li>Touch or click and drag within the joystick circles to control the robot</li>
|
||||||
|
<li>Joysticks take priority when active; otherwise, keyboard controls are used</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><strong>Max Speed:</strong> Sets the maximum translation speed for keyboard/joystick input
|
||||||
|
</li>
|
||||||
|
<li><strong>Max Rotation:</strong> Sets the maximum rotation speed for keyboard/joystick input
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4>Slider Mode</h4>
|
||||||
|
<p>Use sliders to set precise velocity values:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Strafe Left/Right:</strong> Controls the robot's velocity in the X direction
|
<li><strong>Strafe Left/Right:</strong> Controls the robot's velocity in the X direction
|
||||||
(field-relative). Positive values move right, negative values move left.</li>
|
(field-relative). Positive values move right, negative values move left.</li>
|
||||||
@ -40,11 +76,16 @@
|
|||||||
(field-relative). Positive values move forward, negative values move backward.</li>
|
(field-relative). Positive values move forward, negative values move backward.</li>
|
||||||
<li><strong>Rotation:</strong> Controls the robot's angular velocity (turn rate) in radians per
|
<li><strong>Rotation:</strong> Controls the robot's angular velocity (turn rate) in radians per
|
||||||
second. Positive values rotate counter-clockwise.</li>
|
second. Positive values rotate counter-clockwise.</li>
|
||||||
<li><strong>Max Module Speed:</strong> Sets the maximum speed limit for any individual swerve
|
|
||||||
module. If calculated speeds exceed this, all modules are scaled proportionally.</li>
|
|
||||||
<li><strong>Reset Controls:</strong> Returns all velocity sliders to zero.</li>
|
<li><strong>Reset Controls:</strong> Returns all velocity sliders to zero.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h3>Performance Limits</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Max Module Speed:</strong> Sets the maximum speed limit for any individual swerve
|
||||||
|
module. If calculated speeds exceed this, all modules are scaled proportionally to maintain
|
||||||
|
the intended direction of movement.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3>Preset Configurations</h3>
|
<h3>Preset Configurations</h3>
|
||||||
<p>Choose from 9 pre-built robot configurations ranging from 2 to 16 wheels. Each preset
|
<p>Choose from 9 pre-built robot configurations ranging from 2 to 16 wheels. Each preset
|
||||||
demonstrates different module arrangements:</p>
|
demonstrates different module arrangements:</p>
|
||||||
@ -207,10 +248,10 @@ if scale < 1:
|
|||||||
<legend>Translation & Rotation</legend>
|
<legend>Translation & Rotation</legend>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<button id="control-mode-toggle" type="button">Switch to Keyboard/Joystick Controls</button>
|
<button id="control-mode-toggle" type="button">Switch to Slider Controls</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="slider-controls">
|
<div id="slider-controls" style="display: none;">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label for="vy-slider">Move Forward/Backward (pixels/s)</label>
|
<label for="vy-slider">Move Forward/Backward (pixels/s)</label>
|
||||||
<input type="range" id="vy-slider" min="-300" max="300" step="10" value="0">
|
<input type="range" id="vy-slider" min="-300" max="300" step="10" value="0">
|
||||||
@ -232,7 +273,7 @@ if scale < 1:
|
|||||||
<button id="reset-btn" type="button">Reset Controls</button>
|
<button id="reset-btn" type="button">Reset Controls</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="keyboard-controls" style="display: none;">
|
<div id="keyboard-controls">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label for="keyboard-max-speed">Max Speed (pixels/s)</label>
|
<label for="keyboard-max-speed">Max Speed (pixels/s)</label>
|
||||||
<input type="range" id="keyboard-max-speed" min="50" max="300" step="10" value="150">
|
<input type="range" id="keyboard-max-speed" min="50" max="300" step="10" value="150">
|
||||||
@ -317,6 +358,11 @@ if scale < 1:
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site-footer">
|
||||||
|
<a href="https://git.munebase.dev/Munelit/swerve-visualizer">Source for this site can be found here.</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script type="module" src="vendor/lucio/graham-scan.mjs"></script>
|
<script type="module" src="vendor/lucio/graham-scan.mjs"></script>
|
||||||
|
|||||||
47
script.js
47
script.js
@ -5,7 +5,7 @@
|
|||||||
import GrahamScan from "./vendor/lucio/graham-scan.mjs";
|
import GrahamScan from "./vendor/lucio/graham-scan.mjs";
|
||||||
|
|
||||||
class Joystick {
|
class Joystick {
|
||||||
constructor(ctx, x, y, radius) {
|
constructor(ctx, x, y, radius, visibleByDefault) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
@ -13,7 +13,7 @@ class Joystick {
|
|||||||
this.nubX = x;
|
this.nubX = x;
|
||||||
this.nubY = y;
|
this.nubY = y;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.visible = false;
|
this.visible = visibleByDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -45,9 +45,13 @@ class Joystick {
|
|||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
touchInRange(x, y) {
|
touchInRange(x, y) {
|
||||||
|
if (!this.visible)
|
||||||
|
return false;
|
||||||
|
|
||||||
const deltaX = x - this.x;
|
const deltaX = x - this.x;
|
||||||
const deltaY = y - this.y;
|
const deltaY = y - this.y;
|
||||||
const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
@ -398,7 +402,7 @@ const preset16OctBtn = document.getElementById('preset-16oct');
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Control mode state
|
// Control mode state
|
||||||
let isManualInputMode = false; // true = keyboard/gamepad mode, false = slider mode
|
let isManualInputMode = true; // true = keyboard/gamepad mode, false = slider mode
|
||||||
|
|
||||||
// Keyboard state tracking
|
// Keyboard state tracking
|
||||||
const keyState = {
|
const keyState = {
|
||||||
@ -446,6 +450,9 @@ keyboardMaxRotation.addEventListener('input', (e) => {
|
|||||||
});
|
});
|
||||||
keyboardMaxRotationOutput.textContent = parseFloat(keyboardMaxRotation.value);
|
keyboardMaxRotationOutput.textContent = parseFloat(keyboardMaxRotation.value);
|
||||||
|
|
||||||
|
|
||||||
|
const supportsTouch = (navigator.maxTouchPoints > 0);
|
||||||
|
|
||||||
// Control mode toggle
|
// Control mode toggle
|
||||||
controlModeToggle.addEventListener('click', () => {
|
controlModeToggle.addEventListener('click', () => {
|
||||||
isManualInputMode = !isManualInputMode;
|
isManualInputMode = !isManualInputMode;
|
||||||
@ -463,13 +470,15 @@ controlModeToggle.addEventListener('click', () => {
|
|||||||
vxOutput.textContent = '0';
|
vxOutput.textContent = '0';
|
||||||
vyOutput.textContent = '0';
|
vyOutput.textContent = '0';
|
||||||
omegaOutput.textContent = '0';
|
omegaOutput.textContent = '0';
|
||||||
leftJoystick.setIsVisible(true);
|
if (supportsTouch) {
|
||||||
rightJoystick.setIsVisible(true);
|
leftJoystick.setIsVisible(true);
|
||||||
|
rightJoystick.setIsVisible(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Switch to slider mode
|
// Switch to slider mode
|
||||||
sliderControls.style.display = 'block';
|
sliderControls.style.display = 'block';
|
||||||
keyboardControls.style.display = 'none';
|
keyboardControls.style.display = 'none';
|
||||||
controlModeToggle.textContent = 'Switch to Keyboard/Joystick Controls';
|
controlModeToggle.textContent = supportsTouch ? 'Switch to Keyboard/Joystick Controls' : 'Switch to Keyboard';
|
||||||
|
|
||||||
// Reset manual input state
|
// Reset manual input state
|
||||||
Object.keys(keyState).forEach(key => keyState[key] = false);
|
Object.keys(keyState).forEach(key => keyState[key] = false);
|
||||||
@ -883,8 +892,8 @@ function updateModuleDisplays(robot) {
|
|||||||
* BEGIN ANIMATION CODE
|
* BEGIN ANIMATION CODE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const leftJoystick = new Joystick(ctx, -250, 250, 100);
|
const leftJoystick = new Joystick(ctx, -250, 250, 100, supportsTouch);
|
||||||
const rightJoystick = new Joystick(ctx, 250, 250, 100);
|
const rightJoystick = new Joystick(ctx, 250, 250, 100, supportsTouch);
|
||||||
|
|
||||||
function drawGrid(ctx, sideLength, gridSquareSize, xOffset, yOffset) {
|
function drawGrid(ctx, sideLength, gridSquareSize, xOffset, yOffset) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
@ -990,9 +999,6 @@ function drawRobot(ctx, robot, heading) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Variables
|
// Initialize Variables
|
||||||
// Joysticks
|
|
||||||
const supportsTouch = (navigator.maxTouchPoints > 0);
|
|
||||||
|
|
||||||
|
|
||||||
// General robot
|
// General robot
|
||||||
const robotSize = 200;
|
const robotSize = 200;
|
||||||
@ -1018,13 +1024,20 @@ function animate() {
|
|||||||
if (isManualInputMode) {
|
if (isManualInputMode) {
|
||||||
const maxSpeed = parseFloat(keyboardMaxSpeed.value);
|
const maxSpeed = parseFloat(keyboardMaxSpeed.value);
|
||||||
const maxRotation = parseFloat(keyboardMaxRotation.value);
|
const maxRotation = parseFloat(keyboardMaxRotation.value);
|
||||||
// xSpeed = manualInputVelX;
|
|
||||||
// ySpeed = -manualInputVelY; // Negative because canvas Y axis is inverted
|
|
||||||
// turnSpeed = manualInputOmega;
|
|
||||||
|
|
||||||
xSpeed = leftJoystick.getX() * maxSpeed;
|
// Combine keyboard and joystick inputs
|
||||||
ySpeed = -leftJoystick.getY() * maxSpeed;
|
const keyboardX = manualInputVelX;
|
||||||
turnSpeed = rightJoystick.getX() * maxRotation;
|
const keyboardY = -manualInputVelY; // Negative because canvas Y axis is inverted
|
||||||
|
const keyboardOmega = manualInputOmega;
|
||||||
|
|
||||||
|
const joystickX = leftJoystick.getX() * maxSpeed;
|
||||||
|
const joystickY = -leftJoystick.getY() * maxSpeed;
|
||||||
|
const joystickOmega = rightJoystick.getX() * maxRotation;
|
||||||
|
|
||||||
|
// Use joystick if active, otherwise use keyboard
|
||||||
|
xSpeed = leftJoystick.active ? joystickX : keyboardX;
|
||||||
|
ySpeed = leftJoystick.active ? joystickY : keyboardY;
|
||||||
|
turnSpeed = rightJoystick.active ? joystickOmega : keyboardOmega;
|
||||||
} else {
|
} else {
|
||||||
xSpeed = parseFloat(vxSlider.value);
|
xSpeed = parseFloat(vxSlider.value);
|
||||||
ySpeed = -parseFloat(vySlider.value);
|
ySpeed = -parseFloat(vySlider.value);
|
||||||
|
|||||||
@ -232,6 +232,8 @@ tr:hover {
|
|||||||
margin-top: var(--spacing-large);
|
margin-top: var(--spacing-large);
|
||||||
border-top: 2px solid var(--border-color);
|
border-top: 2px solid var(--border-color);
|
||||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||||
|
grid-row: 4 / 5;
|
||||||
|
grid-column: 1 / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Additions for this page */
|
/* Additions for this page */
|
||||||
|
|||||||
Reference in New Issue
Block a user