@ -10,7 +10,7 @@ use transform::{PlotBounds, ScreenTransform};
pub use items ::{
Arrows , Bar , BarChart , BoxElem , BoxPlot , BoxSpread , HLine , Line , LineStyle , MarkerShape ,
PlotImage , Points , Polygon , Text , VLine , Value , Values ,
Orientation , PlotImage , Points , Polygon , Text , VLine , Value , Values ,
} ;
pub use legend ::{ Corner , Legend } ;
@ -21,6 +21,9 @@ mod transform;
type CustomLabelFunc = dyn Fn ( & str , & Value ) -> String ;
type CustomLabelFuncRef = Option < Box < CustomLabelFunc > > ;
type AxisFormatterFn = dyn Fn ( f64 ) -> String ;
type AxisFormatter = Option < Box < AxisFormatterFn > > ;
// ----------------------------------------------------------------------------
/// Information about the plot that has to persist between frames.
@ -80,6 +83,7 @@ pub struct Plot {
show_x : bool ,
show_y : bool ,
custom_label_func : CustomLabelFuncRef ,
axis_formatters : [ AxisFormatter ; 2 ] ,
legend_config : Option < Legend > ,
show_background : bool ,
show_axes : [ bool ; 2 ] ,
@ -107,6 +111,7 @@ impl Plot {
show_x : true ,
show_y : true ,
custom_label_func : None ,
axis_formatters : [ None , None ] , // [None; 2] requires Copy
legend_config : None ,
show_background : true ,
show_axes : [ true ; 2 ] ,
@ -208,11 +213,35 @@ impl Plot {
/// .show(ui, |plot_ui| plot_ui.line(line));
/// # });
/// ```
pub fn custom_label_func < F : 'static + Fn ( & str , & Value ) -> String > (
pub fn custom_label_func (
mut self ,
custom_lebel_func : F ,
custom_label_func : impl Fn ( & str , & Value ) -> String + 'static ,
) -> Self {
self . custom_label_func = Some ( Box ::new ( custom_lebel_func ) ) ;
self . custom_label_func = Some ( Box ::new ( custom_label_func ) ) ;
self
}
/// Provide a function to customize the labels for the X axis.
///
/// This is useful for custom input domains, e.g. date/time.
///
/// If axis labels should not appear for certain values or beyond a certain zoom/resolution,
/// the formatter function can return empty strings. This is also useful if your domain is
/// discrete (e.g. only full days in a calendar).
pub fn x_axis_formatter ( mut self , func : impl Fn ( f64 ) -> String + 'static ) -> Self {
self . axis_formatters [ 0 ] = Some ( Box ::new ( func ) ) ;
self
}
/// Provide a function to customize the labels for the Y axis.
///
/// This is useful for custom value representation, e.g. percentage or units.
///
/// If axis labels should not appear for certain values or beyond a certain zoom/resolution,
/// the formatter function can return empty strings. This is also useful if your Y values are
/// discrete (e.g. only integers).
pub fn y_axis_formatter ( mut self , func : impl Fn ( f64 ) -> String + 'static ) -> Self {
self . axis_formatters [ 1 ] = Some ( Box ::new ( func ) ) ;
self
}
@ -270,6 +299,7 @@ impl Plot {
mut show_x ,
mut show_y ,
custom_label_func ,
axis_formatters ,
legend_config ,
show_background ,
show_axes ,
@ -442,6 +472,7 @@ impl Plot {
show_x ,
show_y ,
custom_label_func ,
axis_formatters ,
show_axes ,
transform : transform . clone ( ) ,
} ;
@ -650,6 +681,7 @@ struct PreparedPlot {
show_x : bool ,
show_y : bool ,
custom_label_func : CustomLabelFuncRef ,
axis_formatters : [ AxisFormatter ; 2 ] ,
show_axes : [ bool ; 2 ] ,
transform : ScreenTransform ,
}
@ -680,7 +712,11 @@ impl PreparedPlot {
}
fn paint_axis ( & self , ui : & Ui , axis : usize , shapes : & mut Vec < Shape > ) {
let Self { transform , . . } = self ;
let Self {
transform ,
axis_formatters ,
. .
} = self ;
let bounds = transform . bounds ( ) ;
@ -740,18 +776,26 @@ impl PreparedPlot {
if text_alpha > 0.0 {
let color = color_from_alpha ( ui , text_alpha ) ;
let text = emath ::round_to_decimals ( value_main , 5 ) . to_string ( ) ; // hack
let galley = ui . painter ( ) . layout_no_wrap ( text , font_id . clone ( ) , color ) ;
let text : String = if let Some ( formatter ) = axis_formatters [ axis ] . as_deref ( ) {
formatter ( value_main )
} else {
emath ::round_to_decimals ( value_main , 5 ) . to_string ( ) // hack
} ;
let mut text_pos = pos_in_gui + vec2 ( 1.0 , - galley . size ( ) . y ) ;
// Custom formatters can return empty string to signal "no label at this resolution"
if ! text . is_empty ( ) {
let galley = ui . painter ( ) . layout_no_wrap ( text , font_id . clone ( ) , color ) ;
// Make sure we see the labels, even if the axis is off-screen:
text_pos [ 1 - axis ] = text_pos [ 1 - axis ]
. at_most ( transform . frame ( ) . max [ 1 - axis ] - galley . size ( ) [ 1 - axis ] - 2.0 )
. at_least ( transform . frame ( ) . min [ 1 - axis ] + 1.0 ) ;
let mut text_pos = pos_in_gui + vec2 ( 1.0 , - galley . size ( ) . y ) ;
shapes . push ( Shape ::galley ( text_pos , galley ) ) ;
// Make sure we see the labels, even if the axis is off-screen:
text_pos [ 1 - axis ] = text_pos [ 1 - axis ]
. at_most ( transform . frame ( ) . max [ 1 - axis ] - galley . size ( ) [ 1 - axis ] - 2.0 )
. at_least ( transform . frame ( ) . min [ 1 - axis ] + 1.0 ) ;
shapes . push ( Shape ::galley ( text_pos , galley ) ) ;
}
}
}