mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
Break out plotting to own crate egui_plot (#3282)
This replaces `egui::plot` with the new crate `egui_plot`
This commit is contained in:
190
crates/egui_plot/src/items/bar.rs
Normal file
190
crates/egui_plot/src/items/bar.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use egui::emath::NumExt;
|
||||
use egui::epaint::{Color32, RectShape, Rounding, Shape, Stroke};
|
||||
|
||||
use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
|
||||
use crate::{BarChart, Cursor, PlotPoint, PlotTransform};
|
||||
|
||||
/// One bar in a [`BarChart`]. Potentially floating, allowing stacked bar charts.
|
||||
/// Width can be changed to allow variable-width histograms.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Bar {
|
||||
/// Name of plot element in the diagram (annotated by default formatter)
|
||||
pub name: String,
|
||||
|
||||
/// Which direction the bar faces in the diagram
|
||||
pub orientation: Orientation,
|
||||
|
||||
/// Position on the argument (input) axis -- X if vertical, Y if horizontal
|
||||
pub argument: f64,
|
||||
|
||||
/// Position on the value (output) axis -- Y if vertical, X if horizontal
|
||||
pub value: f64,
|
||||
|
||||
/// For stacked bars, this denotes where the bar starts. None if base axis
|
||||
pub base_offset: Option<f64>,
|
||||
|
||||
/// Thickness of the bar
|
||||
pub bar_width: f64,
|
||||
|
||||
/// Line width and color
|
||||
pub stroke: Stroke,
|
||||
|
||||
/// Fill color
|
||||
pub fill: Color32,
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
/// Create a bar. Its `orientation` is set by its [`BarChart`] parent.
|
||||
///
|
||||
/// - `argument`: Position on the argument axis (X if vertical, Y if horizontal).
|
||||
/// - `value`: Height of the bar (if vertical).
|
||||
///
|
||||
/// By default the bar is vertical and its base is at zero.
|
||||
pub fn new(argument: f64, height: f64) -> Bar {
|
||||
Bar {
|
||||
argument,
|
||||
value: height,
|
||||
orientation: Orientation::default(),
|
||||
name: Default::default(),
|
||||
base_offset: None,
|
||||
bar_width: 0.5,
|
||||
stroke: Stroke::new(1.0, Color32::TRANSPARENT),
|
||||
fill: Color32::TRANSPARENT,
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of this bar chart element.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn name(mut self, name: impl ToString) -> Self {
|
||||
self.name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a custom stroke.
|
||||
pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {
|
||||
self.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a custom fill color.
|
||||
pub fn fill(mut self, color: impl Into<Color32>) -> Self {
|
||||
self.fill = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Offset the base of the bar.
|
||||
/// This offset is on the Y axis for a vertical bar
|
||||
/// and on the X axis for a horizontal bar.
|
||||
pub fn base_offset(mut self, offset: f64) -> Self {
|
||||
self.base_offset = Some(offset);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the bar width.
|
||||
pub fn width(mut self, width: f64) -> Self {
|
||||
self.bar_width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set orientation of the element as vertical. Argument axis is X.
|
||||
pub fn vertical(mut self) -> Self {
|
||||
self.orientation = Orientation::Vertical;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set orientation of the element as horizontal. Argument axis is Y.
|
||||
pub fn horizontal(mut self) -> Self {
|
||||
self.orientation = Orientation::Horizontal;
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn lower(&self) -> f64 {
|
||||
if self.value.is_sign_positive() {
|
||||
self.base_offset.unwrap_or(0.0)
|
||||
} else {
|
||||
self.base_offset.map_or(self.value, |o| o + self.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn upper(&self) -> f64 {
|
||||
if self.value.is_sign_positive() {
|
||||
self.base_offset.map_or(self.value, |o| o + self.value)
|
||||
} else {
|
||||
self.base_offset.unwrap_or(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_shapes(
|
||||
&self,
|
||||
transform: &PlotTransform,
|
||||
highlighted: bool,
|
||||
shapes: &mut Vec<Shape>,
|
||||
) {
|
||||
let (stroke, fill) = if highlighted {
|
||||
highlighted_color(self.stroke, self.fill)
|
||||
} else {
|
||||
(self.stroke, self.fill)
|
||||
};
|
||||
|
||||
let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max());
|
||||
let rect = Shape::Rect(RectShape::new(rect, Rounding::ZERO, fill, stroke));
|
||||
|
||||
shapes.push(rect);
|
||||
}
|
||||
|
||||
pub(super) fn add_rulers_and_text(
|
||||
&self,
|
||||
parent: &BarChart,
|
||||
plot: &PlotConfig<'_>,
|
||||
shapes: &mut Vec<Shape>,
|
||||
cursors: &mut Vec<Cursor>,
|
||||
) {
|
||||
let text: Option<String> = parent
|
||||
.element_formatter
|
||||
.as_ref()
|
||||
.map(|fmt| fmt(self, parent));
|
||||
|
||||
add_rulers_and_text(self, plot, text, shapes, cursors);
|
||||
}
|
||||
}
|
||||
|
||||
impl RectElement for Bar {
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn bounds_min(&self) -> PlotPoint {
|
||||
self.point_at(self.argument - self.bar_width / 2.0, self.lower())
|
||||
}
|
||||
|
||||
fn bounds_max(&self) -> PlotPoint {
|
||||
self.point_at(self.argument + self.bar_width / 2.0, self.upper())
|
||||
}
|
||||
|
||||
fn values_with_ruler(&self) -> Vec<PlotPoint> {
|
||||
let base = self.base_offset.unwrap_or(0.0);
|
||||
let value_center = self.point_at(self.argument, base + self.value);
|
||||
|
||||
let mut ruler_positions = vec![value_center];
|
||||
|
||||
if let Some(offset) = self.base_offset {
|
||||
ruler_positions.push(self.point_at(self.argument, offset));
|
||||
}
|
||||
|
||||
ruler_positions
|
||||
}
|
||||
|
||||
fn orientation(&self) -> Orientation {
|
||||
self.orientation
|
||||
}
|
||||
|
||||
fn default_values_format(&self, transform: &PlotTransform) -> String {
|
||||
let scale = transform.dvalue_dpos();
|
||||
let scale = match self.orientation {
|
||||
Orientation::Horizontal => scale[0],
|
||||
Orientation::Vertical => scale[1],
|
||||
};
|
||||
let decimals = ((-scale.abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
||||
crate::format_number(self.value, decimals)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user