telemetry wizard beta preview
This commit is contained in:
parent
7b18ed568f
commit
d97c4f07a0
@ -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;
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user