Skip to content

Commit 1f4220a

Browse files
committed
Add logarithmic plot axes
This commit is an initial implementation for adding logarithmic plotting axis. This very much needs more testing! The basic idea is, that everything stays the same, but PlotTransform does the much needed coordinate transformation for us. That is, unfortunatley not all of the story. * In a lot of places, we need estimates of "how many pixels does 1 plot space unit take" and the likes, either for overdraw reduction, or generally to size things. PlotTransform has been modifed for that for now, so this should work. * While the normal grid spacer renders just fine, it will also casually try to generate 100s of thousands of lines for a bigger range log plot. So GridInput has been made aware if there is a log axis present. The default spacer has also been modified to work initially. * All of the PlotBound transformations within PlotTransform need to be aware and handle the log scaling properly. This is done and works well, but its a bit.. icky, for lack of a better word. If someone has a better idea how to handle this, be my guest :D * PlotPoint generation from generator functions has to become aware of logarithmic plotting, otherwise the resolution of the plotted points will suffer. Especially the spacer generation is still kinda WIP; it is messy at best right now. Especially for zooming in, it currently only adds lines on the lower bound due to the way the generator function works right now. I will address this in a follow up commit/--amend (or someone else will).
1 parent 3c5516f commit 1f4220a

23 files changed

+659
-144
lines changed

demo/src/plot_demo.rs

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ use std::f64::consts::TAU;
22
use std::ops::RangeInclusive;
33

44
use egui::{
5-
remap, vec2, Color32, ComboBox, NumExt, Pos2, Response, ScrollArea, Stroke, TextWrapMode,
6-
Vec2b, WidgetInfo, WidgetType,
5+
remap, vec2, Color32, ComboBox, DragValue, NumExt, Pos2, Response, ScrollArea, Stroke,
6+
TextWrapMode, Vec2b, WidgetInfo, WidgetType,
77
};
8-
98
use egui_plot::{
10-
Arrows, AxisHints, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner,
11-
GridInput, GridMark, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, PlotPoint,
12-
PlotPoints, PlotResponse, Points, Polygon, Text, VLine,
9+
Arrows, AxisHints, AxisTransform, AxisTransforms, Bar, BarChart, BoxElem, BoxPlot, BoxSpread,
10+
CoordinatesFormatter, Corner, GridInput, GridMark, HLine, Legend, Line, LineStyle, MarkerShape,
11+
Plot, PlotBounds, PlotImage, PlotPoint, PlotPoints, PlotResponse, Points, Polygon, Text, VLine,
1312
};
1413

1514
// ----------------------------------------------------------------------------
@@ -24,6 +23,7 @@ enum Panel {
2423
Interaction,
2524
CustomAxes,
2625
LinkedAxes,
26+
LogAxes,
2727
}
2828

2929
impl Default for Panel {
@@ -44,6 +44,7 @@ pub struct PlotDemo {
4444
interaction_demo: InteractionDemo,
4545
custom_axes_demo: CustomAxesDemo,
4646
linked_axes_demo: LinkedAxesDemo,
47+
log_axes_demo: LogAxesDemo,
4748
open_panel: Panel,
4849
}
4950

@@ -88,6 +89,7 @@ impl PlotDemo {
8889
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
8990
ui.selectable_value(&mut self.open_panel, Panel::CustomAxes, "Custom Axes");
9091
ui.selectable_value(&mut self.open_panel, Panel::LinkedAxes, "Linked Axes");
92+
ui.selectable_value(&mut self.open_panel, Panel::LogAxes, "Log Axes");
9193
});
9294
});
9395
ui.separator();
@@ -117,6 +119,9 @@ impl PlotDemo {
117119
Panel::LinkedAxes => {
118120
self.linked_axes_demo.ui(ui);
119121
}
122+
Panel::LogAxes => {
123+
self.log_axes_demo.ui(ui);
124+
}
120125
}
121126
}
122127
}
@@ -706,6 +711,111 @@ impl LinkedAxesDemo {
706711
}
707712
}
708713

