From ba10cfcbdd801a634ab5927f624d081b299d9074 Mon Sep 17 00:00:00 2001 From: Mathias Malmqvist Date: Wed, 29 Oct 2025 20:14:39 +0100 Subject: [PATCH] Add stick dials to the range calibration modal --- css/main.css | 10 ++++ index.html | 4 +- js/core.js | 21 +++++-- js/modals/calib-range-modal.js | 105 ++++++++++++++++++++++++++++++++- js/modals/finetune-modal.js | 6 +- js/stick-renderer.js | 2 +- templates/range-modal.html | 14 ++++- 7 files changed, 148 insertions(+), 14 deletions(-) diff --git a/css/main.css b/css/main.css index 0e42965..bbcc3e2 100644 --- a/css/main.css +++ b/css/main.css @@ -114,6 +114,16 @@ dl.row dd { animation: blink 1s infinite; } +/* Pulsing animation for text */ +@keyframes pulse-text { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.pulsing-text { + animation: pulse-text 0.75s ease-in-out infinite; +} + /* Set text color to red for internationalized elements */ /* .ds-i18n { color: red; diff --git a/index.html b/index.html index a339b1a..908500d 100644 --- a/index.html +++ b/index.html @@ -180,7 +180,7 @@ diff --git a/js/core.js b/js/core.js index 1e5c927..00b8f59 100644 --- a/js/core.js +++ b/js/core.js @@ -5,10 +5,10 @@ import { initControllerManager } from './controller-manager.js'; import ControllerFactory from './controllers/controller-factory.js'; import { lang_init, l } from './translations.js'; import { loadAllTemplates } from './template-loader.js'; -import { draw_stick_position, CIRCULARITY_DATA_SIZE } from './stick-renderer.js'; +import { draw_stick_dial, CIRCULARITY_DATA_SIZE } from './stick-renderer.js'; import { ds5_finetune, isFinetuneVisible, finetune_handle_controller_input } from './modals/finetune-modal.js'; import { calibrate_stick_centers, auto_calibrate_stick_centers } from './modals/calib-center-modal.js'; -import { calibrate_range } from './modals/calib-range-modal.js'; +import { calibrate_range, rangeCalibHandleControllerInput } from './modals/calib-range-modal.js'; import { show_quick_test_modal, isQuickTestVisible, @@ -526,14 +526,14 @@ function refresh_stick_pos() { const enable_circ_test = circ_checked(); // Draw left stick - draw_stick_position(ctx, hb, yb, sz, plx, ply, { + draw_stick_dial(ctx, hb, yb, sz, plx, ply, { circularity_data: enable_circ_test ? ll_data : null, enable_zoom_center, }); if(!hasSingleStick) { // Draw right stick - draw_stick_position(ctx, w-hb, yb, sz, prx, pry, { + draw_stick_dial(ctx, w-hb, yb, sz, prx, pry, { circularity_data: enable_circ_test ? rr_data : null, enable_zoom_center, }); @@ -763,10 +763,23 @@ function detectFailedRangeCalibration(changes) { } } +function isRangeCalibrationVisible() { + const modal = document.getElementById('rangeModal'); + if (!modal) return false; + return modal.classList.contains('show'); +} + // Callback function to handle UI updates after controller input processing function handleControllerInput({ changes, inputConfig, touchPoints, batteryStatus }) { const { buttonMap } = inputConfig; + // Update range calibration modal stick visualization if visible + if (isRangeCalibrationVisible() && changes.sticks) { + collectCircularityData(changes.sticks, ll_data, rr_data); + rangeCalibHandleControllerInput(changes); + return; + } + // Handle Quick Test Modal input (can be open from any tab) if (isQuickTestVisible()) { quicktest_handle_controller_input(changes, batteryStatus); diff --git a/js/modals/calib-range-modal.js b/js/modals/calib-range-modal.js index 0b369b8..fe89080 100644 --- a/js/modals/calib-range-modal.js +++ b/js/modals/calib-range-modal.js @@ -2,10 +2,9 @@ import { sleep } from '../utils.js'; import { l } from '../translations.js'; -import { CIRCULARITY_DATA_SIZE } from '../stick-renderer.js'; +import { CIRCULARITY_DATA_SIZE, draw_stick_dial } from '../stick-renderer.js'; const SECONDS_UNTIL_UNLOCK = 15; -const EXPERT_MODE_STORAGE_KEY = 'rangeCalibExpertMode'; /** * Calibrate Stick Range Modal Class @@ -41,6 +40,16 @@ export class CalibRangeModal { this.doneCallback = doneCallback; this.hasSingleStick = (this.controller.currentController.getNumberOfSticks() == 1); + + // Stick rendering + this.leftCanvasCtx = null; + this.rightCanvasCtx = null; + this.stickRenderInterval = null; + this.currentStickPositions = { + left: { x: 0, y: 0 }, + right: { x: 0, y: 0 } + }; + this._initEventListeners(); } @@ -78,6 +87,8 @@ export class CalibRangeModal { this.ll_data.fill(0); this.rr_data.fill(0); + this._initializeCanvases(); + this._updateUIVisibility(); if (!this.expertMode) { this.updateProgress(); // reset progress bar @@ -92,6 +103,7 @@ export class CalibRangeModal { } async onClose() { + this.stopStickRendering(); this.stopProgressMonitoring(); this.stopCountdown(); @@ -292,6 +304,89 @@ export class CalibRangeModal { $('#range-progress-text-container').show(); } } + + /** + * Initialize canvas elements for stick rendering + */ + _initializeCanvases() { + const leftCanvas = document.getElementById('range-left-stick-canvas'); + const rightCanvas = document.getElementById('range-right-stick-canvas'); + + this.leftCanvasCtx = leftCanvas.getContext('2d'); + this.rightCanvasCtx = rightCanvas.getContext('2d'); + + // Clear initial canvases + this._clearCanvas(this.leftCanvasCtx, leftCanvas); + this._clearCanvas(this.rightCanvasCtx, rightCanvas); + + // Start rendering loop + this.startStickRendering(); + } + + /** + * Clear a canvas with white background + */ + _clearCanvas(ctx, canvas) { + if (!ctx) return; + ctx.fillStyle = '#ffffff'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + + /** + * Update current stick positions for rendering + */ + handleControllerInput({sticks}) { + if (sticks?.left) { + this.currentStickPositions.left = { ...sticks.left }; + } + if (sticks?.right) { + this.currentStickPositions.right = { ...sticks.right }; + } + } + + /** + * Start stick rendering loop + */ + startStickRendering() { + if (this.stickRenderInterval) return; + + this.stickRenderInterval = setInterval(() => { + this._renderSticks(); + }, 16); // ~60 FPS + } + + /** + * Stop stick rendering loop + */ + stopStickRendering() { + if (this.stickRenderInterval) { + clearInterval(this.stickRenderInterval); + this.stickRenderInterval = null; + } + } + + /** + * Render both stick dials + */ + _renderSticks() { + if (!this.leftCanvasCtx || !this.rightCanvasCtx) return; + + const leftCanvas = this.leftCanvasCtx.canvas; + const rightCanvas = this.rightCanvasCtx.canvas; + + // Clear canvases + this._clearCanvas(this.leftCanvasCtx, leftCanvas); + this._clearCanvas(this.rightCanvasCtx, rightCanvas); + + // Draw stick dials in normal mode (no circularity data, no zoom) + const size = 60; + const centerX = leftCanvas.width / 2; + const centerY = leftCanvas.height / 2; + const {left, right} = this.currentStickPositions; + + draw_stick_dial(this.leftCanvasCtx, centerX, centerY, size, left.x, left.y); + draw_stick_dial(this.rightCanvasCtx, centerX, centerY, size, right.x, right.y); + } } // Global reference to the current range calibration instance @@ -321,5 +416,11 @@ async function calibrate_range_on_close() { } } +export function rangeCalibHandleControllerInput(changes) { + if (currentCalibRangeInstance) { + currentCalibRangeInstance.handleControllerInput(changes); + } +} + // Expose functions to window for HTML onclick handlers window.calibrate_range_on_close = calibrate_range_on_close; diff --git a/js/modals/finetune-modal.js b/js/modals/finetune-modal.js index 810690b..38ab711 100644 --- a/js/modals/finetune-modal.js +++ b/js/modals/finetune-modal.js @@ -1,6 +1,6 @@ 'use strict'; -import { draw_stick_position } from '../stick-renderer.js'; +import { draw_stick_dial } from '../stick-renderer.js'; import { dec2hex32, float_to_str, la } from '../utils.js'; import { auto_calibrate_stick_centers } from './calib-center-modal.js'; import { calibrate_range } from './calib-range-modal.js'; @@ -698,13 +698,13 @@ export class Finetune { if (this._mode === 'circularity') { // Draw stick position with circle const circularityData = lOrR === 'left' ? this.ll_data : this.rr_data; - draw_stick_position(ctx, hb, yb, sz, plx, ply, { + draw_stick_dial(ctx, hb, yb, sz, plx, ply, { circularity_data: circularityData, highlight }); } else { // Draw stick position with crosshair - draw_stick_position(ctx, hb, yb, sz, plx, ply, { + draw_stick_dial(ctx, hb, yb, sz, plx, ply, { enable_zoom_center: true, highlight }); diff --git a/js/stick-renderer.js b/js/stick-renderer.js index 21f98d6..d5e54a1 100644 --- a/js/stick-renderer.js +++ b/js/stick-renderer.js @@ -16,7 +16,7 @@ export const CIRCULARITY_DATA_SIZE = 48; // Number of angular positions to sampl * @param {boolean} opts.enable_zoom_center - Whether to apply center zoom transformation * @param {boolean} opts.highlight - Whether to highlight the stick position */ -export function draw_stick_position(ctx, center_x, center_y, sz, stick_x, stick_y, opts = {}) { +export function draw_stick_dial(ctx, center_x, center_y, sz, stick_x, stick_y, opts = {}) { const { circularity_data = null, enable_zoom_center = false, highlight } = opts; // Draw base circle diff --git a/templates/range-modal.html b/templates/range-modal.html index f9cfa77..163d890 100644 --- a/templates/range-modal.html +++ b/templates/range-modal.html @@ -6,9 +6,19 @@

Range calibration