From 666b14286c1873492ee01ebfabafeb77fdeadbdb Mon Sep 17 00:00:00 2001 From: "marcus.stueben" Date: Mon, 23 Feb 2026 09:09:04 +0100 Subject: [PATCH] add input graph feature --- Pages/Index.cshtml | 5 ++ wwwroot/css/app.css | 6 ++ wwwroot/ts/hudElements/DriverInputs.ts | 14 +++++ wwwroot/ts/hudElements/InputGraph.ts | 76 ++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 wwwroot/ts/hudElements/InputGraph.ts diff --git a/Pages/Index.cshtml b/Pages/Index.cshtml index 908d3af..2f3ceda 100644 --- a/Pages/Index.cshtml +++ b/Pages/Index.cshtml @@ -259,6 +259,11 @@ + + + + +
diff --git a/wwwroot/css/app.css b/wwwroot/css/app.css index 48dbc51..190f689 100644 --- a/wwwroot/css/app.css +++ b/wwwroot/css/app.css @@ -568,6 +568,12 @@ progress#revs::-webkit-progress-value { padding: 0px; } +#input-graph { + background-color: rgba(0, 0, 0, 0.4); + border: 1px solid rgba(255, 255, 255, 0.2); + display: block; +} + #steering-wheel { width: 90px; } diff --git a/wwwroot/ts/hudElements/DriverInputs.ts b/wwwroot/ts/hudElements/DriverInputs.ts index d3b7853..0db74d7 100644 --- a/wwwroot/ts/hudElements/DriverInputs.ts +++ b/wwwroot/ts/hudElements/DriverInputs.ts @@ -1,10 +1,13 @@ import HudElement from "./HudElement.js"; import {validNumberOrDefault, valueIsValidAssertUndefined} from "../consts.js"; import {SharedMemoryKey} from '../SharedMemoryConsumer.js'; +import {InputGraph} from "./InputGraph.js"; export default class DriverInputs extends HudElement { override sharedMemoryKeys: SharedMemoryKey[] = ['throttleRaw', 'throttle', 'brakeRaw', 'brake', 'clutchRaw', 'clutch', 'steerInputRaw', 'steerWheelRangeDegrees']; + private inputGraph: InputGraph | null = null; + private static rawOrReal(n: number, r: number): number { return validNumberOrDefault(n, validNumberOrDefault(r, 0)); } @@ -19,6 +22,13 @@ export default class DriverInputs extends HudElement { const steer = document.getElementById('steering-wheel'); + if (!this.inputGraph) { + const canvas = document.getElementById('input-graph') as HTMLCanvasElement; + if (canvas) { + this.inputGraph = new InputGraph(canvas, 100); + } + } + tRaw = DriverInputs.rawOrReal(tRaw, t); bRaw = DriverInputs.rawOrReal(bRaw, b); cRaw = DriverInputs.rawOrReal(cRaw, c); @@ -33,6 +43,10 @@ export default class DriverInputs extends HudElement { brakeProgress.value = bRaw.toString(); clutchProgress.value = cRaw.toString(); + if (this.inputGraph) { + this.inputGraph.update(tRaw, bRaw); + } + const steerAngle = sRaw * sRange / 2; steer.style.transform = `rotate(${steerAngle}deg)`; diff --git a/wwwroot/ts/hudElements/InputGraph.ts b/wwwroot/ts/hudElements/InputGraph.ts new file mode 100644 index 0000000..262c709 --- /dev/null +++ b/wwwroot/ts/hudElements/InputGraph.ts @@ -0,0 +1,76 @@ +export class InputHistory { + private history: number[] = []; + private readonly maxSize: number; + + constructor(maxSize: number) { + this.maxSize = maxSize; + } + + public add(value: number): void { + this.history.push(value); + if (this.history.length > this.maxSize) { + this.history.shift(); + } + } + + public getHistory(): number[] { + return this.history; + } +} + +export class InputGraph { + private readonly canvas: HTMLCanvasElement; + private readonly ctx: CanvasRenderingContext2D; + private readonly throttleHistory: InputHistory; + private readonly brakeHistory: InputHistory; + + constructor(canvas: HTMLCanvasElement, historySize: number) { + this.canvas = canvas; + const context = canvas.getContext('2d'); + if (!context) { + throw new Error('Could not get 2d context'); + } + this.ctx = context; + this.throttleHistory = new InputHistory(historySize); + this.brakeHistory = new InputHistory(historySize); + } + + public update(throttle: number, brake: number): void { + this.throttleHistory.add(throttle); + this.brakeHistory.add(brake); + this.render(); + } + + private render(): void { + const width = this.canvas.width; + const height = this.canvas.height; + this.ctx.clearRect(0, 0, width, height); + + this.drawCurve(this.throttleHistory.getHistory(), '#00ff00'); // Green for throttle + this.drawCurve(this.brakeHistory.getHistory(), '#ff0000'); // Red for brake + } + + private drawCurve(history: number[], color: string): void { + const historyLength = history.length; + if (historyLength < 2) return; + + this.ctx.strokeStyle = color; + this.ctx.lineWidth = 2; + this.ctx.beginPath(); + + const width = this.canvas.width; + const height = this.canvas.height; + const step = width / (historyLength - 1); + + for (let i = 0; i < historyLength; i++) { + const x = i * step; + const y = height - (history[i] * height); + if (i === 0) { + this.ctx.moveTo(x, y); + } else { + this.ctx.lineTo(x, y); + } + } + this.ctx.stroke(); + } +}