@@ -200,6 +200,8 @@ impl eframe::App for SimTraceApp {
200200 } else {
201201 None
202202 } ;
203+ // Clone the reference lap so the closure can read it without borrowing self.
204+ let reference_lap = self . lap_store . reference_lap . clone ( ) ;
203205
204206 ctx. send_viewport_cmd ( egui:: ViewportCommand :: MinInnerSize ( egui:: vec2 (
205207 MIN_WIDTH , MIN_HEIGHT ,
@@ -447,6 +449,7 @@ impl eframe::App for SimTraceApp {
447449 self . max_steering_angle ,
448450 a,
449451 cap_r,
452+ reference_lap. as_deref ( ) ,
450453 ) ;
451454 } else {
452455 let font_size = ( content_rect. height ( ) * 0.28 ) . clamp ( 14.0 , 42.0 ) ;
@@ -667,6 +670,7 @@ fn draw_telemetry(
667670 max_steering_angle : f32 ,
668671 a : u8 ,
669672 cap_r : f32 ,
673+ reference_lap : Option < & [ crate :: core:: lap_store:: LapPoint ] > ,
670674) {
671675 let opacity = settings. overlay . opacity ;
672676 let available = ui. available_rect_before_wrap ( ) ;
@@ -899,6 +903,7 @@ fn draw_telemetry(
899903 & settings. graph ,
900904 colors,
901905 a,
906+ reference_lap,
902907 ) ;
903908 }
904909
@@ -924,6 +929,7 @@ fn draw_track_strip(
924929 settings : & crate :: config:: GraphSettings ,
925930 colors : & ParsedColors ,
926931 a : u8 ,
932+ reference_lap : Option < & [ crate :: core:: lap_store:: LapPoint ] > ,
927933) {
928934 // Background
929935 painter. rect_filled (
@@ -938,42 +944,108 @@ fn draw_track_strip(
938944 egui:: StrokeKind :: Middle ,
939945 ) ;
940946
941- // Brake zone blips from recent history.
947+ // ── Reference lap ghost ─────────────────────────────────────────────────
948+ // Bin reference points by pixel column and take the max brake/throttle
949+ // per column, which avoids aliasing when many points map to the same pixel.
950+ if let Some ( ref_lap) = reference_lap {
951+ let w = rect. width ( ) . max ( 1.0 ) as usize ;
952+ let mut brake_bins = vec ! [ 0.0_f32 ; w] ;
953+ let mut throttle_bins = vec ! [ 0.0_f32 ; w] ;
954+
955+ for pt in ref_lap {
956+ let bin = ( ( pt. track_position . clamp ( 0.0 , 1.0 ) * w as f32 ) as usize ) . min ( w - 1 ) ;
957+ brake_bins[ bin] = brake_bins[ bin] . max ( pt. brake ) ;
958+ throttle_bins[ bin] = throttle_bins[ bin] . max ( pt. throttle ) ;
959+ }
960+
961+ let inner_h = rect. height ( ) - 2.0 ;
962+ let ghost_opacity = a as f32 * 0.38 ; // dim — reference sits behind live data
963+
964+ let [ br, bg, bb, _] = colors. brake . to_array ( ) ;
965+ let [ tr, tg, tb, _] = colors. throttle . to_array ( ) ;
966+
967+ for col in 0 ..w {
968+ let x = rect. min . x + col as f32 + 0.5 ;
969+
970+ // Brake: from bottom up
971+ if brake_bins[ col] > 0.02 {
972+ let h = ( brake_bins[ col] * inner_h) . max ( 1.0 ) ;
973+ let alpha = ( ghost_opacity * brake_bins[ col] ) . min ( 255.0 ) as u8 ;
974+ painter. line_segment (
975+ [
976+ egui:: pos2 ( x, rect. max . y - 1.0 ) ,
977+ egui:: pos2 ( x, rect. max . y - 1.0 - h) ,
978+ ] ,
979+ egui:: Stroke :: new ( 1.0 , egui:: Color32 :: from_rgba_unmultiplied ( br, bg, bb, alpha) ) ,
980+ ) ;
981+ }
982+
983+ // Throttle: from top down
984+ if throttle_bins[ col] > 0.02 {
985+ let h = ( throttle_bins[ col] * inner_h) . max ( 1.0 ) ;
986+ let alpha = ( ghost_opacity * throttle_bins[ col] ) . min ( 255.0 ) as u8 ;
987+ painter. line_segment (
988+ [
989+ egui:: pos2 ( x, rect. min . y + 1.0 ) ,
990+ egui:: pos2 ( x, rect. min . y + 1.0 + h) ,
991+ ] ,
992+ egui:: Stroke :: new ( 1.0 , egui:: Color32 :: from_rgba_unmultiplied ( tr, tg, tb, alpha) ) ,
993+ ) ;
994+ }
995+ }
996+ }
997+
998+ // Live brake and throttle blips from recent history.
942999 if let Some ( buf) = buffer {
9431000 let points = buf. get_points ( ) ;
9441001 let now = std:: time:: Instant :: now ( ) ;
9451002 let window_secs = settings. window_seconds as f32 ;
9461003 let window_dur = std:: time:: Duration :: from_secs_f64 ( settings. window_seconds ) ;
1004+ let inner_h = rect. height ( ) - 2.0 ;
9471005
9481006 for pt in points
9491007 . iter ( )
9501008 . filter ( |p| now. duration_since ( p. captured_at ) <= window_dur)
9511009 {
952- if pt. telemetry . brake < 0.05 {
953- continue ;
954- }
9551010 let age = now. duration_since ( pt. captured_at ) . as_secs_f32 ( ) ;
9561011 let freshness = ( 1.0 - age / window_secs) . clamp ( 0.0 , 1.0 ) ;
957- let intensity = pt. telemetry . brake * freshness;
958- if intensity < 0.02 {
959- continue ;
1012+ let x = rect. min . x + pt. telemetry . track_position * rect. width ( ) ;
1013+
1014+ // Brake: from bottom up
1015+ let brake_intensity = pt. telemetry . brake * freshness;
1016+ if brake_intensity > 0.02 {
1017+ let color = if pt. abs_active && settings. show_abs {
1018+ colors. abs_active
1019+ } else {
1020+ colors. brake
1021+ } ;
1022+ let [ r, g, b, ca] = color. to_array ( ) ;
1023+ let alpha = ( ( ca as f32 ) * ( a as f32 / 255.0 ) * brake_intensity) . min ( 255.0 ) as u8 ;
1024+ let h = ( pt. telemetry . brake * inner_h) . max ( 1.0 ) ;
1025+ painter. line_segment (
1026+ [
1027+ egui:: pos2 ( x, rect. max . y - 1.0 ) ,
1028+ egui:: pos2 ( x, rect. max . y - 1.0 - h) ,
1029+ ] ,
1030+ egui:: Stroke :: new ( 1.5 , egui:: Color32 :: from_rgba_unmultiplied ( r, g, b, alpha) ) ,
1031+ ) ;
9601032 }
9611033
962- let x = rect . min . x + pt . telemetry . track_position * rect . width ( ) ;
963- let color = if pt. abs_active && settings . show_abs {
964- colors . abs_active
965- } else {
966- colors . brake
967- } ;
968- let [ r , g , b , ca ] = color . to_array ( ) ;
969- let alpha = ( ( ca as f32 ) * ( a as f32 / 255.0 ) * intensity ) . min ( 255.0 ) as u8 ;
970- painter . line_segment (
971- [
972- egui:: pos2 ( x, rect. min . y + 1.5 ) ,
973- egui :: pos2 ( x , rect . max . y - 1.5 ) ,
974- ] ,
975- egui :: Stroke :: new ( 1.5 , egui :: Color32 :: from_rgba_unmultiplied ( r , g , b , alpha ) ) ,
976- ) ;
1034+ // Throttle: from top down
1035+ let throttle_intensity = pt. telemetry . throttle * freshness ;
1036+ if throttle_intensity > 0.02 {
1037+ let [ r , g , b , ca ] = colors . throttle . to_array ( ) ;
1038+ let alpha =
1039+ ( ( ca as f32 ) * ( a as f32 / 255.0 ) * throttle_intensity ) . min ( 255.0 ) as u8 ;
1040+ let h = ( pt . telemetry . throttle * inner_h ) . max ( 1.0 ) ;
1041+ painter . line_segment (
1042+ [
1043+ egui :: pos2 ( x , rect . min . y + 1.0 ) ,
1044+ egui:: pos2 ( x, rect. min . y + 1.0 + h ) ,
1045+ ] ,
1046+ egui :: Stroke :: new ( 1.5 , egui :: Color32 :: from_rgba_unmultiplied ( r , g , b , alpha ) ) ,
1047+ ) ;
1048+ }
9771049 }
9781050 }
9791051
0 commit comments