summaryrefslogtreecommitdiff
path: root/src/gauges.cpp
diff options
context:
space:
mode:
authoraura <nw@moneybot.cc>2026-07-13 10:15:03 +0200
committeraura <nw@moneybot.cc>2026-07-13 10:15:03 +0200
commit57d172df4db1451ba3caeeb0ca485d0845a61912 (patch)
treead0be7c8bec79a1eb58b25f2a631bc4b6809e7fd /src/gauges.cpp
push source
Diffstat (limited to 'src/gauges.cpp')
-rw-r--r--src/gauges.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/src/gauges.cpp b/src/gauges.cpp
new file mode 100644
index 0000000..a15b65c
--- /dev/null
+++ b/src/gauges.cpp
@@ -0,0 +1,621 @@
+#include <cstdio>
+#include <math.h>
+
+#include "gauges.h"
+
+// 7-seg masks, bit order A B C D E F G; >=10 are letters
+const U8 SEG7[G_COUNT] = {
+ 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
+ 0x50, // r
+ 0x54, // n
+ 0x40, // -
+ 0x77, // A
+ 0x7C, // b
+ 0x6D, // S
+ 0x78, // t
+ 0x39, // C
+};
+
+F32 gauge_angle( F32 t ) {
+ const F32 A0 = -90.0f * (F32)M_PI / 180.0f; // south
+ const F32 A1 = -360.0f * (F32)M_PI / 180.0f; // east, clockwise
+ if( t < 0.0f ) t = 0.0f;
+ if( t > 1.0f ) t = 1.0f;
+ return A0 + ( A1 - A0 ) * t;
+}
+
+GAUGE_RENDERER::GAUGE_RENDERER() {
+ nverts = 0;
+ fade = 1.0f;
+}
+
+void GAUGE_RENDERER::clear() {
+ nverts = 0;
+ fade = 1.0f;
+}
+
+void GAUGE_RENDERER::set_fade( F32 f ) {
+ if( f < 0.0f ) f = 0.0f;
+ if( f > 1.0f ) f = 1.0f;
+ fade = f;
+}
+
+U32 GAUGE_RENDERER::faded( U32 c ) const {
+ if( fade >= 0.999f )
+ return c;
+ U32 a = (U32)( ( c >> 24 ) * fade );
+ return ( c & 0x00ffffffu ) | ( a << 24 );
+}
+
+void GAUGE_RENDERER::rect( F32 x, F32 y, F32 w, F32 h, U32 c ) {
+ c = faded( c );
+ if( w <= 0.0f || h <= 0.0f )
+ return;
+ if( nverts + 6 > MAX_VERTS )
+ return;
+
+ VERTEX *v = verts + nverts;
+ v[0].x = x; v[0].y = y;
+ v[1].x = x + w; v[1].y = y;
+ v[2].x = x; v[2].y = y + h;
+ v[3].x = x + w; v[3].y = y;
+ v[4].x = x + w; v[4].y = y + h;
+ v[5].x = x; v[5].y = y + h;
+ for( I32 i = 0; i < 6; i++ )
+ v[i].col = c;
+ nverts += 6;
+}
+
+void GAUGE_RENDERER::line( F32 x0, F32 y0, F32 x1, F32 y1, F32 t, U32 c ) {
+ c = faded( c );
+ F32 dx = x1 - x0, dy = y1 - y0;
+ F32 len = sqrtf( dx * dx + dy * dy );
+ if( len < 1e-3f || nverts + 6 > MAX_VERTS )
+ return;
+
+ F32 nx = -dy / len * t * 0.5f;
+ F32 ny = dx / len * t * 0.5f;
+ VERTEX *v = verts + nverts;
+ v[0].x = x0 + nx; v[0].y = y0 + ny;
+ v[1].x = x1 + nx; v[1].y = y1 + ny;
+ v[2].x = x0 - nx; v[2].y = y0 - ny;
+ v[3].x = x1 + nx; v[3].y = y1 + ny;
+ v[4].x = x1 - nx; v[4].y = y1 - ny;
+ v[5].x = x0 - nx; v[5].y = y0 - ny;
+ for( I32 i = 0; i < 6; i++ )
+ v[i].col = c;
+ nverts += 6;
+}
+
+void GAUGE_RENDERER::arc(
+ F32 cx, F32 cy, F32 r0, F32 r1,
+ F32 a0, F32 a1, U32 c
+) {
+ c = faded( c );
+ F32 span = a1 - a0;
+ I32 segs = (I32)( fabsf( span ) / 0.05f ) + 1;
+ if( segs > 256 )
+ segs = 256;
+ F32 step = span / (F32)segs;
+
+ F32 ca = cosf( a0 ), sa = sinf( a0 );
+ for( I32 i = 0; i < segs; i++ ) {
+ if( nverts + 6 > MAX_VERTS )
+ return;
+
+ F32 a = a0 + step * (F32)( i + 1 );
+ F32 cb = cosf( a ), sb = sinf( a );
+ VERTEX *v = verts + nverts;
+ v[0].x = cx + r0 * ca; v[0].y = cy - r0 * sa;
+ v[1].x = cx + r1 * ca; v[1].y = cy - r1 * sa;
+ v[2].x = cx + r0 * cb; v[2].y = cy - r0 * sb;
+ v[3].x = cx + r1 * ca; v[3].y = cy - r1 * sa;
+ v[4].x = cx + r1 * cb; v[4].y = cy - r1 * sb;
+ v[5].x = cx + r0 * cb; v[5].y = cy - r0 * sb;
+ for( I32 k = 0; k < 6; k++ )
+ v[k].col = c;
+ nverts += 6;
+ ca = cb; sa = sb;
+ }
+}
+
+void GAUGE_RENDERER::arc_grad(
+ F32 cx, F32 cy, F32 r0, F32 r1,
+ F32 a0, F32 a1, U32 c_in, U32 c_out
+) {
+ c_in = faded( c_in );
+ c_out = faded( c_out );
+ F32 span = a1 - a0;
+ I32 segs = (I32)( fabsf( span ) / 0.05f ) + 1;
+ if( segs > 256 )
+ segs = 256;
+ F32 step = span / (F32)segs;
+
+ F32 ca = cosf( a0 ), sa = sinf( a0 );
+ for( I32 i = 0; i < segs; i++ ) {
+ if( nverts + 6 > MAX_VERTS )
+ return;
+
+ F32 a = a0 + step * (F32)( i + 1 );
+ F32 cb = cosf( a ), sb = sinf( a );
+ VERTEX *v = verts + nverts;
+ v[0].x = cx + r0 * ca; v[0].y = cy - r0 * sa; v[0].col = c_in;
+ v[1].x = cx + r1 * ca; v[1].y = cy - r1 * sa; v[1].col = c_out;
+ v[2].x = cx + r0 * cb; v[2].y = cy - r0 * sb; v[2].col = c_in;
+ v[3].x = cx + r1 * ca; v[3].y = cy - r1 * sa; v[3].col = c_out;
+ v[4].x = cx + r1 * cb; v[4].y = cy - r1 * sb; v[4].col = c_out;
+ v[5].x = cx + r0 * cb; v[5].y = cy - r0 * sb; v[5].col = c_in;
+ nverts += 6;
+ ca = cb; sa = sb;
+ }
+}
+
+void GAUGE_RENDERER::disc( F32 cx, F32 cy, F32 r, U32 c ) {
+ arc( cx, cy, 0.0f, r, 0.0f, 2.0f * (F32)M_PI, c );
+}
+
+void GAUGE_RENDERER::disc_grad( F32 cx, F32 cy, F32 r, U32 c_in, U32 c_out ) {
+ arc_grad( cx, cy, 0.0f, r, 0.0f, 2.0f * (F32)M_PI, c_in, c_out );
+}
+
+void GAUGE_RENDERER::tick(
+ F32 cx, F32 cy, F32 a,
+ F32 rin, F32 rout,
+ F32 t, U32 c
+) {
+ F32 dx = cosf( a ), dy = -sinf( a );
+ line( cx + dx * rin, cy + dy * rin,
+ cx + dx * rout, cy + dy * rout, t, c );
+}
+
+void GAUGE_RENDERER::needle(
+ F32 cx, F32 cy, F32 a,
+ F32 len, F32 back,
+ F32 w, U32 c
+) {
+ c = faded( c );
+ if( nverts + 6 > MAX_VERTS )
+ return;
+
+ F32 dx = cosf( a ), dy = -sinf( a );
+ F32 px = -dy, py = dx;
+ F32 bx = cx - dx * back, by = cy - dy * back;
+ F32 tx = cx + dx * len, ty = cy + dy * len;
+ F32 wt = w * 0.18f;
+ VERTEX *v = verts + nverts;
+ v[0].x = bx + px * w * 0.5f; v[0].y = by + py * w * 0.5f;
+ v[1].x = tx + px * wt; v[1].y = ty + py * wt;
+ v[2].x = bx - px * w * 0.5f; v[2].y = by - py * w * 0.5f;
+ v[3].x = tx + px * wt; v[3].y = ty + py * wt;
+ v[4].x = tx - px * wt; v[4].y = ty - py * wt;
+ v[5].x = bx - px * w * 0.5f; v[5].y = by - py * w * 0.5f;
+ for( I32 i = 0; i < 6; i++ )
+ v[i].col = c;
+ nverts += 6;
+}
+
+void GAUGE_RENDERER::needle_trail(
+ F32 cx, F32 cy,
+ F32 a0, F32 a1,
+ F32 r0, F32 r1,
+ U32 c
+) {
+ c = faded( c );
+ F32 span = a1 - a0;
+ if( fabsf( span ) < 1e-3f )
+ return;
+
+ I32 segs = (I32)( fabsf( span ) / 0.03f ) + 1;
+ if( segs > 96 )
+ segs = 96;
+ F32 step = span / (F32)segs;
+ U32 base_a = c >> 24;
+ U32 rgb = c & 0x00ffffffu;
+
+ F32 ca = cosf( a0 ), sa = sinf( a0 );
+ for( I32 i = 0; i < segs; i++ ) {
+ if( nverts + 6 > MAX_VERTS )
+ return;
+
+ F32 f0 = (F32)i / (F32)segs;
+ F32 f1 = (F32)( i + 1 ) / (F32)segs;
+ U32 c0 = rgb | ( (U32)( base_a * f0 * f0 ) << 24 );
+ U32 c1 = rgb | ( (U32)( base_a * f1 * f1 ) << 24 );
+
+ F32 a = a0 + step * (F32)( i + 1 );
+ F32 cb = cosf( a ), sb = sinf( a );
+ VERTEX *v = verts + nverts;
+ v[0].x = cx + r0 * ca; v[0].y = cy - r0 * sa; v[0].col = c0;
+ v[1].x = cx + r1 * ca; v[1].y = cy - r1 * sa; v[1].col = c0;
+ v[2].x = cx + r0 * cb; v[2].y = cy - r0 * sb; v[2].col = c1;
+ v[3].x = cx + r1 * ca; v[3].y = cy - r1 * sa; v[3].col = c1;
+ v[4].x = cx + r1 * cb; v[4].y = cy - r1 * sb; v[4].col = c1;
+ v[5].x = cx + r0 * cb; v[5].y = cy - r0 * sb; v[5].col = c0;
+ nverts += 6;
+ ca = cb; sa = sb;
+ }
+}
+
+void GAUGE_RENDERER::glyph( F32 x, F32 y, F32 dw, F32 dh, I32 g, U32 c ) {
+ if( g < 0 || g >= G_COUNT )
+ return;
+
+ U32 m = SEG7[g];
+ F32 t = dh * 0.13f;
+ if( t < 1.0f )
+ t = 1.0f;
+ F32 half = ( dh - 3.0f * t ) * 0.5f;
+
+ if( m & 0x01 ) rect( x + t, y, dw - 2 * t, t, c );
+ if( m & 0x02 ) rect( x + dw - t, y + t, t, half, c );
+ if( m & 0x04 ) rect( x + dw - t, y + 2 * t + half, t, half, c );
+ if( m & 0x08 ) rect( x + t, y + dh - t, dw - 2 * t, t, c );
+ if( m & 0x10 ) rect( x, y + 2 * t + half, t, half, c );
+ if( m & 0x20 ) rect( x, y + t, t, half, c );
+ if( m & 0x40 ) rect( x + t, y + ( dh - t ) * 0.5f, dw - 2 * t, t, c );
+}
+
+void GAUGE_RENDERER::glyph_c( F32 cx, F32 cy, F32 dw, F32 dh, I32 g, U32 c ) {
+ glyph( cx - dw * 0.5f, cy - dh * 0.5f, dw, dh, g, c );
+}
+
+void GAUGE_RENDERER::number(
+ F32 cx, F32 cy,
+ F32 dw, F32 dh,
+ I32 val, U32 c
+) {
+ if( val < 0 )
+ val = 0;
+ if( val > 99999 )
+ val = 99999;
+
+ I32 nd = 1, v = val;
+ while( v >= 10 ) {
+ nd++;
+ v /= 10;
+ }
+
+ F32 adv = dw * 1.35f;
+ F32 x = cx + ( adv * (F32)nd - ( adv - dw ) ) - dw;
+ v = val;
+ for( I32 i = 0; i < nd; i++ ) {
+ glyph( x, cy - dh * 0.5f, dw, dh, v % 10, c );
+ v /= 10;
+ x -= adv;
+ }
+}
+
+void GAUGE_RENDERER::number_r(
+ F32 cx, F32 cy,
+ F32 dw, F32 dh,
+ I32 val, U32 c
+) {
+ if( val < 0 )
+ val = 0;
+ if( val > 99999 )
+ val = 99999;
+
+ I32 nd = 1, v = val;
+ while( v >= 10 ) {
+ nd++;
+ v /= 10;
+ }
+
+ F32 adv = dw * 1.35f;
+ F32 x = cx;
+ v = val;
+ for( I32 i = 0; i < nd; i++ ) {
+ glyph( x, cy - dh * 0.5f, dw, dh, v % 10, c );
+ v /= 10;
+ x -= adv;
+ }
+}
+
+void GAUGE_RENDERER::number_c(
+ F32 cx, F32 cy,
+ F32 dw, F32 dh,
+ I32 val, U32 c
+) {
+ if( val < 0 )
+ val = 0;
+ if( val > 99999 )
+ val = 99999;
+
+ I32 nd = 1, v = val;
+ while( v >= 10 ) {
+ nd++;
+ v /= 10;
+ }
+
+ F32 adv = dw * 1.35f;
+ F32 x = cx + ( adv * (F32)nd - ( adv - dw ) ) * 0.5f - dw;
+ v = val;
+ for( I32 i = 0; i < nd; i++ ) {
+ glyph( x, cy - dh * 0.5f, dw, dh, v % 10, c );
+ v /= 10;
+ x -= adv;
+ }
+}
+
+void GAUGE_RENDERER::lamp( F32 cx, F32 cy, F32 h, const I32 g[3], U32 c ) {
+ F32 dw = h * 0.62f;
+ F32 adv = dw * 1.35f;
+ F32 x = cx - adv;
+ for( I32 i = 0; i < 3; i++ ) {
+ glyph_c( x, cy, dw, h, g[i], c );
+ x += adv;
+ }
+}
+
+
+void GAUGE_RENDERER::build_tach_bg(
+ F32 cx, F32 cy, F32 r,
+ F32 rpm_max, F32 red_ang, F32 rpm_off
+) {
+ disc_grad( cx, cy, r * 1.10f, C_HALO_IN, C_HALO_OUT );
+ disc_grad( cx, cy, r, C_FACE_MID, C_FACE_EDGE );
+
+ arc( cx, cy, r * 0.20f, r * 0.54f,
+ 40.0f * (F32)M_PI / 180.0f, 270.0f * (F32)M_PI / 180.0f, C_SWIRL );
+ arc( cx, cy, r - 2.0f, r, 0.0f, 2.0f * (F32)M_PI, C_RIM );
+
+ if( rpm_off > 0.f ) {
+ arc( cx, cy, r * 0.84f, r * 0.965f,
+ gauge_angle( red_ang ), gauge_angle( red_ang + rpm_off ), C_REDLINE2 );
+ }
+ arc( cx, cy, r * 0.84f, r * 0.965f,
+ gauge_angle( red_ang + rpm_off ), gauge_angle( 1.0f ), C_REDLINE );
+
+
+ I32 kmax = (I32)( rpm_max / 1000.0f + 0.999f );
+ for( I32 k1000 = 0; k1000 <= kmax; k1000++ ) {
+ F32 t = (F32)k1000 * 1000.0f / rpm_max;
+ if( t > 1.0f )
+ break;
+
+ F32 a = gauge_angle( t );
+ tick( cx, cy, a, r * 0.85f, r - 2.0f, r * 0.020f, C_TICK );
+ F32 lx = cx + cosf( a ) * r * 0.74f;
+ F32 ly = cy - sinf( a ) * r * 0.74f;
+ if( k1000 < 10 )
+ glyph_c( lx, ly, r * 0.09f, r * 0.15f, k1000, C_LABEL );
+ else
+ number_c( lx, ly, r * 0.075f, r * 0.13f, k1000, C_LABEL );
+ }
+
+ for( I32 rr = 250; rr <= (I32)rpm_max; rr += 250 ) {
+ if( rr % 1000 == 0 )
+ continue;
+ F32 t = (F32)rr / rpm_max;
+ if( t > 1.0f )
+ break;
+ tick( cx, cy, gauge_angle( t ),
+ r * 0.87f, r * 0.93f, r * 0.010f, C_TICKMIN );
+ }
+}
+
+void GAUGE_RENDERER::build_tach_gear_speed(
+ F32 cx, F32 cy, F32 r,
+ F32 rpm, F32 rpm_max,
+ I32 gear, F32 speed,
+ F32 red_ang
+) {
+ F32 now = t_nows();
+ U8 flash = fmodf( now, 0.2f ) > 0.1f;
+
+ I32 g = gear == 0 ? G_R : ( gear >= 11 ? G_N : gear );
+ glyph_c( cx + r * 0.45f, cy - r * 0.11f, r * 0.19f, r * 0.29f,
+ 0, C_LAMPBG );
+
+ U32 gearc = C_GEAR;
+ if( gear != G_N && rpm > rpm_max * red_ang && !flash )
+ gearc = rgba( 255, 0, 0, 255 );
+ else if( gear == 0 && flash )
+ gearc = rgba( 0, 255, 255, 255 );
+
+ glyph_c( cx + r * 0.45f, cy - r * 0.11f, r * 0.19f, r * 0.29f,
+ g, gearc );
+ I32 spx = cx + r * 0.37f;
+ I32 spy = cy + r * 0.24f;
+ for( U32 i = 0; i < 3; ++i )
+ number( spx + i * r * 0.13f * 1.35f, spy, r * 0.13f, r * 0.24f, 0, C_LAMPBG );
+ number_r( spx + r * 0.13f * 1.35f * 2, spy, r * 0.13f, r * 0.24f, (I32)( speed + 0.5f ), C_SPEED );
+}
+
+void GAUGE_RENDERER::build_tach(
+ F32 cx, F32 cy, F32 r, F32 trail,
+ F32 rpm, F32 rpm_max, F32 red_ang, F32 rpm_off, F32 peak,
+ I32 gear, F32 speed, U8 have_ext, U8 flash_needle
+) {
+ F32 now = t_nows();
+ U8 flash = fmodf( now, 0.2f ) > 0.1f;
+ if( rpm_max < 100.0f )
+ rpm_max = 8000.0f;
+
+ build_tach_bg( cx, cy, r, rpm_max, red_ang, rpm_off );
+
+ if( have_ext ) {
+ build_tach_gear_speed( cx, cy, r, rpm, rpm_max, gear, speed, red_ang );
+ }
+
+ F32 pt = peak / rpm_max;
+ if( pt > 0.01f && pt <= 1.0f ) {
+ tick( cx, cy, gauge_angle( pt ),
+ r * 0.84f, r * 0.945f, r * 0.016f, C_PEAK );
+ }
+
+ U32 trailc = C_TRAIL;
+ U32 needlec = C_NEEDLE;
+ if( flash_needle ) {
+ if( flash && rpm > rpm_max * red_ang )
+ needlec = trailc = rgba( 255, 0, 0, 255 );
+ else if( !flash && gear == 0 )
+ needlec = trailc = C_LAMP;
+ }
+
+ F32 t = rpm / rpm_max;
+ if( t < 0.0f ) t = 0.0f;
+ if( t > 1.0f ) t = 1.0f;
+ if( fabsf( trail ) > 0.002f ) {
+ F32 t0 = t - trail;
+ if( t0 < 0.0f ) t0 = 0.0f;
+ if( t0 > 1.0f ) t0 = 1.0f;
+ needle_trail( cx, cy, gauge_angle( t0 ), gauge_angle( t ),
+ r * 0.10f, r * 0.82f, trailc );
+ }
+ needle( cx, cy, gauge_angle( t ),
+ r * 0.82f, r * 0.16f, r * 0.05f, needlec );
+ disc( cx, cy, r * 0.065f, C_HUB );
+ disc( cx, cy, r * 0.026f, C_HUBDOT );
+}
+
+
+void GAUGE_RENDERER::build_boost_bg( F32 cx, F32 cy, F32 r, F32 range ) {
+ F32 span = range + 1.0f;
+
+ disc_grad( cx, cy, r * 1.12f, C_HALO_IN, C_HALO_OUT );
+ disc_grad( cx, cy, r, C_FACE_MID, C_FACE_EDGE );
+ arc( cx, cy, r - 2.0f, r, 0.0f, 2.0f * (F32)M_PI, C_RIM );
+
+ arc(
+ cx, cy, r * 0.80f, r * 0.92f,
+ gauge_angle( 0.0f ), gauge_angle( 0.10f / span ),
+ C_REDLINE
+ );
+ arc(
+ cx, cy, r * 0.80f, r * 0.92f,
+ gauge_angle( ( span - 0.25f ) / span ), gauge_angle( 1.0f ),
+ C_REDLINE
+ );
+ arc(
+ cx, cy, r * 0.86f, r * 0.90f,
+ gauge_angle( 0.10f / span ), gauge_angle( 1.0f / span ),
+ C_VACUUM
+ );
+
+ for( I32 q = 0; q <= (I32)( span / 0.25f + 0.5f ); q++ ) {
+ F32 v = -1.0f + 0.25f * (F32)q;
+ F32 t = ( v + 1.0f ) / span;
+ if( t > 1.001f )
+ break;
+
+ U8 whole = fabsf( v - roundf( v ) ) < 0.01f;
+ U8 zero = fabsf( v ) < 0.01f;
+ tick( cx, cy, gauge_angle( t ),
+ whole ? r * 0.74f : r * 0.85f, r - 2.0f,
+ zero ? r * 0.05f : ( whole ? r * 0.03f : r * 0.018f ),
+ zero ? C_ZERO : ( whole ? C_TICK : C_TICKMIN )
+ );
+
+ if( whole ) {
+ F32 a = gauge_angle( t );
+ F32 lx = cx + cosf( a ) * r * 0.58f;
+ F32 ly = cy - sinf( a ) * r * 0.58f;
+ I32 av = (I32)fabsf( roundf( v ) );
+ if( v < -0.01f ) {
+ glyph_c( lx - r * 0.09f, ly, r * 0.09f, r * 0.16f, G_DASH, C_LABEL );
+ glyph_c( lx + r * 0.05f, ly, r * 0.09f, r * 0.16f, av, C_LABEL );
+ } else {
+ glyph_c( lx, ly, r * 0.10f, r * 0.17f, av, C_LABEL );
+ }
+ }
+ }
+}
+
+void GAUGE_RENDERER::build_boost( F32 cx, F32 cy, F32 r, F32 bar, F32 range, F32 trail ) {
+ build_boost_bg( cx, cy, r, range );
+
+ F32 span = range + 1.0f;
+ F32 t = ( bar + 1.0f ) / span;
+ if( fabsf( trail ) > 0.002f ) {
+ F32 t0 = t - trail;
+ if( t0 < 0.0f ) t0 = 0.0f;
+ if( t0 > 1.0f ) t0 = 1.0f;
+ needle_trail( cx, cy, gauge_angle( t0 ), gauge_angle( t ), r * 0.12f, r * 0.76f, C_TRAIL );
+ }
+ needle( cx, cy, gauge_angle( t ), r * 0.78f, r * 0.16f, r * 0.06f, C_NEEDLE );
+ disc( cx, cy, r * 0.075f, C_HUB );
+ disc( cx, cy, r * 0.030f, C_HUBDOT );
+}
+
+void GAUGE_RENDERER::build_lamps(
+ F32 cx, F32 cy, F32 r, F32 h,
+ U8 hb, U8 abs_on, U8 tcs_on, U8 abs_flash, U8 tcs_flash
+) {
+ const I32 ABS[3] = { G_A, G_B, G_S };
+ const I32 TCS[3] = { G_T, G_C, G_S };
+
+ F32 hbx = cx + r * 0.48f;
+ F32 hby = cy + r * 0.53f;
+ U32 col = !hb ? C_LAMPBG : rgba( 255, 0, 0, 255 );
+ arc( hbx, hby, r * 0.1f, r * 0.117f, 0, M_PI * 2.f, col );
+ line( hbx, hby - r * 0.075, hbx, hby + r * 0.04f, r * 0.03, col );
+ line( hbx, hby + r * 0.05, hbx, hby + r * 0.085f, r * 0.03, col );
+
+ if( abs_on )
+ lamp( cx + h * 1.6f, cy + r * 0.35f, h, ABS, C_LAMPBG );
+ if( abs_flash )
+ lamp( cx + h * 1.6f, cy + r * 0.35f, h, ABS, C_LAMP );
+ if( tcs_on )
+ lamp( cx + h * 1.6f, cy + r * 0.35f + h * 1.1f, h, TCS, C_LAMPBG );
+ if( tcs_flash )
+ lamp( cx + h * 1.6f, cy + r * 0.35f + h * 1.1f, h, TCS, C_LAMP );
+}
+
+U32 get_tire_color( F32 temp ) {
+ U32 a = 200;
+ temp -= 32.f;
+ temp /= 1.8f;
+ if( temp < 30.f )
+ return rgba( 0, 0, 255, a );
+ if( temp < 45.f )
+ return rgba( 0, 255, 255, a );
+ if( temp < 75.f )
+ return rgba( 0, 255, 0, a );
+ if( temp < 90.f )
+ return rgba( 0, 170, 0, a );
+ if( temp < 120.f )
+ return rgba( 255, 170, 0, a );
+ if( temp < 150.f )
+ return rgba( 255, 100, 0, a );
+ return rgba( 255, 0, 0, a );
+}
+
+void GAUGE_RENDERER::build_tires( F32 cx, F32 cy, F32 h, F32 fl, F32 fr, F32 rl, F32 rr ) {
+ const F32 tire_w = h * 0.023f;
+ const F32 tire_h = h * 0.037f;
+ const F32 rect_w = tire_w * 0.53f;
+ const F32 rect_h = tire_h * 0.3f;
+
+ line( cx - tire_w, cy - tire_h, cx + tire_w, cy - tire_h, 2, C_TICK );
+ line( cx - tire_w, cy + tire_h, cx + tire_w, cy + tire_h, 2, C_TICK );
+ line( cx, cy - tire_h, cx, cy + tire_h, 2, C_TICK );
+
+ // fl
+ rect( cx - tire_w - rect_w, cy - tire_h - rect_h, rect_w, rect_h * 2.f, get_tire_color( fl ) );
+ line( cx - tire_w, cy - tire_h - rect_h, cx - tire_w, cy - tire_h + rect_h, 2, C_TICK );
+ line( cx - tire_w, cy - tire_h - rect_h, cx - tire_w - rect_w, cy - tire_h - rect_h, 2, C_TICK );
+ line( cx - tire_w, cy - tire_h + rect_h, cx - tire_w - rect_w, cy - tire_h + rect_h, 2, C_TICK );
+ line( cx - tire_w - rect_w, cy - tire_h - rect_h, cx - tire_w - rect_w, cy - tire_h + rect_h, 2, C_TICK );
+
+ // fr
+ rect( cx + tire_w, cy - tire_h - rect_h, rect_w, rect_h * 2.f, get_tire_color( fr ) );
+ line( cx + tire_w, cy - tire_h - rect_h, cx + tire_w, cy - tire_h + rect_h, 2, C_TICK );
+ line( cx + tire_w, cy - tire_h - rect_h, cx + tire_w + rect_w, cy - tire_h - rect_h, 2, C_TICK );
+ line( cx + tire_w, cy - tire_h + rect_h, cx + tire_w + rect_w, cy - tire_h + rect_h, 2, C_TICK );
+ line( cx + tire_w + rect_w, cy - tire_h - rect_h, cx + tire_w + rect_w, cy - tire_h + rect_h, 2, C_TICK );
+
+ // rl
+ rect( cx - tire_w - rect_w, cy + tire_h - rect_h, rect_w, rect_h * 2.f, get_tire_color( rl ) );
+ line( cx - tire_w, cy + tire_h - rect_h, cx - tire_w, cy + tire_h + rect_h, 2, C_TICK );
+ line( cx - tire_w, cy + tire_h - rect_h, cx - tire_w - rect_w, cy + tire_h - rect_h, 2, C_TICK );
+ line( cx - tire_w, cy + tire_h + rect_h, cx - tire_w - rect_w, cy + tire_h + rect_h, 2, C_TICK );
+ line( cx - tire_w - rect_w, cy + tire_h - rect_h, cx - tire_w - rect_w, cy + tire_h + rect_h, 2, C_TICK );
+
+ // rr
+ rect( cx + tire_w, cy + tire_h - rect_h, rect_w, rect_h * 2.f, get_tire_color( rr ) );
+ line( cx + tire_w, cy + tire_h - rect_h, cx + tire_w, cy + tire_h + rect_h, 2, C_TICK );
+ line( cx + tire_w, cy + tire_h - rect_h, cx + tire_w + rect_w, cy + tire_h - rect_h, 2, C_TICK );
+ line( cx + tire_w, cy + tire_h + rect_h, cx + tire_w + rect_w, cy + tire_h + rect_h, 2, C_TICK );
+ line( cx + tire_w + rect_w, cy + tire_h - rect_h, cx + tire_w + rect_w, cy + tire_h + rect_h, 2, C_TICK );
+}