Skip to content

Commit

Permalink
Plotter: add control on how to resolve the axes origin label drawing …
Browse files Browse the repository at this point in the history
…and possibility to override axes text adaptive contrast behaviour (#2)

* allow for selecting which axis has priority when having to resolve a label conflict (i.e.: not displaying the 0 label on both axes)

* add stroke overrides for axes, nasty hack

* comment

* spelling mistakes

* pr feedback

* pr feedback, remove custom colors and use bool to drive adaptive contrast
  • Loading branch information
EmilioLaiso authored and tosti007 committed Sep 28, 2023
1 parent 3563bde commit 6940d48
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 1 deletion.
2 changes: 1 addition & 1 deletion crates/egui_extras/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ pub struct Table<'a> {

/// Returned from [`Table::body`]. Contains information about the way the table has been built by the context.
pub struct TableOutput {
/// The response recieved upon the creation of the table's header, if one was defined.
/// The response received upon the creation of the table's header, if one was defined.
pub header_response: Option<Response>,

/// The output of the [`ScrollArea`] containing the table body's contents.
Expand Down
139 changes: 139 additions & 0 deletions crates/egui_plot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,14 @@ pub struct Plot {
show_y: bool,
label_formatter: LabelFormatter,
coordinates_formatter: Option<(Corner, CoordinatesFormatter)>,
<<<<<<< HEAD:crates/egui_plot/src/lib.rs
x_axes: Vec<AxisHints>, // default x axes
y_axes: Vec<AxisHints>, // default y axes
=======
axis_formatters: [AxisFormatter; 2],
x_axis_origin_label_has_priority: bool,
axis_text_use_adaptive_contrast: [bool; 2],
>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
legend_config: Option<Legend>,
show_background: bool,
show_axes: AxisBools,
Expand Down Expand Up @@ -278,8 +284,14 @@ impl Plot {
show_y: true,
label_formatter: None,
coordinates_formatter: None,
<<<<<<< HEAD:crates/egui_plot/src/lib.rs
x_axes: vec![Default::default()],
y_axes: vec![Default::default()],
=======
axis_formatters: [None, None], // [None; 2] requires Copy
x_axis_origin_label_has_priority: true,
axis_text_use_adaptive_contrast: [true, true],
>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
legend_config: None,
show_background: true,
show_axes: true.into(),
Expand Down Expand Up @@ -446,6 +458,62 @@ impl Plot {
self
}

<<<<<<< HEAD:crates/egui_plot/src/lib.rs
=======
/// Provide a function to customize the labels for the X axis based on the current visible value range.
///
/// 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, &RangeInclusive<f64>) -> String + 'static,
) -> Self {
self.axis_formatters[0] = Some(Box::new(func));
self
}

/// Provide a function to customize the labels for the Y axis based on the current value range.
///
/// 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, &RangeInclusive<f64>) -> String + 'static,
) -> Self {
self.axis_formatters[1] = Some(Box::new(func));
self
}

/// Set the priority of the X axis origin label. Default: `true`.
/// This defines which label has display priority when resolving the overlapping of the origin (0.0) labels.
pub fn x_axis_origin_label_has_priority(mut self, val: bool) -> Self {
self.x_axis_origin_label_has_priority = val;
self
}

/// Overrides the default behaviour of the X axis' text rendering.
/// Always use the ui foreground color instead of calculating the color of the stroke using the `color_from_contrast` calculations,
/// which blends the background and foreground colors according to a defined contrast
pub fn x_axis_text_adaptive_contrast(mut self, val: bool) -> Self {
self.axis_text_use_adaptive_contrast[0] = val;
self
}

/// Overrides the default behaviour of the Y axis' text rendering.
/// Always use the ui foreground color instead of calculating the color of the stroke using the `color_from_contrast` calculations,
/// which blends the background and foreground colors according to a defined contrast
pub fn y_axis_text_adaptive_contrast(mut self, val: bool) -> Self {
self.axis_text_use_adaptive_contrast[1] = val;
self
}

>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
/// Configure how the grid in the background is spaced apart along the X axis.
///
/// Default is a log-10 grid, i.e. every plot unit is divided into 10 other units.
Expand Down Expand Up @@ -719,8 +787,14 @@ impl Plot {
mut show_y,
label_formatter,
coordinates_formatter,
<<<<<<< HEAD:crates/egui_plot/src/lib.rs
x_axes,
y_axes,
=======
axis_formatters,
x_axis_origin_label_has_priority,
axis_text_use_adaptive_contrast,
>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
legend_config,
reset,
show_background,
Expand Down Expand Up @@ -1179,6 +1253,9 @@ impl Plot {
grid_spacers,
sharp_grid_lines,
clamp_grid,

x_axis_origin_label_has_priority,
axis_text_use_adaptive_contrast,
};

let plot_cursors = prepared.ui(ui, &response);
Expand Down Expand Up @@ -1552,7 +1629,11 @@ pub struct GridInput {
}

/// One mark (horizontal or vertical line) in the background grid of a plot.
<<<<<<< HEAD:crates/egui_plot/src/lib.rs
#[derive(Debug, Clone, Copy)]
=======
#[derive(Debug)]
>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
pub struct GridMark {
/// X or Y value in the plot.
pub value: f64,
Expand Down Expand Up @@ -1624,6 +1705,9 @@ struct PreparedPlot {

sharp_grid_lines: bool,
clamp_grid: bool,

x_axis_origin_label_has_priority: bool,
axis_text_use_adaptive_contrast: [bool; 2],
}

impl PreparedPlot {
Expand Down Expand Up @@ -1807,6 +1891,61 @@ impl PreparedPlot {
line_strength,
));
}
<<<<<<< HEAD:crates/egui_plot/src/lib.rs
=======

const MIN_TEXT_SPACING: f32 = 40.0;
if spacing_in_points > MIN_TEXT_SPACING {
let text_strength =
remap_clamp(spacing_in_points, MIN_TEXT_SPACING..=150.0, 0.0..=1.0);

let color = if self.axis_text_use_adaptive_contrast[axis] {
color_from_contrast(ui, text_strength)
} else {
ui.visuals().widgets.open.fg_stroke.color
};

let text: String = if let Some(formatter) = axis_formatters[axis].as_deref() {
formatter(value_main, &axis_range)
} else {
emath::round_to_decimals(value_main, 5).to_string() // hack
};

// Skip origin label for the axis that doesn't have priority, if the axis with priority is already showing it (otherwise it's displayed twice)
let priority_axis_idx = if self.x_axis_origin_label_has_priority {
0
} else {
1
};
let skip_low_priority_origin =
axis != priority_axis_idx && other_axis_shown && value_main == 0.0;

// Custom formatters can return empty string to signal "no label at this resolution"
if !text.is_empty() && !skip_low_priority_origin {
let galley = ui.painter().layout_no_wrap(text, font_id.clone(), color);

let mut text_pos = pos_in_gui + vec2(1.0, -galley.size().y);

// 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), text_strength));
}
}
}

fn color_from_contrast(ui: &Ui, contrast: f32) -> Color32 {
let bg = ui.visuals().extreme_bg_color;
let fg = ui.visuals().widgets.open.fg_stroke.color;
let mix = 0.5 * contrast.sqrt();
Color32::from_rgb(
lerp((bg.r() as f32)..=(fg.r() as f32), mix) as u8,
lerp((bg.g() as f32)..=(fg.g() as f32), mix) as u8,
lerp((bg.b() as f32)..=(fg.b() as f32), mix) as u8,
)
>>>>>>> e76d03b9 (Plotter: add control on how to resolve the axes origin label drawing and possibility to override axes text adaptive contrast behaviour (#2)):crates/egui/src/widgets/plot/mod.rs
}
}

Expand Down

0 comments on commit 6940d48

Please sign in to comment.