From: Dominik Csapak <d.csapak@proxmox.com>
To: yew-devel@lists.proxmox.com
Subject: [yew-devel] [PATCH yew-comp 16/20] rrd: introduce GraphSpace struct and use it to precalculate graph data
Date: Fri, 30 May 2025 14:21:58 +0200 [thread overview]
Message-ID: <20250530122202.2779300-17-d.csapak@proxmox.com> (raw)
In-Reply-To: <20250530122202.2779300-1-d.csapak@proxmox.com>
This contains all information about the data and the svg graph that is
needed to convert coordinates between data and svg space.
This replaces the LayoutProps struct on the graph, and provides the
necessary interfaces.
It's data is updated whenever the grid parameters are changed so that we
need to recalculate them, e.g. when the data changes, or the width of
the grid. Since this is now only done when necessary, it should be much
faster when unrelated redraws occur, e.g. when moving the mouse cursor
over the data.
We need to derive PartialEq for the Series struct to check if they have
changed.
Also change the interfaces of compute_*_path, so that they only take
a GraphSpace struct anymore instead of the min/max data and compute
functions (all that is provided by the struct).
`compute_fill_path` calculation of y0 is changed slightly to accommodate
the fact that the GraphSpace only takes values inside the min and max
data ranges, but the end result should be the same.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/rrd/graph.rs | 144 ++++++++++++++++------------------------
src/rrd/graph_space.rs | 147 +++++++++++++++++++++++++++++++++++++++++
src/rrd/mod.rs | 2 +
src/rrd/series.rs | 45 +++++--------
4 files changed, 223 insertions(+), 115 deletions(-)
create mode 100644 src/rrd/graph_space.rs
diff --git a/src/rrd/graph.rs b/src/rrd/graph.rs
index 5edab66..a02e086 100644
--- a/src/rrd/graph.rs
+++ b/src/rrd/graph.rs
@@ -118,7 +118,7 @@ pub struct PwtRRDGraph {
node_ref: NodeRef,
size_observer: Option<DomSizeObserver>,
canvas_ref: NodeRef,
- layout: LayoutProps,
+ graph_space: GraphSpace,
selection: Option<(usize, usize)>,
view_range: Option<(usize, usize)>,
captured_pointer_id: Option<i32>,
@@ -130,28 +130,9 @@ pub struct PwtRRDGraph {
serie1_visible: bool,
}
-pub struct LayoutProps {
- width: usize,
- height: usize,
- grid_border: usize,
- left_offset: usize,
- bottom_offset: usize,
-}
-
-impl Default for LayoutProps {
- fn default() -> Self {
- Self {
- width: 800,
- height: 250,
- grid_border: 10,
- left_offset: 50,
- bottom_offset: 30,
- }
- }
-}
-
use pwt::widget::canvas::{Canvas, Circle, Group, Path, Rect, SvgLength, Text};
+use super::graph_space::{CoordinateRange, GraphSpace};
use super::series::{compute_fill_path, compute_outline_path};
use super::units::GraphKeyData;
use super::Series;
@@ -201,6 +182,13 @@ fn render_value(props: &RRDGraph, v: f64) -> String {
}
impl PwtRRDGraph {
+ fn update_grid_content(&mut self, ctx: &Context<Self>) {
+ let props = ctx.props();
+ let (time_data, data1, data2) = self.get_view_data(ctx);
+ self.graph_space
+ .update(time_data, &[data1, data2], props.include_zero, props.binary);
+ }
+
fn get_view_data<'a>(&self, ctx: &'a Context<Self>) -> (&'a [i64], &'a [f64], &'a [f64]) {
let props = ctx.props();
@@ -232,37 +220,17 @@ impl PwtRRDGraph {
fn create_graph(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
- let layout = &self.layout;
-
let (data0, data1, data2) = self.get_view_data(ctx);
let GraphKeyData {
data_min,
data_max,
data_interval,
- data_range,
- time_min,
time_max,
time_interval,
start_time,
- time_range,
- } = GraphKeyData::new(data0, &[data1, data2], props.include_zero, props.binary);
-
- let compute_x = {
- let width = (layout.width - layout.left_offset - layout.grid_border * 2) as f64;
- move |t: i64| -> f64 {
- (layout.left_offset + layout.grid_border) as f64
- + ((t - time_min) as f64 * width) / time_range as f64
- }
- };
-
- let compute_y = {
- let height = (layout.height - layout.bottom_offset - layout.grid_border * 2) as f64;
- move |value: f64| -> f64 {
- (layout.height - layout.grid_border - layout.bottom_offset) as f64
- - ((value - data_min) * height) / data_range
- }
- };
+ ..
+ } = self.graph_space.graph_data;
let mut grid_path = String::new();
@@ -270,12 +238,11 @@ impl PwtRRDGraph {
let mut time_labels: Vec<Html> = Vec::new();
if !data0.is_empty() {
- let x0 = compute_x(time_min) - (layout.grid_border as f64);
- let x1 = compute_x(time_max) + (layout.grid_border as f64);
+ let (x0, x1) = self.graph_space.get_x_range(CoordinateRange::OutsideBorder);
let mut v = data_min;
while v <= data_max {
- let y = compute_y(v);
+ let y = self.graph_space.compute_y(v);
grid_path.push_str(&format!("M {:.1} {:.1} L {:.1} {:.1}", x0, y, x1, y));
let label = render_value(props, v);
@@ -293,13 +260,12 @@ impl PwtRRDGraph {
}
let mut t = start_time;
- let ymax = compute_y(data_max) - (layout.grid_border as f64);
- let ymin = compute_y(data_min) + (layout.grid_border as f64);
+ let (ymin, ymax) = self.graph_space.get_y_range(CoordinateRange::OutsideBorder);
let mut last_date = String::new();
while t <= time_max {
- let x = compute_x(t);
+ let x = self.graph_space.compute_x(t);
grid_path.push_str(&format!("M {:.1} {:.1} L {:.1} {:.1}", x, ymin, x, ymax));
let (time, date) = format_time(t);
@@ -349,9 +315,8 @@ impl PwtRRDGraph {
);
if self.serie0_visible && props.serie0.is_some() {
- let path = compute_outline_path(data0, data1, compute_x, compute_y);
- let pos_fill_path =
- compute_fill_path(data0, data1, data_min, data_max, compute_x, compute_y);
+ let path = compute_outline_path(data0, data1, &self.graph_space);
+ let pos_fill_path = compute_fill_path(data0, data1, &self.graph_space);
children.extend(vec![
Path::new()
@@ -368,9 +333,8 @@ impl PwtRRDGraph {
}
if self.serie1_visible && props.serie1.is_some() {
- let path = compute_outline_path(data0, data2, compute_x, compute_y);
- let pos_fill_path =
- compute_fill_path(data0, data2, data_min, data_max, compute_x, compute_y);
+ let path = compute_outline_path(data0, data2, &self.graph_space);
+ let pos_fill_path = compute_fill_path(data0, data2, &self.graph_space);
children.extend(vec![
Path::new()
@@ -392,15 +356,15 @@ impl PwtRRDGraph {
match (data0.get(start), data0.get(end)) {
(Some(start_data), Some(end_data)) => {
- let mut start_x = compute_x(*start_data);
- let mut end_x = compute_x(*end_data);
+ let mut start_x = self.graph_space.compute_x(*start_data);
+ let mut end_x = self.graph_space.compute_x(*end_data);
if start_x > end_x {
std::mem::swap(&mut start_x, &mut end_x);
}
- let start_y = compute_y(data_min);
- let end_y = compute_y(data_max);
+ let (start_y, end_y) =
+ self.graph_space.get_y_range(CoordinateRange::InsideBorder);
children.push(
Rect::new()
@@ -423,8 +387,8 @@ impl PwtRRDGraph {
if let Some(t) = data0.get(idx) {
if let Some(v) = data1.get(idx) {
if v.is_finite() {
- let px = compute_x(*t) as f32;
- let py = compute_y(*v) as f32;
+ let px = self.graph_space.compute_x(*t) as f32;
+ let py = self.graph_space.compute_y(*v) as f32;
children.push(
Circle::new()
.key("selection-circle1")
@@ -437,8 +401,8 @@ impl PwtRRDGraph {
}
if let Some(v) = data2.get(idx) {
if v.is_finite() {
- let px = compute_x(*t) as f32;
- let py = compute_y(*v) as f32;
+ let px = self.graph_space.compute_x(*t) as f32;
+ let py = self.graph_space.compute_y(*v) as f32;
children.push(
Circle::new()
.key("selection-circle2")
@@ -451,18 +415,17 @@ impl PwtRRDGraph {
}
}
- let max_y = compute_y(data_min);
- let min_x = self.layout.left_offset + self.layout.grid_border;
- let max_x = self.layout.width - self.layout.grid_border;
+ let (min_y, _) = self.graph_space.get_y_range(CoordinateRange::InsideBorder);
+ let (min_x, max_x) = self.graph_space.get_x_range(CoordinateRange::InsideBorder);
let x = x.max(min_x as i32).min(max_x as i32);
- let y = y.min(max_y as i32);
+ let y = y.min(min_y as i32);
children.push(
Path::new()
.key("cross")
.class("pwt-rrd-cross")
- .d(format!("M {x} 0 L {x} {max_y} M {min_x} {y} L {max_x} {y}"))
+ .d(format!("M {x} 0 L {x} {min_y} M {min_x} {y} L {max_x} {y}"))
.into(),
);
}
@@ -470,8 +433,8 @@ impl PwtRRDGraph {
Canvas::new()
.node_ref(self.canvas_ref.clone())
.class("pwt-rrd-svg")
- .width(layout.width)
- .height(layout.height)
+ .width(self.graph_space.get_width())
+ .height(self.graph_space.get_height())
.children(children)
.ondblclick(ctx.link().callback(|_| Msg::ClearViewRange))
.onpointerenter(ctx.link().callback(|_| Msg::PointerEnter))
@@ -503,16 +466,7 @@ impl PwtRRDGraph {
}
fn offset_to_time_index(&self, x: i32, data0: &[i64]) -> usize {
- let layout = &self.layout;
- let width = (layout.width - layout.left_offset - layout.grid_border * 2) as f64;
-
- let start_time: i64 = *data0.first().unwrap_or(&0);
- let end_time: i64 = *data0.last().unwrap_or(&0);
- let time_span: i64 = end_time - start_time;
-
- let fraction: f64 = ((x - (layout.left_offset + layout.grid_border) as i32) as f64) / width;
-
- let t: i64 = ((fraction * (time_span as f64)) as i64) + start_time;
+ let t = self.graph_space.original_x(x as f64);
let start_index = data0.partition_point(|&x| x < t);
// Select nearest point
@@ -543,11 +497,11 @@ impl Component for PwtRRDGraph {
fn create(ctx: &Context<Self>) -> Self {
ctx.link().send_message(Msg::Reload);
- Self {
+ let mut this = Self {
node_ref: NodeRef::default(),
size_observer: None,
canvas_ref: NodeRef::default(),
- layout: LayoutProps::default(),
+ graph_space: GraphSpace::default(),
selection: None,
view_range: None,
captured_pointer_id: None,
@@ -557,7 +511,10 @@ impl Component for PwtRRDGraph {
y_label_ref: NodeRef::default(),
serie0_visible: true,
serie1_visible: true,
- }
+ };
+
+ this.update_grid_content(ctx);
+ this
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
@@ -566,7 +523,8 @@ impl Component for PwtRRDGraph {
Msg::Reload => true,
Msg::ViewportResize(width, _height) => {
if width > 0.0 {
- self.layout.width = width as usize;
+ self.graph_space.set_width(width as usize);
+ self.update_grid_content(ctx);
}
true
}
@@ -582,14 +540,17 @@ impl Component for PwtRRDGraph {
self.serie0_visible = true;
}
}
+ self.update_grid_content(ctx);
true
}
Msg::ClearViewRange => {
self.view_range = None;
+ self.update_grid_content(ctx);
true
}
Msg::AdjustLeftOffset(offset) => {
- self.layout.left_offset = offset;
+ self.graph_space.set_left_offset(offset);
+ self.update_grid_content(ctx);
true
}
Msg::PointerEnter => {
@@ -656,6 +617,7 @@ impl Component for PwtRRDGraph {
}
None => None,
};
+ self.update_grid_content(ctx);
true
}
@@ -751,7 +713,7 @@ impl Component for PwtRRDGraph {
panel.into()
}
- fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
+ fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
let props = ctx.props();
// clamp view range to the new time data range
@@ -765,6 +727,14 @@ impl Component for PwtRRDGraph {
}
}
+ // we need to recalculate the grid content when the series or time data changes
+ if props.serie0 != old_props.serie0
+ || props.serie1 != old_props.serie1
+ || props.time_data != old_props.time_data
+ {
+ self.update_grid_content(ctx);
+ }
+
true
}
@@ -786,7 +756,7 @@ impl Component for PwtRRDGraph {
if let Some(el) = self.y_label_ref.cast::<web_sys::SvgsvgElement>() {
if let Ok(bbox) = el.get_b_box() {
let offset = (bbox.width() + 10.0) as usize;
- if self.layout.left_offset != offset {
+ if self.graph_space.get_left_offset() != offset {
ctx.link().send_message(Msg::AdjustLeftOffset(offset));
}
}
diff --git a/src/rrd/graph_space.rs b/src/rrd/graph_space.rs
new file mode 100644
index 0000000..cd75177
--- /dev/null
+++ b/src/rrd/graph_space.rs
@@ -0,0 +1,147 @@
+use super::units::GraphKeyData;
+
+// Holds the basic data necessary for the SVG Layout
+struct LayoutProps {
+ width: usize,
+ height: usize,
+ grid_border: usize,
+ left_offset: usize,
+ bottom_offset: usize,
+ inner_width: usize,
+ inner_height: usize,
+}
+
+impl Default for LayoutProps {
+ fn default() -> Self {
+ Self {
+ width: 800,
+ height: 250,
+ grid_border: 10,
+ left_offset: 50,
+ bottom_offset: 30,
+ inner_width: 0,
+ inner_height: 0,
+ }
+ }
+}
+
+// maps value in range 0.0..=1.0 to SVG coordinates on the y axis
+fn map_relative_to_y(input: f64, layout: &LayoutProps) -> f64 {
+ #[cfg(debug_assertions)]
+ assert!((0.0..=1.0).contains(&input), "input: {input}");
+ layout.inner_height as f64 * (1.0 - input) + layout.grid_border as f64
+}
+
+// maps value in range 0.0..=1.0 to SVG coordinates on the x axis
+fn map_relative_to_x(input: f64, layout: &LayoutProps) -> f64 {
+ #[cfg(debug_assertions)]
+ assert!((0.0..=1.0).contains(&input), "input: {input}");
+ layout.inner_width as f64 * input + (layout.left_offset + layout.grid_border) as f64
+}
+
+/// Holds all necessary information to calculate between data space and svg space
+#[derive(Default)]
+pub struct GraphSpace {
+ layout: LayoutProps,
+ pub graph_data: GraphKeyData,
+}
+
+/// Options for getting the boundaries of the SVG coordinates
+pub enum CoordinateRange {
+ /// Coordinates will be inside the border
+ InsideBorder,
+ /// Coordinates can include the border
+ OutsideBorder,
+}
+
+impl GraphSpace {
+ /// Update the graph space with new graph data
+ pub fn update(&mut self, time_data: &[i64], data: &[&[f64]], include_zero: bool, binary: bool) {
+ self.graph_data = GraphKeyData::new(time_data, data, include_zero, binary);
+ }
+
+ /// Converts from data space to svg space on the x axis
+ pub fn compute_x(&self, x: i64) -> f64 {
+ map_relative_to_x(
+ (x - self.graph_data.time_min) as f64 / self.graph_data.time_range as f64,
+ &self.layout,
+ )
+ }
+
+ /// Converts from data space to svg space on the y axis
+ pub fn compute_y(&self, y: f64) -> f64 {
+ map_relative_to_y(
+ (y - self.graph_data.data_min) / self.graph_data.data_range,
+ &self.layout,
+ )
+ }
+
+ /// Returns the minimum and maximum coordinates for the x axis
+ pub fn get_x_range(&self, opts: CoordinateRange) -> (f64, f64) {
+ let mut min = map_relative_to_x(0.0, &self.layout);
+ if let CoordinateRange::OutsideBorder = opts {
+ min -= self.layout.grid_border as f64;
+ }
+ let mut max = map_relative_to_x(1.0, &self.layout);
+ if let CoordinateRange::OutsideBorder = opts {
+ max += self.layout.grid_border as f64;
+ }
+ (min, max)
+ }
+
+ /// Returns the minimum and maximum coordinates for the y axis
+ pub fn get_y_range(&self, opts: CoordinateRange) -> (f64, f64) {
+ let mut min = map_relative_to_y(0.0, &self.layout);
+ if let CoordinateRange::OutsideBorder = opts {
+ min += self.layout.grid_border as f64;
+ }
+ let mut max = map_relative_to_y(1.0, &self.layout);
+ if let CoordinateRange::OutsideBorder = opts {
+ max -= self.layout.grid_border as f64;
+ }
+ (min, max)
+ }
+
+ /// Converts back from svg space to data space for the x axis
+ pub fn original_x(&self, x: f64) -> i64 {
+ let layout = &self.layout;
+ let width = layout.inner_width as f64;
+ let fraction: f64 = (x - (layout.left_offset + layout.grid_border) as f64) / width;
+
+ ((fraction * (self.graph_data.time_range as f64)) as i64) + self.graph_data.time_min
+ }
+
+ /// Returns the complete current width of the graph
+ pub fn get_width(&self) -> usize {
+ self.layout.width
+ }
+
+ /// Returns the complete current height of the graph
+ pub fn get_height(&self) -> usize {
+ self.layout.height
+ }
+
+ fn update_inner_size(&mut self) {
+ let layout = &mut self.layout;
+ layout.inner_width = layout.width - layout.left_offset - layout.grid_border * 2;
+ layout.inner_height = layout.height - layout.bottom_offset - layout.grid_border * 2;
+ }
+
+ /// Updates the width of the layout, recalculates all necessary fields
+ pub fn set_width(&mut self, width: usize) {
+ self.layout.width = width;
+ self.update_inner_size();
+ }
+
+ /// Updates the left offset of the layout, recalculates all necessary fields
+ pub fn set_left_offset(&mut self, offset: usize) {
+ self.layout.left_offset = offset;
+ self.update_inner_size();
+ }
+
+ /// Returns the left offset of the graph. This is useful for dynamically
+ /// updating the space for the value labels
+ pub fn get_left_offset(&self) -> usize {
+ self.layout.left_offset
+ }
+}
diff --git a/src/rrd/mod.rs b/src/rrd/mod.rs
index fe63ff5..b55ac3f 100644
--- a/src/rrd/mod.rs
+++ b/src/rrd/mod.rs
@@ -1,6 +1,8 @@
mod graph;
pub use graph::*;
+pub(crate) mod graph_space;
+
pub(crate) mod series;
pub use series::Series;
diff --git a/src/rrd/series.rs b/src/rrd/series.rs
index 8807598..a210a42 100644
--- a/src/rrd/series.rs
+++ b/src/rrd/series.rs
@@ -1,5 +1,8 @@
use yew::AttrValue;
+use super::graph_space::GraphSpace;
+
+#[derive(PartialEq)]
/// Represents a series of data for an [`crate::RRDGraph`]
pub struct Series {
pub label: AttrValue,
@@ -18,31 +21,26 @@ impl Series {
/// Calculate the outline path of a series of [`f64`] data for [`i64`] points in time.
///
/// The line will not be drawn for points that are missing
-pub fn compute_outline_path(
- time_data: &[i64],
- values: &[f64],
- compute_x: impl Fn(i64) -> f64,
- compute_y: impl Fn(f64) -> f64,
-) -> String {
+pub fn compute_outline_path(time_data: &[i64], values: &[f64], graph_space: &GraphSpace) -> String {
let mut path = String::new();
let mut last_undefined = true;
for (i, t) in time_data.iter().enumerate() {
let value = *values.get(i).unwrap_or(&f64::NAN);
- let x = compute_x(*t);
+ let x = graph_space.compute_x(*t);
if last_undefined {
if value.is_nan() {
continue;
}
last_undefined = false;
- let y = compute_y(value);
+ let y = graph_space.compute_y(value);
path.push_str(&format!(" M {:.1} {:.1}", x, y));
} else {
if value.is_nan() {
last_undefined = true;
continue;
}
- let y = compute_y(value);
+ let y = graph_space.compute_y(value);
path.push_str(&format!(" L {:.1} {:.1}", x, y));
}
}
@@ -52,28 +50,19 @@ pub fn compute_outline_path(
/// Calculate the fill path for a series of [`f64`] points for [`i64`] points in time.
///
/// The area will not be filled for points that are missing
-pub fn compute_fill_path(
- time_data: &[i64],
- values: &[f64],
- min_data: f64,
- max_data: f64,
- compute_x: impl Fn(i64) -> f64,
- compute_y: impl Fn(f64) -> f64,
-) -> String {
- let mut y0 = compute_y(0.0);
- if min_data > 0.0 {
- y0 = compute_y(min_data)
- }
- if max_data < 0.0 {
- y0 = compute_y(max_data)
- }
+pub fn compute_fill_path(time_data: &[i64], values: &[f64], graph_space: &GraphSpace) -> String {
+ let y0 = graph_space.compute_y(
+ 0.0_f64
+ .max(graph_space.graph_data.data_min)
+ .min(graph_space.graph_data.data_max),
+ );
let mut path = String::new();
let mut last_undefined = true;
for i in 0..time_data.len() {
let t = time_data[i];
let value = *values.get(i).unwrap_or(&f64::NAN);
- let x = compute_x(t);
+ let x = graph_space.compute_x(t);
if last_undefined {
if value.is_nan() {
@@ -84,7 +73,7 @@ pub fn compute_fill_path(
} else if value.is_nan() {
last_undefined = true;
let x = if i > 0 {
- compute_x(time_data[i - 1])
+ graph_space.compute_x(time_data[i - 1])
} else {
x
};
@@ -92,13 +81,13 @@ pub fn compute_fill_path(
continue;
}
- let y = compute_y(value);
+ let y = graph_space.compute_y(value);
path.push_str(&format!(" L {:.1} {:.1}", x, y));
}
if let Some(t) = time_data.last() {
if !last_undefined {
- let x = compute_x(*t);
+ let x = graph_space.compute_x(*t);
path.push_str(&format!(" L {:.1} {:.1}", x, y0));
}
}
--
2.39.5
_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel
next prev parent reply other threads:[~2025-05-30 12:22 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-30 12:21 [yew-devel] [PATCH yew-comp 00/20] refactor and improve rrd graph code Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 01/20] remove old rrd uplot code Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 02/20] rrd: refactor code for compute_min_max Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 03/20] rrd: move into own module Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 04/20] rrd: move unit calculation to " Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 05/20] rrd: units: add tests Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 06/20] rrd: units: simplify calculations for get_grid_unit_base Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 07/20] rrd: remove unnecessary `no_data` field Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 08/20] rrd: align tooltip directly to pointer position Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 09/20] rrd: use 'cross_pos' state instead of 'draw_cross' Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 10/20] rrd: give all elements in svg keys Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 11/20] rrd: simplify toggle Msg Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 12/20] rrd: remove wrongly annotated lifetime Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 13/20] rrd: refactor series related struct and functions into own module Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 14/20] rrd: clamp view range when time_data changes Dominik Csapak
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 15/20] rrd: refactor grid data computation Dominik Csapak
2025-05-30 12:21 ` Dominik Csapak [this message]
2025-05-30 12:21 ` [yew-devel] [PATCH yew-comp 17/20] rrd: precalculate the grid line and label positions Dominik Csapak
2025-05-30 12:22 ` [yew-devel] [PATCH yew-comp 18/20] rrd: calculate series svg data only when necessary Dominik Csapak
2025-05-30 12:22 ` [yew-devel] [PATCH yew-comp 19/20] rrd: refactor selection rectangle calculation Dominik Csapak
2025-05-30 12:22 ` [yew-devel] [PATCH yew-comp 20/20] rrd: refactor the cross position calculation Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250530122202.2779300-17-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=yew-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal