aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakobst1n <jakob.stendahl@outlook.com>2024-06-04 05:00:57 +0200
committerjakobst1n <jakob.stendahl@outlook.com>2024-06-04 05:00:57 +0200
commit6e9628e2d20c09eaeb7eedfe2f7278de0b65a09f (patch)
tree745677d456a80d6e3d3d80d66eea1bf5d0c4b636
downloadtextgraph-6e9628e2d20c09eaeb7eedfe2f7278de0b65a09f.tar.gz
textgraph-6e9628e2d20c09eaeb7eedfe2f7278de0b65a09f.zip
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml8
-rw-r--r--src/graph.rs142
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs31
6 files changed, 190 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..53dcf10
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "textgraph"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..eaeeca4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "textgraph"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/graph.rs b/src/graph.rs
new file mode 100644
index 0000000..f837f68
--- /dev/null
+++ b/src/graph.rs
@@ -0,0 +1,142 @@
+const _BRAILLE_1: char = '⣿';
+
+const ASCII_0: char = '─';
+const ASCII_1: char = '│';
+const ASCII_2: char = '╭';
+const ASCII_3: char = '╰';
+const ASCII_4: char = '╮';
+const ASCII_7: char = '╯';
+
+const BRAILLE_1_0: char = '⡀';
+const BRAILLE_1_1: char = '⣀';
+const BRAILLE_1_2: char = '⣀';
+const BRAILLE_2_0: char = '⡄';
+const BRAILLE_3_0: char = '⡆';
+const BRAILLE_4_0: char = '⡇';
+
+
+/*
+ ╭────────╮
+╭─╯ │
+╰ ╰╮
+ ╰────────
+*/
+
+#[derive(Debug)]
+pub struct GraphOptions {
+ pub width: f64,
+ pub height: f64,
+}
+
+#[derive(Debug)]
+pub struct SeriesAspects<T> {
+ max: T,
+ min: T,
+}
+
+pub trait SeriesTraits: std::cmp::PartialOrd + Clone + std::ops::Div + std::ops::Sub {}
+impl<T: std::cmp::PartialOrd + Clone + std::ops::Div + std::ops::Sub> SeriesTraits for T {}
+
+impl<T: SeriesTraits> From<&Vec<T>> for SeriesAspects<T> {
+ fn from(series: &Vec<T>) -> SeriesAspects<T> {
+ let mut it = series.iter();
+ let first = it.next();
+ let mut min = first.expect("TG2");
+ let mut max = first.expect("TG3");
+ while let Some(i) = it.next() {
+ if i < min {
+ min = i;
+ }
+ if i > max {
+ max = i;
+ }
+ }
+ SeriesAspects {
+ max: max.clone(),
+ min: min.clone(),
+ }
+ }
+}
+
+
+pub fn downsample(series: &[f64], column_count: usize) -> Vec<f64> {
+ let factor = series.len() as f64 / column_count as f64;
+ (0..column_count).map(|i| series[(i as f64 * factor) as usize]).collect()
+}
+
+pub fn interpolate(series: &[f64], marks: &[f64], column_count: usize) -> Vec<f64> {
+ let min_mark = marks.iter().cloned().fold(f64::INFINITY, f64::min);
+ let max_mark = marks.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
+ let step = (max_mark - min_mark) / (column_count as f64 - 1.0);
+ let mut interpolated_data = Vec::new();
+
+ for i in 0..column_count {
+ let target_mark = min_mark + i as f64 * step;
+ let mut j = 0;
+ while j < marks.len() - 1 && marks[j + 1] < target_mark {
+ j += 1;
+ }
+ let t0 = marks[j];
+ let t1 = marks[j + 1];
+ let d0 = series[j];
+ let d1 = series[j + 1];
+ let value = d0 + (d1 - d0) * (target_mark - t0) / (t1 - t0);
+ interpolated_data.push(value);
+ }
+
+ interpolated_data
+}
+
+fn scale(series: &[f64], row_count: usize) -> Vec<usize> {
+ let min_value = series.iter().cloned().fold(f64::INFINITY, f64::min);
+ let max_value = series.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
+ let scale_factor = (row_count - 1) as f64 / (max_value - min_value);
+ series.iter().map(|&y| ((y - min_value) * scale_factor).round() as usize).collect()
+}
+
+pub fn star(series: &[f64], options: &GraphOptions) -> String {
+ let scaled_data = scale(series, options.height as usize);
+ let mut graph = vec![vec![' '; options.width as usize]; options.height as usize];
+
+ for (i, &value) in scaled_data.iter().enumerate() {
+ let y = options.height as usize - value - 1; // Invert y-axis for ASCII graph
+ graph[y][i] = '*';
+ }
+
+ graph.iter().map(|row| row.iter().collect::<String>()).collect::<Vec<String>>().join("\n")
+}
+
+pub fn ascii_trailing(series: &[f64], options: &GraphOptions) -> String {
+ let scaled_data = scale(series, options.height as usize);
+ let mut graph = vec![vec![' '; options.width as usize]; options.height as usize];
+
+ for (i, &value) in scaled_data.iter().enumerate() {
+ let y = options.height as usize - value - 1; // Invert y-axis for ASCII graph
+ graph[y][i] = '*';
+ }
+
+ graph.iter().map(|row| row.iter().collect::<String>()).collect::<Vec<String>>().join("\n")
+}
+
+pub fn braille(series: &Vec<f64>, options: &GraphOptions) -> String {
+ let aspects = SeriesAspects::from(series);
+ let canvas = String::with_capacity((options.width * options.height) as usize);
+
+
+ /*
+ r = (max - min)
+ r' = (max' - min')
+ y' = (((y - min) * r') / r) + min'
+ */
+ let r = aspects.max - aspects.min;
+ let r_marked = options.height;
+
+ let norm_after = options.height;
+
+ //for (x, y) in series.iter().enumerate() {
+ // let y = norm(y.clone(), 0.0, options.height);
+ // let x = norm(x.clone(), 0.0, options.width);
+ //}
+
+ String::from("")
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6f94350
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1 @@
+pub mod graph;
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..4cae5be
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,31 @@
+use textgraph::graph;
+
+fn main() {
+ let mut line: Vec<f64> = Vec::new();
+ let mut marks: Vec<f64> = Vec::new();
+ for i in 0..500 {
+ line.push((i as f64 * std::f64::consts::PI / 120.0).sin());
+ marks.push(i as f64);
+ }
+
+ // Choose one of the methods based on sample speed:
+ //let downsampled_data = graph::downsample(&line, 100);
+ let interpolated_data = graph::interpolate(&line, &marks, 100);
+
+
+ //let processed_data = if marks.windows(2).all(|w| w[1] - w[0] == w[0] - w[1]) {
+ // downsample(&series, options.width)
+ //} else {
+ // interpolate(&series, &marks, options.width)
+ //};
+
+
+ let g = graph::ascii_trailing(
+ &interpolated_data,
+ &graph::GraphOptions {
+ width: 100.0,
+ height: 30.0,
+ },
+ );
+ println!("{}", g);
+}