telemetry wizard beta preview

This commit is contained in:
e7d 2023-06-10 22:03:57 +02:00
parent 7b18ed568f
commit d97c4f07a0
No known key found for this signature in database
GPG Key ID: F320BE007C0B8881
2 changed files with 176 additions and 25 deletions

View File

@ -1,6 +1,8 @@
#gamepad #telemetry { #gamepad #telemetry {
background: var(--main-bg-color); background: var(--main-bg-color);
display: flex; display: flex;
width: fit-content;
margin: auto;
height: 120px; height: 120px;
padding: 4px; padding: 4px;
border-radius: 6px; border-radius: 6px;
@ -14,7 +16,7 @@
--throttle-color: #0CA818; --throttle-color: #0CA818;
} }
#gamepad #telemetry.with-steering { #gamepad #telemetry:has(#steering:not([style*="display: none"])) {
border-top-right-radius: 60px; border-top-right-radius: 60px;
border-bottom-right-radius: 60px; border-bottom-right-radius: 60px;
} }
@ -115,14 +117,22 @@
transition: transform 100ms; transition: transform 100ms;
} }
#wizard #wizard-instructions h4, #wizard h4 {
text-align: center;
margin-bottom: 0.5em;
}
#wizard #wizard-instructions #wizard-preview .controller {
position: relative;
transform: none;
top: auto;
left: auto;
}
#wizard #wizard-instructions p { #wizard #wizard-instructions p {
text-align: center; text-align: center;
} }
#wizard #wizard-instructions h4 {
margin-bottom: 0.5em;
}
#wizard .wizard-options { #wizard .wizard-options {
display: flex; display: flex;

View File

