mirror of
https://github.com/emilk/egui.git
synced 2026-06-28 07:23:13 -04:00
Remove generics to minimize monomorphization code bloat
This commit is contained in:
@@ -5,17 +5,31 @@ use epaint::{
|
||||
Pos2, Rect, Shape, Stroke, TextShape,
|
||||
};
|
||||
|
||||
use crate::{Response, Sense, TextStyle, Ui, Widget, WidgetText};
|
||||
use crate::{Response, Sense, TextStyle, Ui, WidgetText};
|
||||
|
||||
use super::{transform::PlotTransform, GridMark};
|
||||
|
||||
pub(super) type AxisFormatterFn = fn(f64, usize, &RangeInclusive<f64>) -> String;
|
||||
|
||||
/// Generic constant for x-Axis
|
||||
pub(super) const X_AXIS: usize = 0;
|
||||
/// X or Y axis.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Axis {
|
||||
/// Horizontal X-Axis
|
||||
X,
|
||||
|
||||
/// Generic constant for y-Axis
|
||||
pub(super) const Y_AXIS: usize = 1;
|
||||
/// Vertical Y-axis
|
||||
Y,
|
||||
}
|
||||
|
||||
impl From<Axis> for usize {
|
||||
#[inline]
|
||||
fn from(value: Axis) -> Self {
|
||||
match value {
|
||||
Axis::X => 0,
|
||||
Axis::Y => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Placement of the horizontal X-Axis.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -61,23 +75,11 @@ impl From<VPlacement> for Placement {
|
||||
}
|
||||
}
|
||||
|
||||
// shorthand types for AxisHints, public API
|
||||
/// Configuration for x-axis
|
||||
pub type XAxisHints = AxisHints<X_AXIS>;
|
||||
|
||||
/// Configuration for y-axis
|
||||
pub type YAxisHints = AxisHints<Y_AXIS>;
|
||||
|
||||
// shorthand types for AxisWidget
|
||||
pub(super) type XAxisWidget = AxisWidget<X_AXIS>;
|
||||
pub(super) type YAxisWidget = AxisWidget<Y_AXIS>;
|
||||
|
||||
/// Axis configuration.
|
||||
///
|
||||
/// Used to configure axis label and ticks.
|
||||
/// The AXIS argument must be either [`X_AXIS`] or [`Y_AXIS`]. Everything else is disallowed.
|
||||
#[derive(Clone)]
|
||||
pub struct AxisHints<const AXIS: usize> {
|
||||
pub struct AxisHints {
|
||||
pub(super) label: WidgetText,
|
||||
pub(super) formatter: AxisFormatterFn,
|
||||
pub(super) digits: usize,
|
||||
@@ -87,7 +89,7 @@ pub struct AxisHints<const AXIS: usize> {
|
||||
// TODO: this just a guess. It might cease to work if a user changes font size.
|
||||
const LINE_HEIGHT: f32 = 12.0;
|
||||
|
||||
impl<const AXIS: usize> Default for AxisHints<AXIS> {
|
||||
impl Default for AxisHints {
|
||||
/// Initializes a default axis configuration for the specified axis.
|
||||
///
|
||||
/// `label` is empty.
|
||||
@@ -103,7 +105,7 @@ impl<const AXIS: usize> Default for AxisHints<AXIS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const AXIS: usize> AxisHints<AXIS> {
|
||||
impl AxisHints {
|
||||
/// Specify custom formatter for ticks.
|
||||
///
|
||||
/// The first parameter of `formatter` is the raw tick value as `f64`.
|
||||
@@ -151,39 +153,38 @@ impl<const AXIS: usize> AxisHints<AXIS> {
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn thickness(&self) -> f32 {
|
||||
match AXIS {
|
||||
X_AXIS => {
|
||||
pub(super) fn thickness(&self, axis: Axis) -> f32 {
|
||||
match axis {
|
||||
Axis::X => {
|
||||
if self.label.is_empty() {
|
||||
1.0 * LINE_HEIGHT
|
||||
} else {
|
||||
3.0 * LINE_HEIGHT
|
||||
}
|
||||
}
|
||||
Y_AXIS => {
|
||||
Axis::Y => {
|
||||
if self.label.is_empty() {
|
||||
(self.digits as f32) * LINE_HEIGHT
|
||||
} else {
|
||||
(self.digits as f32 + 1.0) * LINE_HEIGHT
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct AxisWidget<const AXIS: usize> {
|
||||
pub(super) struct AxisWidget {
|
||||
pub(super) range: RangeInclusive<f64>,
|
||||
pub(super) hints: AxisHints<AXIS>,
|
||||
pub(super) hints: AxisHints,
|
||||
pub(super) rect: Rect,
|
||||
pub(super) transform: Option<PlotTransform>,
|
||||
pub(super) steps: Arc<Vec<GridMark>>,
|
||||
}
|
||||
|
||||
impl<const AXIS: usize> AxisWidget<AXIS> {
|
||||
impl AxisWidget {
|
||||
/// if `rect` as width or height == 0, is will be automatically calculated from ticks and text.
|
||||
pub(super) fn new(hints: AxisHints<AXIS>, rect: Rect) -> Self {
|
||||
pub(super) fn new(hints: AxisHints, rect: Rect) -> Self {
|
||||
Self {
|
||||
range: (0.0..=0.0),
|
||||
hints,
|
||||
@@ -192,10 +193,8 @@ impl<const AXIS: usize> AxisWidget<AXIS> {
|
||||
steps: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
pub fn ui(self, ui: &mut Ui, axis: Axis) -> Response {
|
||||
let response = ui.allocate_rect(self.rect, Sense::hover());
|
||||
|
||||
if ui.is_rect_visible(response.rect) {
|
||||
@@ -205,46 +204,43 @@ impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
let text_color = visuals
|
||||
.override_text_color
|
||||
.unwrap_or_else(|| ui.visuals().text_color());
|
||||
let angle: f32 = match AXIS {
|
||||
X_AXIS => 0.0,
|
||||
Y_AXIS => -std::f32::consts::TAU * 0.25,
|
||||
_ => unreachable!(),
|
||||
let angle: f32 = match axis {
|
||||
Axis::X => 0.0,
|
||||
Axis::Y => -std::f32::consts::TAU * 0.25,
|
||||
};
|
||||
// select text_pos and angle depending on placement and orientation of widget
|
||||
let text_pos = match self.hints.placement {
|
||||
Placement::LeftBottom => match AXIS {
|
||||
X_AXIS => {
|
||||
Placement::LeftBottom => match axis {
|
||||
Axis::X => {
|
||||
let pos = response.rect.center_bottom();
|
||||
Pos2 {
|
||||
x: pos.x - galley.size().x / 2.0,
|
||||
y: pos.y - galley.size().y * 1.25,
|
||||
}
|
||||
}
|
||||
Y_AXIS => {
|
||||
Axis::Y => {
|
||||
let pos = response.rect.left_center();
|
||||
Pos2 {
|
||||
x: pos.x,
|
||||
y: pos.y + galley.size().x / 2.0,
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Placement::RightTop => match AXIS {
|
||||
X_AXIS => {
|
||||
Placement::RightTop => match axis {
|
||||
Axis::X => {
|
||||
let pos = response.rect.center_top();
|
||||
Pos2 {
|
||||
x: pos.x - galley.size().x / 2.0,
|
||||
y: pos.y + galley.size().y * 0.25,
|
||||
}
|
||||
}
|
||||
Y_AXIS => {
|
||||
Axis::Y => {
|
||||
let pos = response.rect.right_center();
|
||||
Pos2 {
|
||||
x: pos.x - galley.size().y * 1.5,
|
||||
y: pos.y + galley.size().x / 2.0,
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
let shape = TextShape {
|
||||
@@ -269,7 +265,7 @@ impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
const MIN_TEXT_SPACING: f32 = 20.0;
|
||||
const FULL_CONTRAST_SPACING: f32 = 40.0;
|
||||
let spacing_in_points =
|
||||
(transform.dpos_dvalue()[AXIS] * step.step_size).abs() as f32;
|
||||
(transform.dpos_dvalue()[usize::from(axis)] * step.step_size).abs() as f32;
|
||||
|
||||
if spacing_in_points <= MIN_TEXT_SPACING {
|
||||
continue;
|
||||
@@ -284,8 +280,9 @@ impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
let galley = ui
|
||||
.painter()
|
||||
.layout_no_wrap(text, font_id.clone(), line_color);
|
||||
let text_pos = match AXIS {
|
||||
X_AXIS => {
|
||||
|
||||
let text_pos = match axis {
|
||||
Axis::X => {
|
||||
let y = match self.hints.placement {
|
||||
Placement::LeftBottom => self.rect.min.y,
|
||||
Placement::RightTop => self.rect.max.y - galley.size().y,
|
||||
@@ -297,7 +294,7 @@ impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
y,
|
||||
}
|
||||
}
|
||||
Y_AXIS => {
|
||||
Axis::Y => {
|
||||
let x = match self.hints.placement {
|
||||
Placement::LeftBottom => self.rect.max.x - galley.size().x,
|
||||
Placement::RightTop => self.rect.min.x,
|
||||
@@ -309,7 +306,6 @@ impl<const AXIS: usize> Widget for AxisWidget<AXIS> {
|
||||
- galley.size().y / 2.0,
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
ui.painter().add(Shape::galley(text_pos, galley));
|
||||
|
||||
@@ -6,7 +6,7 @@ use ahash::HashMap;
|
||||
use epaint::util::FloatOrd;
|
||||
use epaint::Hsva;
|
||||
|
||||
use axis::{XAxisWidget, YAxisWidget, X_AXIS, Y_AXIS};
|
||||
use axis::AxisWidget;
|
||||
use items::PlotItem;
|
||||
use legend::LegendWidget;
|
||||
|
||||
@@ -21,7 +21,7 @@ pub use transform::{PlotBounds, PlotTransform};
|
||||
|
||||
use items::{horizontal_line, rulers_color, vertical_line};
|
||||
|
||||
pub use axis::{HPlacement, Placement, VPlacement, XAxisHints, YAxisHints};
|
||||
pub use axis::{Axis, AxisHints, HPlacement, Placement, VPlacement};
|
||||
|
||||
mod axis;
|
||||
mod items;
|
||||
@@ -36,6 +36,9 @@ type GridSpacer = Box<GridSpacerFn>;
|
||||
|
||||
type CoordinatesFormatterFn = dyn Fn(&PlotPoint, &PlotBounds) -> String;
|
||||
|
||||
const X_AXIS: usize = 0;
|
||||
const Y_AXIS: usize = 1;
|
||||
|
||||
/// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`].
|
||||
pub struct CoordinatesFormatter {
|
||||
function: Box<CoordinatesFormatterFn>,
|
||||
@@ -219,8 +222,8 @@ pub struct Plot {
|
||||
show_y: bool,
|
||||
label_formatter: LabelFormatter,
|
||||
coordinates_formatter: Option<(Corner, CoordinatesFormatter)>,
|
||||
x_axes: Vec<XAxisHints>, // default x axes
|
||||
y_axes: Vec<YAxisHints>, // default y axes
|
||||
x_axes: Vec<AxisHints>, // default x axes
|
||||
y_axes: Vec<AxisHints>, // default y axes
|
||||
legend_config: Option<Legend>,
|
||||
show_background: bool,
|
||||
show_axes: AxisBools,
|
||||
@@ -650,7 +653,7 @@ impl Plot {
|
||||
/// Set custom configuration for X-axis
|
||||
///
|
||||
/// More than one axis may be specified. The first specified axis is considered the main axis.
|
||||
pub fn custom_x_axes(mut self, hints: Vec<XAxisHints>) -> Self {
|
||||
pub fn custom_x_axes(mut self, hints: Vec<AxisHints>) -> Self {
|
||||
self.x_axes = hints;
|
||||
self
|
||||
}
|
||||
@@ -658,7 +661,7 @@ impl Plot {
|
||||
/// Set custom configuration for left Y-axis
|
||||
///
|
||||
/// More than one axis may be specified. The first specified axis is considered the main axis.
|
||||
pub fn custom_y_axes(mut self, hints: Vec<YAxisHints>) -> Self {
|
||||
pub fn custom_y_axes(mut self, hints: Vec<AxisHints>) -> Self {
|
||||
self.y_axes = hints;
|
||||
self
|
||||
}
|
||||
@@ -769,10 +772,10 @@ impl Plot {
|
||||
for cfg in &x_axes {
|
||||
match cfg.placement {
|
||||
axis::Placement::LeftBottom => {
|
||||
margin.bottom += cfg.thickness();
|
||||
margin.bottom += cfg.thickness(Axis::X);
|
||||
}
|
||||
axis::Placement::RightTop => {
|
||||
margin.top += cfg.thickness();
|
||||
margin.top += cfg.thickness(Axis::X);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,10 +784,10 @@ impl Plot {
|
||||
for cfg in &y_axes {
|
||||
match cfg.placement {
|
||||
axis::Placement::LeftBottom => {
|
||||
margin.left += cfg.thickness();
|
||||
margin.left += cfg.thickness(Axis::Y);
|
||||
}
|
||||
axis::Placement::RightTop => {
|
||||
margin.right += cfg.thickness();
|
||||
margin.right += cfg.thickness(Axis::Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -793,8 +796,8 @@ impl Plot {
|
||||
// determine plot rectangle
|
||||
margin.shrink_rect(complete_rect)
|
||||
};
|
||||
let mut x_axis_widgets = Vec::<XAxisWidget>::new();
|
||||
let mut y_axis_widgets = Vec::<YAxisWidget>::new();
|
||||
let mut x_axis_widgets = Vec::<AxisWidget>::new();
|
||||
let mut y_axis_widgets = Vec::<AxisWidget>::new();
|
||||
{
|
||||
// Determine absolute rectangle for each axis label widget.
|
||||
|
||||
@@ -813,7 +816,7 @@ impl Plot {
|
||||
};
|
||||
if show_axes.x {
|
||||
for cfg in &x_axes {
|
||||
let size_y = Vec2::new(0.0, cfg.thickness());
|
||||
let size_y = Vec2::new(0.0, cfg.thickness(Axis::X));
|
||||
let rect = match cfg.placement {
|
||||
axis::Placement::LeftBottom => {
|
||||
let off = num_widgets.bottom as f32;
|
||||
@@ -832,12 +835,12 @@ impl Plot {
|
||||
}
|
||||
}
|
||||
};
|
||||
x_axis_widgets.push(XAxisWidget::new(cfg.clone(), rect));
|
||||
x_axis_widgets.push(AxisWidget::new(cfg.clone(), rect));
|
||||
}
|
||||
}
|
||||
if show_axes.y {
|
||||
for cfg in &y_axes {
|
||||
let size_x = Vec2::new(cfg.thickness(), 0.0);
|
||||
let size_x = Vec2::new(cfg.thickness(Axis::Y), 0.0);
|
||||
let rect = match cfg.placement {
|
||||
axis::Placement::LeftBottom => {
|
||||
let off = num_widgets.left as f32;
|
||||
@@ -856,7 +859,7 @@ impl Plot {
|
||||
}
|
||||
}
|
||||
};
|
||||
y_axis_widgets.push(YAxisWidget::new(cfg.clone(), rect));
|
||||
y_axis_widgets.push(AxisWidget::new(cfg.clone(), rect));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1187,13 +1190,13 @@ impl Plot {
|
||||
widget.range = x_axis_range.clone();
|
||||
widget.transform = Some(transform);
|
||||
widget.steps = x_steps.clone();
|
||||
ui.add(widget);
|
||||
widget.ui(ui, Axis::X);
|
||||
}
|
||||
for mut widget in y_axis_widgets {
|
||||
widget.range = y_axis_range.clone();
|
||||
widget.transform = Some(transform);
|
||||
widget.steps = y_steps.clone();
|
||||
ui.add(widget);
|
||||
widget.ui(ui, Axis::Y);
|
||||
}
|
||||
|
||||
// Initialize values from functions.
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::ops::RangeInclusive;
|
||||
use egui::*;
|
||||
|
||||
use egui::plot::{
|
||||
Arrows, AxisBools, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner,
|
||||
GridInput, GridMark, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, PlotPoint,
|
||||
PlotPoints, PlotResponse, Points, Polygon, Text, VLine, XAxisHints, YAxisHints,
|
||||
Arrows, AxisBools, AxisHints, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter,
|
||||
Corner, GridInput, GridMark, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage,
|
||||
PlotPoint, PlotPoints, PlotResponse, Points, Polygon, Text, VLine,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -565,15 +565,15 @@ impl CustomAxesDemo {
|
||||
ui.label("Zoom in on the X-axis to see hours and minutes");
|
||||
|
||||
let x_axes = vec![
|
||||
XAxisHints::default().label("Time").formatter(x_fmt),
|
||||
XAxisHints::default().label("Value"),
|
||||
AxisHints::default().label("Time").formatter(x_fmt),
|
||||
AxisHints::default().label("Value"),
|
||||
];
|
||||
let y_axes = vec![
|
||||
YAxisHints::default()
|
||||
AxisHints::default()
|
||||
.label("Percent")
|
||||
.formatter(y_fmt)
|
||||
.max_digits(4),
|
||||
YAxisHints::default()
|
||||
AxisHints::default()
|
||||
.label("Absolute")
|
||||
.placement(plot::HPlacement::Right),
|
||||
];
|
||||
@@ -602,15 +602,11 @@ struct LinkedAxesDemo {
|
||||
|
||||
impl Default for LinkedAxesDemo {
|
||||
fn default() -> Self {
|
||||
let link_x = true;
|
||||
let link_y = false;
|
||||
let link_cursor_x = true;
|
||||
let link_cursor_y = false;
|
||||
Self {
|
||||
link_x,
|
||||
link_y,
|
||||
link_cursor_x,
|
||||
link_cursor_y,
|
||||
link_x: true,
|
||||
link_y: true,
|
||||
link_cursor_x: true,
|
||||
link_cursor_y: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user