@ -1,14 +1,14 @@
use std ::{ fmt ::Debug , ops ::RangeInclusive , sync ::Arc } ;
use std ::{ fmt ::Debug , ops ::RangeInclusive , sync ::Arc } ;
use egui ::{
use egui ::{
emath ::{ remap_clamp , round_to_decimals , Rot2 } ,
emath ::{ remap_clamp , Rot2 } ,
epaint ::TextShape ,
epaint ::TextShape ,
Pos2 , Rangef , Rect , Response , Sense , TextStyle , TextWrapMode , Ui , Vec2 , WidgetText ,
Pos2 , Rangef , Rect , Response , Sense , TextStyle , TextWrapMode , Ui , Vec2 , WidgetText ,
} ;
} ;
use super ::{ transform ::PlotTransform , GridMark } ;
use super ::{ transform ::PlotTransform , GridMark } ;
pub ( super ) type AxisFormatterFn < 'a > = dyn Fn ( GridMark , usize , & RangeInclusive < f64 > ) -> String + 'a ;
pub ( super ) type AxisFormatterFn < 'a > = dyn Fn ( GridMark , & RangeInclusive < f64 > ) -> String + 'a ;
/// X or Y axis.
/// X or Y axis.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
@ -101,7 +101,7 @@ impl From<Placement> for VPlacement {
pub struct AxisHints < 'a > {
pub struct AxisHints < 'a > {
pub ( super ) label : WidgetText ,
pub ( super ) label : WidgetText ,
pub ( super ) formatter : Arc < AxisFormatterFn < 'a > > ,
pub ( super ) formatter : Arc < AxisFormatterFn < 'a > > ,
pub ( super ) digits : usize ,
pub ( super ) min_thickness : f32 ,
pub ( super ) placement : Placement ,
pub ( super ) placement : Placement ,
pub ( super ) label_spacing : Rangef ,
pub ( super ) label_spacing : Rangef ,
}
}
@ -124,12 +124,11 @@ impl<'a> AxisHints<'a> {
///
///
/// `label` is empty.
/// `label` is empty.
/// `formatter` is default float to string formatter.
/// `formatter` is default float to string formatter.
/// maximum `digits` on tick label is 5.
pub fn new ( axis : Axis ) -> Self {
pub fn new ( axis : Axis ) -> Self {
Self {
Self {
label : Default ::default ( ) ,
label : Default ::default ( ) ,
formatter : Arc ::new ( Self ::default_formatter ) ,
formatter : Arc ::new ( Self ::default_formatter ) ,
digits : 5 ,
min_thickness : 14.0 ,
placement : Placement ::LeftBottom ,
placement : Placement ::LeftBottom ,
label_spacing : match axis {
label_spacing : match axis {
Axis ::X = > Rangef ::new ( 60.0 , 80.0 ) , // labels can get pretty wide
Axis ::X = > Rangef ::new ( 60.0 , 80.0 ) , // labels can get pretty wide
@ -141,32 +140,20 @@ impl<'a> AxisHints<'a> {
/// Specify custom formatter for ticks.
/// Specify custom formatter for ticks.
///
///
/// The first parameter of `formatter` is the raw tick value as `f64`.
/// The first parameter of `formatter` is the raw tick value as `f64`.
/// The second parameter is the maximum number of characters that fit into y-labels.
/// The second parameter of `formatter` is the currently shown range on this axis.
/// The second parameter of `formatter` is the currently shown range on this axis.
pub fn formatter (
pub fn formatter (
mut self ,
mut self ,
fmt : impl Fn ( GridMark , usize , & RangeInclusive < f64 > ) -> String + 'a ,
fmt : impl Fn ( GridMark , & RangeInclusive < f64 > ) -> String + 'a ,
) -> Self {
) -> Self {
self . formatter = Arc ::new ( fmt ) ;
self . formatter = Arc ::new ( fmt ) ;
self
self
}
}
fn default_formatter (
fn default_formatter ( mark : GridMark , _range : & RangeInclusive < f64 > ) -> String {
mark : GridMark ,
// Example: If the step to the next tick is `0.01`, we should use 2 decimals of precision:
max_digits : usize ,
let num_decimals = - mark . step_size . log10 ( ) . round ( ) as usize ;
_range : & RangeInclusive < f64 > ,
) -> String {
let tick = mark . value ;
if tick . abs ( ) > 10.0_ f64 . powf ( max_digits as f64 ) {
emath ::format_with_decimals_in_range ( mark . value , num_decimals . . = num_decimals )
let tick_rounded = tick as isize ;
return format ! ( "{tick_rounded:+e}" ) ;
}
let tick_rounded = round_to_decimals ( tick , max_digits ) ;
if tick . abs ( ) < 10.0_ f64 . powf ( - ( max_digits as f64 ) ) & & tick ! = 0.0 {
return format ! ( "{tick_rounded:+e}" ) ;
}
tick_rounded . to_string ( )
}
}
/// Specify axis label.
/// Specify axis label.
@ -178,15 +165,20 @@ impl<'a> AxisHints<'a> {
self
self
}
}
/// Specify maximum number of digits for ticks.
/// Specify minimum thickness of the axis
///
/// This is considered by the default tick formatter and affects the width of the y-axis
#[ inline ]
#[ inline ]
pub fn max_digit s ( mut self , digits : usize ) -> Self {
pub fn min_thickness ( mut self , min_thickness : f32 ) -> Self {
self . digits = digit s;
self . min_thickness = min_thicknes s;
self
self
}
}
/// Specify maximum number of digits for ticks.
#[ inline ]
#[ deprecated = " Use `min_thickness` instead " ]
pub fn max_digits ( self , digits : usize ) -> Self {
self . min_thickness ( 12.0 * digits as f32 )
}
/// Specify the placement of the axis.
/// Specify the placement of the axis.
///
///
/// For X-axis, use [`VPlacement`].
/// For X-axis, use [`VPlacement`].
@ -211,18 +203,17 @@ impl<'a> AxisHints<'a> {
pub ( super ) fn thickness ( & self , axis : Axis ) -> f32 {
pub ( super ) fn thickness ( & self , axis : Axis ) -> f32 {
match axis {
match axis {
Axis ::X = > {
Axis ::X = > self . min_thickness . max ( if self . label . is_empty ( ) {
if self . label . is_empty ( ) {
1.0 * LINE_HEIGHT
1.0 * LINE_HEIGHT
} else {
} else {
3.0 * LINE_HEIGHT
3.0 * LINE_HEIGHT
}
} ) ,
}
Axis ::Y = > {
Axis ::Y = > {
if self . label . is_empty ( ) {
self . min_thickness
( self . digits as f32 ) * LINE_HEIGHT
+ if self . label . is_empty ( ) {
0.0
} else {
} else {
( self . digits as f32 + 1.0 ) * LINE_HEIGHT
LINE_HEIGHT
}
}
}
}
}
}
@ -328,7 +319,7 @@ impl<'a> AxisWidget<'a> {
// Add tick labels:
// Add tick labels:
for step in self . steps . iter ( ) {
for step in self . steps . iter ( ) {
let text = ( self . hints . formatter ) ( * step , self . hints . digits , & self . range ) ;
let text = ( self . hints . formatter ) ( * step , & self . range ) ;
if ! text . is_empty ( ) {
if ! text . is_empty ( ) {
let spacing_in_points =
let spacing_in_points =
( transform . dpos_dvalue ( ) [ usize ::from ( axis ) ] * step . step_size ) . abs ( ) as f32 ;
( transform . dpos_dvalue ( ) [ usize ::from ( axis ) ] * step . step_size ) . abs ( ) as f32 ;