1
1
//! Simple plotting library.
2
2
3
+ use std:: { cell:: RefCell , rc:: Rc } ;
4
+
3
5
use crate :: * ;
4
6
use epaint:: ahash:: AHashSet ;
5
7
use epaint:: color:: Hsva ;
@@ -49,6 +51,63 @@ impl PlotMemory {
49
51
50
52
// ----------------------------------------------------------------------------
51
53
54
+ /// Defines how multiple plots share the same range for one or both of their axes. Can be added while building
55
+ /// a plot with [`Plot::link_axis`]. Contains an internal state, meaning that this object should be stored by
56
+ /// the user between frames.
57
+ #[ derive( Clone , PartialEq ) ]
58
+ pub struct LinkedAxisGroup {
59
+ pub ( crate ) link_x : bool ,
60
+ pub ( crate ) link_y : bool ,
61
+ pub ( crate ) bounds : Rc < RefCell < Option < PlotBounds > > > ,
62
+ }
63
+
64
+ impl LinkedAxisGroup {
65
+ pub fn new ( link_x : bool , link_y : bool ) -> Self {
66
+ Self {
67
+ link_x,
68
+ link_y,
69
+ bounds : Rc :: new ( RefCell :: new ( None ) ) ,
70
+ }
71
+ }
72
+
73
+ /// Only link the x-axis.
74
+ pub fn x ( ) -> Self {
75
+ Self :: new ( true , false )
76
+ }
77
+
78
+ /// Only link the y-axis.
79
+ pub fn y ( ) -> Self {
80
+ Self :: new ( false , true )
81
+ }
82
+
83
+ /// Link both axes. Note that this still respects the aspect ratio of the individual plots.
84
+ pub fn both ( ) -> Self {
85
+ Self :: new ( true , true )
86
+ }
87
+
88
+ /// Change whether the x-axis is linked for this group. Using this after plots in this group have been
89
+ /// drawn in this frame already may lead to unexpected results.
90
+ pub fn set_link_x ( & mut self , link : bool ) {
91
+ self . link_x = link;
92
+ }
93
+
94
+ /// Change whether the y-axis is linked for this group. Using this after plots in this group have been
95
+ /// drawn in this frame already may lead to unexpected results.
96
+ pub fn set_link_y ( & mut self , link : bool ) {
97
+ self . link_y = link;
98
+ }
99
+
100
+ fn get ( & self ) -> Option < PlotBounds > {
101
+ * self . bounds . borrow ( )
102
+ }
103
+
104
+ fn set ( & self , bounds : PlotBounds ) {
105
+ * self . bounds . borrow_mut ( ) = Some ( bounds) ;
106
+ }
107
+ }
108
+
109
+ // ----------------------------------------------------------------------------
110
+
52
111
/// A 2D plot, e.g. a graph of a function.
53
112
///
54
113
/// `Plot` supports multiple lines and points.
@@ -73,6 +132,7 @@ pub struct Plot {
73
132
allow_drag : bool ,
74
133
min_auto_bounds : PlotBounds ,
75
134
margin_fraction : Vec2 ,
135
+ linked_axes : Option < LinkedAxisGroup > ,
76
136
77
137
min_size : Vec2 ,
78
138
width : Option < f32 > ,
@@ -101,6 +161,7 @@ impl Plot {
101
161
allow_drag : true ,
102
162
min_auto_bounds : PlotBounds :: NOTHING ,
103
163
margin_fraction : Vec2 :: splat ( 0.05 ) ,
164
+ linked_axes : None ,
104
165
105
166
min_size : Vec2 :: splat ( 64.0 ) ,
106
167
width : None ,
@@ -281,6 +342,13 @@ impl Plot {
281
342
self
282
343
}
283
344
345
+ /// Add a [`LinkedAxisGroup`] so that this plot will share the bounds with other plots that have this
346
+ /// group assigned. A plot cannot belong to more than one group.
347
+ pub fn link_axis ( mut self , group : LinkedAxisGroup ) -> Self {
348
+ self . linked_axes = Some ( group) ;
349
+ self
350
+ }
351
+
284
352
/// Interact with and add items to the plot and finally draw it.
285
353
pub fn show < R > ( self , ui : & mut Ui , build_fn : impl FnOnce ( & mut PlotUi ) -> R ) -> InnerResponse < R > {
286
354
let Self {
@@ -303,6 +371,7 @@ impl Plot {
303
371
legend_config,
304
372
show_background,
305
373
show_axes,
374
+ linked_axes,
306
375
} = self ;
307
376
308
377
// Determine the size of the plot in the UI
@@ -415,6 +484,22 @@ impl Plot {
415
484
// --- Bound computation ---
416
485
let mut bounds = * last_screen_transform. bounds ( ) ;
417
486
487
+ // Transfer the bounds from a link group.
488
+ if let Some ( axes) = linked_axes. as_ref ( ) {
489
+ if let Some ( linked_bounds) = axes. get ( ) {
490
+ if axes. link_x {
491
+ bounds. min [ 0 ] = linked_bounds. min [ 0 ] ;
492
+ bounds. max [ 0 ] = linked_bounds. max [ 0 ] ;
493
+ }
494
+ if axes. link_y {
495
+ bounds. min [ 1 ] = linked_bounds. min [ 1 ] ;
496
+ bounds. max [ 1 ] = linked_bounds. max [ 1 ] ;
497
+ }
498
+ // Turn off auto bounds to keep it from overriding what we just set.
499
+ auto_bounds = false ;
500
+ }
501
+ }
502
+
418
503
// Allow double clicking to reset to automatic bounds.
419
504
auto_bounds |= response. double_clicked_by ( PointerButton :: Primary ) ;
420
505
@@ -431,7 +516,10 @@ impl Plot {
431
516
432
517
// Enforce equal aspect ratio.
433
518
if let Some ( data_aspect) = data_aspect {
434
- transform. set_aspect ( data_aspect as f64 ) ;
519
+ let preserve_y = linked_axes
520
+ . as_ref ( )
521
+ . map_or ( false , |group| group. link_y && !group. link_x ) ;
522
+ transform. set_aspect ( data_aspect as f64 , preserve_y) ;
435
523
}
436
524
437
525
// Dragging
@@ -484,6 +572,10 @@ impl Plot {
484
572
hovered_entry = legend. get_hovered_entry_name ( ) ;
485
573
}
486
574
575
+ if let Some ( group) = linked_axes. as_ref ( ) {
576
+ group. set ( * transform. bounds ( ) ) ;
577
+ }
578
+
487
579
let memory = PlotMemory {
488
580
auto_bounds,
489
581
hovered_entry,
@@ -504,7 +596,7 @@ impl Plot {
504
596
}
505
597
506
598
/// Provides methods to interact with a plot while building it. It is the single argument of the closure
507
- /// provided to `Plot::show`. See [`Plot`] for an example of how to use it.
599
+ /// provided to [ `Plot::show`] . See [`Plot`] for an example of how to use it.
508
600
pub struct PlotUi {
509
601
items : Vec < Box < dyn PlotItem > > ,
510
602
next_auto_color_idx : usize ,
0 commit comments