1+ use std:: collections:: HashMap ;
12use std:: ops:: RangeInclusive ;
23use std:: { f64:: consts:: TAU , sync:: Arc } ;
34
5+ use egui:: mutex:: Mutex ;
46use egui:: {
5- Checkbox , Color32 , ComboBox , NumExt as _, Pos2 , Response , ScrollArea , Stroke , TextWrapMode ,
7+ Checkbox , Color32 , ComboBox , Id , NumExt as _, Pos2 , Response , ScrollArea , Stroke , TextWrapMode ,
68 Vec2b , WidgetInfo , WidgetType , remap, vec2,
79} ;
810
@@ -24,6 +26,7 @@ enum Panel {
2426 Interaction ,
2527 CustomAxes ,
2628 LinkedAxes ,
29+ Userdata ,
2730}
2831
2932impl Default for Panel {
@@ -44,6 +47,7 @@ pub struct PlotDemo {
4447 interaction_demo : InteractionDemo ,
4548 custom_axes_demo : CustomAxesDemo ,
4649 linked_axes_demo : LinkedAxesDemo ,
50+ userdata_demo : UserdataDemo ,
4751 open_panel : Panel ,
4852}
4953
@@ -124,6 +128,7 @@ impl PlotDemo {
124128 ui. selectable_value ( & mut self . open_panel , Panel :: Interaction , "Interaction" ) ;
125129 ui. selectable_value ( & mut self . open_panel , Panel :: CustomAxes , "Custom Axes" ) ;
126130 ui. selectable_value ( & mut self . open_panel , Panel :: LinkedAxes , "Linked Axes" ) ;
131+ ui. selectable_value ( & mut self . open_panel , Panel :: Userdata , "Userdata" ) ;
127132 } ) ;
128133 ui. separator ( ) ;
129134
@@ -152,6 +157,9 @@ impl PlotDemo {
152157 Panel :: LinkedAxes => {
153158 self . linked_axes_demo . ui ( ui) ;
154159 }
160+ Panel :: Userdata => {
161+ self . userdata_demo . ui ( ui) ;
162+ }
155163 }
156164 }
157165}
@@ -642,7 +650,7 @@ impl CustomAxesDemo {
642650 }
643651 } ;
644652
645- let label_fmt = |_s : & str , val : & PlotPoint | {
653+ let label_fmt = |_s : & str , val : & PlotPoint , _ | {
646654 format ! (
647655 "Day {d}, {h}:{m:02}\n {p:.2}%" ,
648656 d = day( val. x) ,
@@ -1197,6 +1205,145 @@ impl ChartsDemo {
11971205 }
11981206}
11991207
1208+ #[ derive( PartialEq , serde:: Deserialize , serde:: Serialize , Default ) ]
1209+ struct UserdataDemo { }
1210+
1211+ #[ derive( Clone ) ]
1212+ struct DemoPoint {
1213+ x : f64 ,
1214+ y : f64 ,
1215+ custom_label : String ,
1216+ importance : f32 ,
1217+ }
1218+
1219+ impl UserdataDemo {
1220+ #[ expect( clippy:: unused_self, clippy:: significant_drop_tightening) ]
1221+ fn ui ( & self , ui : & mut egui:: Ui ) -> Response {
1222+ ui. label (
1223+ "This demo shows how to attach custom data to plot items and display it in tooltips." ,
1224+ ) ;
1225+ ui. separator ( ) ;
1226+
1227+ // Create multiple datasets with custom metadata
1228+ let sine_points = ( 0 ..=500 )
1229+ . map ( |i| {
1230+ let x = i as f64 / 100.0 ;
1231+ DemoPoint {
1232+ x,
1233+ y : x. sin ( ) ,
1234+ custom_label : format ! ( "Sine #{i}" ) ,
1235+ importance : ( i % 100 ) as f32 / 100.0 ,
1236+ }
1237+ } )
1238+ . collect :: < Vec < _ > > ( ) ;
1239+
1240+ let cosine_points = ( 0 ..=500 )
1241+ . map ( |i| {
1242+ let x = i as f64 / 100.0 ;
1243+ DemoPoint {
1244+ x,
1245+ y : x. cos ( ) ,
1246+ custom_label : format ! ( "Cosine #{i}" ) ,
1247+ importance : ( 1.0 - ( i % 100 ) as f32 / 100.0 ) ,
1248+ }
1249+ } )
1250+ . collect :: < Vec < _ > > ( ) ;
1251+
1252+ let damped_points = ( 0 ..=500 )
1253+ . map ( |i| {
1254+ let x = i as f64 / 100.0 ;
1255+ DemoPoint {
1256+ x,
1257+ y : ( -x * 0.5 ) . exp ( ) * ( 2.0 * x) . sin ( ) ,
1258+ custom_label : format ! ( "Damped #{i}" ) ,
1259+ importance : if i % 50 == 0 { 1.0 } else { 0.3 } ,
1260+ }
1261+ } )
1262+ . collect :: < Vec < _ > > ( ) ;
1263+
1264+ // Store custom data in a shared map
1265+ let custom_data = Arc :: new ( Mutex :: new ( HashMap :: < Id , Vec < DemoPoint > > :: new ( ) ) ) ;
1266+
1267+ let custom_data_ = custom_data. clone ( ) ;
1268+ Plot :: new ( "Userdata Plot Demo" )
1269+ . legend ( Legend :: default ( ) . position ( Corner :: LeftTop ) )
1270+ . label_formatter ( move |name, value, item| {
1271+ if let Some ( ( id, index) ) = item {
1272+ let lock = custom_data_. lock ( ) ;
1273+ if let Some ( points) = lock. get ( & id) {
1274+ if let Some ( point) = points. get ( index) {
1275+ return format ! (
1276+ "{}\n Position: ({:.3}, {:.3})\n Label: {}\n Importance: {:.1}%" ,
1277+ name,
1278+ value. x,
1279+ value. y,
1280+ point. custom_label,
1281+ point. importance * 100.0
1282+ ) ;
1283+ }
1284+ }
1285+ }
1286+ format ! ( "{}\n ({:.3}, {:.3})" , name, value. x, value. y)
1287+ } )
1288+ . show ( ui, |plot_ui| {
1289+ let mut lock = custom_data. lock ( ) ;
1290+
1291+ // Sine wave with custom data
1292+ let sine_id = Id :: new ( "sine_wave" ) ;
1293+ lock. insert ( sine_id, sine_points. clone ( ) ) ;
1294+ plot_ui. line (
1295+ Line :: new (
1296+ "sin(x)" ,
1297+ sine_points. iter ( ) . map ( |p| [ p. x , p. y ] ) . collect :: < Vec < _ > > ( ) ,
1298+ )
1299+ . id ( sine_id)
1300+ . color ( Color32 :: from_rgb ( 200 , 100 , 100 ) ) ,
1301+ ) ;
1302+
1303+ // Cosine wave with custom data
1304+ let cosine_id = Id :: new ( "cosine_wave" ) ;
1305+ lock. insert ( cosine_id, cosine_points. clone ( ) ) ;
1306+ plot_ui. line (
1307+ Line :: new (
1308+ "cos(x)" ,
1309+ cosine_points. iter ( ) . map ( |p| [ p. x , p. y ] ) . collect :: < Vec < _ > > ( ) ,
1310+ )
1311+ . id ( cosine_id)
1312+ . color ( Color32 :: from_rgb ( 100 , 200 , 100 ) ) ,
1313+ ) ;
1314+
1315+ // Damped sine wave with custom data
1316+ let damped_id = Id :: new ( "damped_wave" ) ;
1317+ lock. insert ( damped_id, damped_points. clone ( ) ) ;
1318+ plot_ui. line (
1319+ Line :: new (
1320+ "e^(-0.5x) · sin(2x)" ,
1321+ damped_points. iter ( ) . map ( |p| [ p. x , p. y ] ) . collect :: < Vec < _ > > ( ) ,
1322+ )
1323+ . id ( damped_id)
1324+ . color ( Color32 :: from_rgb ( 100 , 100 , 200 ) ) ,
1325+ ) ;
1326+
1327+ // Add some points with high importance as markers
1328+ let important_points: Vec < _ > = damped_points
1329+ . iter ( )
1330+ . filter ( |p| p. importance > 0.9 )
1331+ . map ( |p| [ p. x , p. y ] )
1332+ . collect ( ) ;
1333+
1334+ if !important_points. is_empty ( ) {
1335+ plot_ui. points (
1336+ Points :: new ( "Important Points" , important_points)
1337+ . color ( Color32 :: from_rgb ( 255 , 150 , 0 ) )
1338+ . radius ( 4.0 )
1339+ . shape ( MarkerShape :: Diamond ) ,
1340+ ) ;
1341+ }
1342+ } )
1343+ . response
1344+ }
1345+ }
1346+
12001347fn is_approx_zero ( val : f64 ) -> bool {
12011348 val. abs ( ) < 1e-6
12021349}
0 commit comments