@ -33,9 +33,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
* @returns {number} * @returns {number}
*/ */
toPercentage(value, min, max) { toPercentage(value, min, max) {
return value !== undefined return Math.max(0, Math.min(100, Math.round((value - min) * (100 / (max - min)))));
? Math.max(0, Math.min(100, Math.round((value - min) * (100 / (max - min)))))
: 0;
} }
/** /**
@ -61,8 +59,12 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
toAxisValue(gamepad, axis) { toAxisValue(gamepad, axis) {
const { type, index, min, max } = this[axis]; const { type, index, min, max } = this[axis];
if (!type || !index) return null; if (!type || !index) return null;
const value = type === 'button' ? gamepad.buttons[index].value : gamepad.axes[index]; const value = type === 'button'
return axis === 'steering' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max); ? gamepad.buttons[index].value
: gamepad.axes[index];
return axis === 'steering'
? this.toDegrees(value, min, max)
: this.toPercentage(value, min, max);
} }
/** /**
@ -85,6 +87,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.$steering = this.$telemetry.querySelector('#steering'); this.$steering = this.$telemetry.querySelector('#steering');
this.$steeringIndicator = this.$steering.querySelector('.indicator'); this.$steeringIndicator = this.$steering.querySelector('.indicator');
this.$wizard = document.querySelector('#wizard'); this.$wizard = document.querySelector('#wizard');
this.$wizardPreview = this.$wizard.querySelector('#wizard-preview');
this.$wizardInstructions = this.$wizard.querySelector('#wizard-instructions'); this.$wizardInstructions = this.$wizard.querySelector('#wizard-instructions');
} }
@ -147,15 +150,19 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
/** /**
* Draws the live chart with the initial data and starts the draw update loop * Draws the live chart with the initial data and starts the draw update loop
*
* @param {boolean} [demo=false]
*/ */
setupChart() { setupChart(demo = false) {
if (!this.withChart) return; if (!this.withChart) return;
this.scaleChart(); this.scaleChart();
const now = Date.now(); const now = Date.now();
this.chartData = []; this.chartData = [];
for (let timestamp = now - this.historyLength; timestamp < now; timestamp += this.interval) { for (let index = 0; index < this.historyLength / this.interval; index++) {
this.chartData.push({ timestamp, clutch: 0, brake: 0, throttle: 0 }); const timestamp = now - (this.historyLength - index * this.interval);
const data = demo ? this.getDemoData() : { clutch: 0, brake: 0, throttle: 0 };
this.chartData.push({ timestamp, ...data });
} }
} }
@ -164,7 +171,6 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
*/ */
init() { init() {
this.interval = 1000 / this.frequency; this.interval = 1000 / this.frequency;
this.length = this.historyLength / this.interval;
this.setupTemplate(); this.setupTemplate();
this.setupChart(); this.setupChart();
@ -188,6 +194,33 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
window.setTimeout(() => this.update(), this.interval); window.setTimeout(() => this.update(), this.interval);
} }
/**
* @todo: produce realistic data
* Generate alternative wave of demo data from 0 to 100 for throttle, brake and clutch
*
* @returns {object}
*/
getDemoData() {
const clutch = this.withClutch ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval)) * 100)) : 0;
const brake = this.withBrake ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 1.5) * 100)) : 0;
const throttle = this.withThrottle ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 3) * 100)) : 0;
const steering = this.withSteering ? Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 3) * this.angle) : 0;
if (this.demoIndex++ > 10000) this.demoIndex = 0;
return { clutch, brake, throttle, steering };
}
/**
* Updates the live chart and the meters for the demo mode
*/
updateDemo() {
const { clutch, brake, throttle, steering } = this.getDemoData();
if (this.withChart) this.drawChart(clutch, brake, throttle);
if (this.withMeters) this.updateMeters(clutch, brake, throttle);
if (this.withSteering) this.updateSteering(steering);
window.setTimeout(() => this.updateDemo(), this.interval);
}
/** /**
* Updates the data used to draw the cart * Updates the data used to draw the cart
* *
@ -223,7 +256,6 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.AXES.forEach((axis) => { this.AXES.forEach((axis) => {
if (axis === 'steering') return; if (axis === 'steering') return;
chartContext.beginPath(); chartContext.beginPath();
for (let index = 0; index < this.chartData.length; index++) { for (let index = 0; index < this.chartData.length; index++) {
@ -248,7 +280,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
*/ */
updateMeters(clutch, brake, throttle) { updateMeters(clutch, brake, throttle) {
for (const [axis, value] of Object.entries({ clutch, brake, throttle })) { for (const [axis, value] of Object.entries({ clutch, brake, throttle })) {
if (value === null) return; if (value === null) continue;
this[`$${axis}Value`].innerHTML = value; this[`$${axis}Value`].innerHTML = value;
this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`; this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`;
this[`$${axis}Bar`].style.height = `${value}%`; this[`$${axis}Bar`].style.height = `${value}%`;
@ -264,6 +296,105 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.$steeringIndicator.style.transform = `rotate(${steering}deg)`; this.$steeringIndicator.style.transform = `rotate(${steering}deg)`;
} }
/**
* Set ups the wizard with the widget demo mode
*/
setupWizard() {
this.interval = 1000 / 60;
this.$wizardInstructions.querySelector('#wizard-preview').appendChild(this.$telemetry);
this.withChart = true;
this.withMeters = true;
this.withClutch = true;
this.withBrake = true;
this.withThrottle = true;
this.withSteering = true;
this.angle = 360;
this.demoIndex = 0;
this.setupChart(true);
this.updateDemo();
const $inputs = {
$clutchOption: this.$wizardInstructions.querySelector('#clutch-option'),
$brakeOption: this.$wizardInstructions.querySelector('#brake-option'),
$throttleOption: this.$wizardInstructions.querySelector('#throttle-option'),
$steeringOption: this.$wizardInstructions.querySelector('#steering-option')
};
const $chartOption = this.$wizardInstructions.querySelector('#chart-option');
const $historyOption = this.$wizardInstructions.querySelector('#history-option');
const $metersOption = this.$wizardInstructions.querySelector('#meters-option');
const $steeringAngleOption = this.$wizardInstructions.querySelector('#steering-angle-option');
const $fpsOption = this.$wizardInstructions.querySelector('[name=fps-option]');
$inputs.$clutchOption.addEventListener('change', () => {
this.withClutch = $inputs.$clutchOption.checked;
if (this.withClutch) {
this.$clutch.style.display = '';
} else {
this.$clutch.style.display = 'none';
this.chartData.forEach((data) => data.clutch = 0);
}
});
$inputs.$brakeOption.addEventListener('change', () => {
this.withBrake = $inputs.$brakeOption.checked;
if (this.withBrake) {
this.$brake.style.display = '';
} else {
this.$brake.style.display = 'none';
this.chartData.forEach((data) => data.brake = 0);
}
});
$inputs.$throttleOption.addEventListener('change', () => {
this.withThrottle = $inputs.$throttleOption.checked;
if (this.withThrottle) {
this.$throttle.style.display = '';
} else {
this.$throttle.style.display = 'none';
this.chartData.forEach((data) => data.throttle = 0);
}
});
$inputs.$steeringOption.addEventListener('change', () => {
this.withSteering = $inputs.$steeringOption.checked;
if (this.withSteering) {
this.$steering.style.display = '';
} else {
this.$steering.style.display = 'none';
}
});
$chartOption.addEventListener('change', () => {
this.withChart = $chartOption.checked;
if (this.withChart) {
this.$chart.style.display = '';
} else {
this.$chart.style.display = 'none';
}
this.setupChart(true);
});
$historyOption.addEventListener('change', () => {
this.historyLength = parseInt($historyOption.value) * 1000;
this.setupChart(true);
});
$metersOption.addEventListener('change', () => {
this.withMeters = $metersOption.checked;
if (this.withMeters) {
this.$meters.style.display = '';
} else {
this.$meters.style.display = 'none';
}
this.setupChart(true);
});
$steeringAngleOption.addEventListener('change', () => {
this.angle = $steeringAngleOption.value;
});
$fpsOption.addEventListener('change', () => {
this.interval = 1000 / parseInt($fpsOption.value);
console.log(this.interval);
this.setupChart(true);
});
return { $inputs, $chartOption, $historyOption, $metersOption, $steeringAngleOption, $fpsOption };
}
/** /**
* Waits for one or all buttons of a gamepad to be released * Waits for one or all buttons of a gamepad to be released
* *
@ -360,6 +491,8 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
*/ */
async askForOptions() { async askForOptions() {
this.$wizardInstructions.innerHTML = ` this.$wizardInstructions.innerHTML = `
<h4>Preview</h4>
<div id="wizard-preview"></div>
<h4>Inputs</h4> <h4>Inputs</h4>
<div class="wizard-options"> <div class="wizard-options">
<div> <div>
@ -416,19 +549,27 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
<p>Then, press any button to continue.</p> <p>Then, press any button to continue.</p>
`; `;
const {
$inputs,
$chartOption,
$historyOption,
$metersOption,
$steeringAngleOption,
$fpsOption
} = this.setupWizard();
await this.waitButtonRelease(); await this.waitButtonRelease();
await this.waitButtonClick(); await this.waitButtonClick();
return { return {
...this.AXES.reduce((options, axis) => { ...this.AXES.reduce((options, axis) => ({
options[`with${this.capitalize(axis)}`] = document.querySelector(`[name=${axis}-option]`).checked; ...options,
return options; [`with${this.capitalize(axis)}`]: $inputs[`$${axis}Option`].checked
}, {}), }), {}),
chart: document.querySelector('[name=chart-option]').checked, chart: $chartOption.checked,
history: parseInt(document.querySelector('[name=history-option]').value) * 1000, history: parseInt($historyOption.value) * 1000,
meters: document.querySelector('[name=meters-option]').checked, meters: $metersOption.checked,
angle: this.$wizardInstructions.querySelector('input[name="steering-angle-option"]').value, angle: $steeringAngleOption.value,
fps: parseInt(document.querySelector('[name=fps-option]').value) fps: parseInt($fpsOption.value)
}; };
} }