aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-01-20 23:29:49 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2024-01-20 23:29:49 +0100
commitb8e501b7d4d3991669233109a6f8213a2d624c14 (patch)
tree14b8af50bbf5e63ec9bb3c24038ddce02c504295 /src
parent24ebe795e0ebf9dfc3cf2fafa609d8dc573a80a6 (diff)
add graph labels and step resizing on zooming
Diffstat (limited to 'src')
-rw-r--r--src/ui/graph.rs73
1 files changed, 58 insertions, 15 deletions
diff --git a/src/ui/graph.rs b/src/ui/graph.rs
index 2f3855d..13e8ac9 100644
--- a/src/ui/graph.rs
+++ b/src/ui/graph.rs
@@ -4,7 +4,7 @@ use super::Message;
use iced::{
event,
mouse::{self, Interaction},
- widget::canvas::{self, stroke, Cache, Frame, Geometry, LineCap, Path, Stroke},
+ widget::canvas::{self, stroke, Cache, Frame, Geometry, LineCap, Path, Stroke, Text},
Color, Point, Rectangle, Renderer, Size, Theme, Vector,
};
@@ -45,12 +45,22 @@ pub struct GraphState {
}
impl GraphState {
- fn view_rectangle(&self, frame: &Frame) -> Rectangle {
- let s = Size::new(frame.width() / self.scale + 2.0, frame.height() / self.scale + 2.0);
- let p = Point::new(-self.center.x / self.scale - s.width / 2.0, -self.center.y / self.scale - s.height / 2.0);
+ fn view_rectangle(&self, frame: &Frame, step: usize) -> Rectangle {
+ let s = Size::new(
+ self.map_to_step(frame.width() / self.scale + 2.0 * step as f32, step),
+ self.map_to_step(frame.height() / self.scale + 2.0 * step as f32, step),
+ );
+ let p = Point::new(
+ self.map_to_step(-self.center.x / self.scale - s.width / 2.0, step),
+ self.map_to_step(-self.center.y / self.scale - s.height / 2.0, step),
+ );
Rectangle::new(p, s)
}
+ fn map_to_step(&self, n: f32, step: usize) -> f32 {
+ ((n as i64 / step as i64) * step as i64) as f32
+ }
+
fn map_coords(&self, x: f32, y: f32) -> Point {
Point::new(x, -y)
}
@@ -79,37 +89,43 @@ impl canvas::Program<Message, Renderer> for GraphCanvas {
_cursor: mouse::Cursor,
) -> Vec<Geometry> {
let graph = self.graph.draw(renderer, bounds.size(), |frame| {
+ let zcolor = Color::new(0.27, 0.52, 0.53, 1.0);
+ let lcolor = Color::new(0.49, 0.43, 0.39, 1.0);
+ let gcolor = Color::new(0.8, 0.14, 0.11, 1.0);
+ let step = 10_usize.pow((400.0 / state.scale).log10() as u32);
+
let unitline_whole = Stroke {
width: 1.0,
- style: stroke::Style::Solid(Color::new(0.5, 0.5, 0.5, 1.0)),
+ style: stroke::Style::Solid(lcolor),
line_cap: LineCap::Round,
..Stroke::default()
};
+
let zeroline = Stroke {
width: 3.0,
- style: stroke::Style::Solid(Color::new(0.0, 0.0, 0.8, 1.0)),
+ style: stroke::Style::Solid(zcolor),
line_cap: LineCap::Round,
..Stroke::default()
};
let graphline = Stroke {
width: 3.0,
- style: stroke::Style::Solid(Color::new(0.8, 0.0, 0.0, 1.0)),
+ style: stroke::Style::Solid(gcolor),
line_cap: LineCap::Round,
..Stroke::default()
};
+ let rect = state.view_rectangle(frame, step);
+ let x_end = (rect.x + rect.width) as i64;
+ let y_end = (rect.y + rect.height) as i64;
+
frame.translate(Vector {
x: frame.center().x + state.center.x,
y: frame.center().y + state.center.y,
});
frame.scale(state.scale);
- let rect = state.view_rectangle(frame);
- let x_end = (rect.x + rect.width) as i64;
- let y_end = (rect.y + rect.height) as i64;
-
- for y in (rect.y as i64..y_end).step_by(1) {
+ for y in (rect.y as i64..y_end).step_by(step) {
let line = Path::line(
Point::new(rect.x, y as f32),
Point::new(rect.x + rect.width, y as f32),
@@ -117,7 +133,7 @@ impl canvas::Program<Message, Renderer> for GraphCanvas {
frame.stroke(&line, unitline_whole.clone());
}
- for x in (rect.x as i64..x_end).step_by(1) {
+ for x in (rect.x as i64..x_end).step_by(step) {
let line = Path::line(
Point::new(x as f32, rect.y),
Point::new(x as f32, rect.y + rect.height),
@@ -142,8 +158,35 @@ impl canvas::Program<Message, Renderer> for GraphCanvas {
);
frame.with_save(|frame| {
- frame.translate(Vector { x: 1.0, y: 0.0 });
- frame.fill_text("1");
+ for x in (rect.x as i64..x_end).step_by(step) {
+ let p = Point::new(x as f32, 0.0);
+ frame.fill(&Path::circle(p, 10.0 / state.scale), zcolor);
+ frame.fill_text(Text {
+ content: x.to_string(),
+ position: p,
+ horizontal_alignment: iced::alignment::Horizontal::Center,
+ vertical_alignment: iced::alignment::Vertical::Center,
+ color: Color::WHITE,
+ ..Text::default()
+ });
+ }
+
+ for y in (rect.y as i64..y_end).step_by(step) {
+ if y == 0 {
+ continue;
+ }
+
+ let p = Point::new(0.0, y as f32);
+ frame.fill(&Path::circle(p, 10.0 / state.scale), zcolor);
+ frame.fill_text(Text {
+ content: (-y).to_string(),
+ position: p,
+ horizontal_alignment: iced::alignment::Horizontal::Center,
+ vertical_alignment: iced::alignment::Vertical::Center,
+ color: Color::WHITE,
+ ..Text::default()
+ });
+ }
});
let mut y1 = self