714+
// ----------------------------------------------------------------------------
715+
#[derive(PartialEq, serde::Deserialize, serde::Serialize, Default)]
716+
struct LogAxesDemo {
717+
axis_transforms: AxisTransforms,
718+
}
719+
720+
/// Helper function showing how to do arbitrary transform picking
721+
fn transform_edit(id: &str, old_transform: AxisTransform, ui: &mut egui::Ui) -> AxisTransform {
722+
ui.horizontal(|ui| {
723+
ui.label(format!("Transform for {id}"));
724+
if ui
725+
.radio(matches!(old_transform, AxisTransform::Linear), "Linear")
726+
.clicked()
727+
{
728+
return AxisTransform::Linear;
729+
}
730+
if ui
731+
.radio(
732+
matches!(old_transform, AxisTransform::Logarithmic(_)),
733+
"Logarithmic",
734+
)
735+
.clicked()
736+
{
737+
let reuse_base = if let AxisTransform::Logarithmic(base) = old_transform {
738+
base
739+
} else {
740+
10.0
741+
};
742+
return AxisTransform::Logarithmic(reuse_base);
743+
}
744+
745+
// no change, but perhaps additional things?
746+
match old_transform {
747+
// Nah?
748+
AxisTransform::Logarithmic(mut base) => {
749+
ui.label("Base:");
750+
ui.add(DragValue::new(&mut base).range(2.0..=100.0));
751+
AxisTransform::Logarithmic(base)
752+
}
753+
AxisTransform::Linear => old_transform,
754+
}
755+
})
756+
.inner
757+
}
758+
759+
impl LogAxesDemo {
760+
fn line_exp<'a>() -> Line<'a> {
761+
Line::new(PlotPoints::from_explicit_callback(
762+
move |x| 10.0_f64.powf(x / 200.0),
763+
0.1..=1000.0,
764+
1000,
765+
))
766+
.name("y = 10^(x/200)")
767+
.color(Color32::RED)
768+
}
769+
770+
fn line_lin<'a>() -> Line<'a> {
771+
Line::new(PlotPoints::from_explicit_callback(
772+
move |x| -5.0 + x,
773+
0.1..=1000.0,
774+
1000,
775+
))
776+
.name("y = -5 + x")
777+
.color(Color32::GREEN)
778+
}
779+
780+
fn line_log<'a>() -> Line<'a> {
781+
Line::new(PlotPoints::from_explicit_callback(
782+
move |x| x.log10(),
783+
0.1..=1000.0,
784+
1000,
785+
))
786+
.name("y = log10(x)")
787+
.color(Color32::BLUE)
788+
}
789+
790+
fn ui(&mut self, ui: &mut egui::Ui) -> Response {
791+
let old_transforms = self.axis_transforms;
792+
self.axis_transforms.horizontal =
793+
transform_edit("horizontal axis", self.axis_transforms.horizontal, ui);
794+
self.axis_transforms.vertical =
795+
transform_edit("vertical axis", self.axis_transforms.vertical, ui);
796+
let just_changed = old_transforms != self.axis_transforms;
797+
Plot::new("log_demo")
798+
.axis_transforms(self.axis_transforms)
799+
.x_axis_label("x")
800+
.y_axis_label("y")
801+
.show_axes(Vec2b::new(true, true))
802+
.legend(Legend::default())
803+
.show(ui, |ui| {
804+
if just_changed {
805+
if let AxisTransform::Logarithmic(_) = self.axis_transforms.horizontal {
806+
ui.set_plot_bounds(PlotBounds::from_min_max([0.1, 0.1], [1e3, 1e4]));
807+
} else {
808+
ui.set_plot_bounds(PlotBounds::from_min_max([0.0, 0.0], [3.0, 1000.0]));
809+
}
810+
}
811+
ui.line(Self::line_exp());
812+
ui.line(Self::line_lin());
813+
ui.line(Self::line_log());
814+
})
815+
.response
816+
}
817+
}
818+
709819
// ----------------------------------------------------------------------------
710820

711821
#[derive(Default, PartialEq, serde::Deserialize, serde::Serialize)]
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading

0 commit comments

Comments
 (0)