From 54afb64ae83bf9a0b3d271dab6191b62ead1d664 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 1 Oct 2024 21:22:41 -0600 Subject: [PATCH 1/8] the line shrinks after me also 1d or 2d graphs are possible --- src/main.rs | 122 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index c1d6031..4d5575e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use macroquad::prelude::*; const DOT_RADIUS: f32 = 1.0; +#[derive(Clone,Copy)] struct Dot { x: f32, y: f32, @@ -48,46 +49,119 @@ impl DebugWindow { } +enum Axis { + One, + Two +} + +struct Tail { + points: [Dot; 500], + head: usize, + axis: Axis, + x_offset: f32, + y_offset: f32, + px_per_s: f32, +} +impl Tail { + fn new(axis: Axis) -> Self { + + let (x_offset, y_offset) = match axis { + Axis::One => (0., screen_height() /2.), + Axis::Two => (screen_width() /2., screen_height() /2.), + }; + + Self { + // start all the dots off screen + points: [Dot::new(-1.,-1.); 500], + head: 0, + axis, + x_offset, + y_offset, + px_per_s: 500., + } + } + fn place_next(&mut self, delta_time: f32, x_displace: f32, y_displace: f32) { + match self.axis { + Axis::One => { + // x will scroll while y gets displaced + if self.x_offset >= screen_width() { + self.x_offset = 0.; + } else { + self.x_offset = self.x_offset + (delta_time * self.px_per_s) + } + self.push(Dot::new(self.x_offset, self.y_offset+y_displace)); + }, + Axis::Two => { + // both x and y will get displaced + self.push(Dot::new(self.x_offset+x_displace,self.y_offset+y_displace)) + }, + } + } + fn push(&mut self, dot: Dot) { + if self.head >= self.points.len() -1 { + self.head = 0 + } else { + self.head += 1 + } + + self.points[self.head] = dot; + } + fn draw(&self) { + // TODO fadding alpha? + self.points.iter().for_each(|d| d.draw()); + } + fn dots(&self) -> usize { + self.points.len() + } +} + #[macroquad::main("Graph")] async fn main() { // Dot params - let y_offset = screen_height() / 2.0; - let mut x_offset = screen_width(); - let mut trail: Vec = Vec::new(); + let mut y_displacement = 0.; + let mut x_displacement = 0.; + let mut tail = Tail::new(Axis::Two); // Selection box let mut inital_x_pos = 0.; let mut inital_y_pos = 0.; // Tooling - let mut show_debug = false; - let mut px_per_s: f32 = 100.; - + let mut show_debug = true; + let mut px_per_s: f32 = 1000.; let stdin = stdin(); let handle= stdin.lock(); + loop { clear_background(BLACK); - // handle. let delta_time = get_frame_time(); - if x_offset >= screen_width() { - // wrap once you've hit the end of the line - x_offset = 0.; - trail.clear(); - } else { - x_offset = x_offset + (delta_time * px_per_s); + tail.place_next(delta_time, x_displacement, y_displacement); + tail.draw(); + + if is_key_down(KeyCode::S) { + if y_displacement < screen_height()/2. { + y_displacement += 10.; + } } - - let x = 0.+x_offset; - let y = 0.+y_offset; - - let dot = Dot::new(x,y); - trail.push(dot); - trail.iter().for_each(|d| d.draw()); - - if is_key_pressed(KeyCode::D) { + if is_key_down(KeyCode::W) { + if y_displacement > -(screen_height()/2.) { + y_displacement -= 10.; + } + } + if is_key_down(KeyCode::D) { + if x_displacement < screen_width()/2. { + x_displacement += 10.; + } + } + if is_key_down(KeyCode::A) { + if x_displacement > -(screen_width()/2.) { + x_displacement -= 10.; + } + } + if is_key_pressed(KeyCode::F3) { show_debug = !show_debug; } if is_key_pressed(KeyCode::Up) { @@ -112,9 +186,9 @@ async fn main() { if show_debug { let mut debug = DebugWindow::new(); debug.add_line(format!("FPS {}, Latency {:.2}ms", get_fps(), delta_time * 1000.0)); - debug.add_line(format!("px/s {}", px_per_s)); - debug.add_line(format!("Dots {}", trail.len())); + debug.add_line(format!("Dots {}", tail.dots())); debug.add_line(format!("Cursor Pos {:?}", mouse_position())); + debug.add_line(format!("px/s {}", px_per_s)); debug.draw(); } -- 2.49.1 From 1b09b4e235cd1c6546a5bb5934a70ee6c15d4951 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 1 Oct 2024 22:20:23 -0600 Subject: [PATCH 2/8] cleanup --- src/main.rs | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4d5575e..f9536b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,20 +3,20 @@ use std::io::stdin; use macroquad::prelude::*; -const DOT_RADIUS: f32 = 1.0; +const DOT_RADIUS: f32 = 2.0; +const TAIL_LEN: usize = 250; #[derive(Clone,Copy)] struct Dot { x: f32, y: f32, } - impl Dot { fn new(x: f32, y: f32) -> Self { Self {x,y} } - fn draw(&self) { - draw_circle(self.x, self.y,DOT_RADIUS, GREEN); + fn draw(&self, color: Color) { + draw_circle(self.x, self.y,DOT_RADIUS, color); } } @@ -48,36 +48,35 @@ impl DebugWindow { } } - enum Axis { One, Two } - struct Tail { - points: [Dot; 500], + points: [Dot; TAIL_LEN], head: usize, axis: Axis, + /// What the tail treats as 0 x_offset: f32, + /// What the tail treats as 0 y_offset: f32, + /// How fast X scrolls when using a since axis tail px_per_s: f32, } impl Tail { fn new(axis: Axis) -> Self { - let (x_offset, y_offset) = match axis { Axis::One => (0., screen_height() /2.), Axis::Two => (screen_width() /2., screen_height() /2.), }; - Self { // start all the dots off screen - points: [Dot::new(-1.,-1.); 500], + points: [Dot::new(-1.,-1.); TAIL_LEN], head: 0, axis, x_offset, y_offset, - px_per_s: 500., + px_per_s: 1000., } } fn place_next(&mut self, delta_time: f32, x_displace: f32, y_displace: f32) { @@ -107,8 +106,19 @@ impl Tail { self.points[self.head] = dot; } fn draw(&self) { - // TODO fadding alpha? - self.points.iter().for_each(|d| d.draw()); + self.points.into_iter().enumerate().for_each(|(index, dot) |{ + let virtual_index = if index > self.head { + index-self.head + } else { + index + (self.points.len() - self.head) + }; + + let faded = virtual_index as f32 / self.points.len() as f32; + let faded_inverse = 1. - faded; + + // dots fade + dot.draw(Color { r: faded_inverse, g: faded, b: 0.2, a: faded+0.1}); + }); } fn dots(&self) -> usize { self.points.len() @@ -120,7 +130,7 @@ async fn main() { // Dot params let mut y_displacement = 0.; let mut x_displacement = 0.; - let mut tail = Tail::new(Axis::Two); + let mut tail = Tail::new(Axis::One); // Selection box let mut inital_x_pos = 0.; @@ -128,7 +138,6 @@ async fn main() { // Tooling let mut show_debug = true; - let mut px_per_s: f32 = 1000.; let stdin = stdin(); let handle= stdin.lock(); @@ -164,12 +173,6 @@ async fn main() { if is_key_pressed(KeyCode::F3) { show_debug = !show_debug; } - if is_key_pressed(KeyCode::Up) { - px_per_s += 5.; - } - if is_key_pressed(KeyCode::Down) { - px_per_s -= 5.; - } // selection box if is_mouse_button_pressed(MouseButton::Left) { (inital_x_pos, inital_y_pos) = mouse_position(); @@ -188,7 +191,6 @@ async fn main() { debug.add_line(format!("FPS {}, Latency {:.2}ms", get_fps(), delta_time * 1000.0)); debug.add_line(format!("Dots {}", tail.dots())); debug.add_line(format!("Cursor Pos {:?}", mouse_position())); - debug.add_line(format!("px/s {}", px_per_s)); debug.draw(); } -- 2.49.1 From 35ae384a43c5cc64b446910ae2c9c6eaf2b323af Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 1 Oct 2024 22:35:18 -0600 Subject: [PATCH 3/8] Change from dots to lines This removes the need for Dot (now Point) to hold rendering logic --- src/main.rs | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/main.rs b/src/main.rs index f9536b5..265045d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,21 +3,18 @@ use std::io::stdin; use macroquad::prelude::*; -const DOT_RADIUS: f32 = 2.0; +const THICKNESS: f32 = 2.0; const TAIL_LEN: usize = 250; #[derive(Clone,Copy)] -struct Dot { +struct Point { x: f32, y: f32, } -impl Dot { +impl Point { fn new(x: f32, y: f32) -> Self { Self {x,y} } - fn draw(&self, color: Color) { - draw_circle(self.x, self.y,DOT_RADIUS, color); - } } struct DebugWindow { @@ -53,7 +50,7 @@ enum Axis { Two } struct Tail { - points: [Dot; TAIL_LEN], + points: [Point; TAIL_LEN], head: usize, axis: Axis, /// What the tail treats as 0 @@ -71,7 +68,7 @@ impl Tail { }; Self { // start all the dots off screen - points: [Dot::new(-1.,-1.); TAIL_LEN], + points: [Point::new(-1.,-1.); TAIL_LEN], head: 0, axis, x_offset, @@ -88,15 +85,15 @@ impl Tail { } else { self.x_offset = self.x_offset + (delta_time * self.px_per_s) } - self.push(Dot::new(self.x_offset, self.y_offset+y_displace)); + self.push(Point::new(self.x_offset, self.y_offset+y_displace)); }, Axis::Two => { // both x and y will get displaced - self.push(Dot::new(self.x_offset+x_displace,self.y_offset+y_displace)) + self.push(Point::new(self.x_offset+x_displace,self.y_offset+y_displace)) }, } } - fn push(&mut self, dot: Dot) { + fn push(&mut self, dot: Point) { if self.head >= self.points.len() -1 { self.head = 0 } else { @@ -106,21 +103,37 @@ impl Tail { self.points[self.head] = dot; } fn draw(&self) { - self.points.into_iter().enumerate().for_each(|(index, dot) |{ + self.points.into_iter().enumerate().for_each(|(index, point) |{ + // dots fade let virtual_index = if index > self.head { index-self.head } else { index + (self.points.len() - self.head) }; - let faded = virtual_index as f32 / self.points.len() as f32; let faded_inverse = 1. - faded; + let color = Color { r: faded_inverse, g: faded, b: 0.2, a: faded+0.1}; - // dots fade - dot.draw(Color { r: faded_inverse, g: faded, b: 0.2, a: faded+0.1}); + let prev = self.previous(index); + + if self.head == index { + draw_circle(point.x, point.y,THICKNESS*2., color); + } else if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { + draw_circle(point.x, point.y,THICKNESS/2., color); + } else { + draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); + } }); } - fn dots(&self) -> usize { + fn previous(&self, index: usize) -> Point { + let prev = if index > 0 { + index -1 + } else { + self.points.len() -1 + }; + self.points[prev] + } + fn points(&self) -> usize { self.points.len() } } @@ -189,7 +202,7 @@ async fn main() { if show_debug { let mut debug = DebugWindow::new(); debug.add_line(format!("FPS {}, Latency {:.2}ms", get_fps(), delta_time * 1000.0)); - debug.add_line(format!("Dots {}", tail.dots())); + debug.add_line(format!("Points {}", tail.points())); debug.add_line(format!("Cursor Pos {:?}", mouse_position())); debug.draw(); } -- 2.49.1 From 8babea285fb976f35efc6cfe6936af9038d4b096 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 1 Oct 2024 22:40:21 -0600 Subject: [PATCH 4/8] Fix edge cases when drawing lines --- src/main.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 265045d..427095b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use macroquad::prelude::*; const THICKNESS: f32 = 2.0; const TAIL_LEN: usize = 250; -#[derive(Clone,Copy)] +#[derive(Clone,Copy, PartialEq)] struct Point { x: f32, y: f32, @@ -117,9 +117,14 @@ impl Tail { let prev = self.previous(index); if self.head == index { + // Draw the head differently draw_circle(point.x, point.y,THICKNESS*2., color); } else if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { - draw_circle(point.x, point.y,THICKNESS/2., color); + // Don't have lines that cross the whole screen while using 1D graph + draw_circle(point.x, point.y,THICKNESS, color); + } else if prev == self.points[self.head] { + // Prevent the tail from connecting to the head + draw_circle(point.x, point.y,THICKNESS, color); } else { draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); } @@ -143,7 +148,7 @@ async fn main() { // Dot params let mut y_displacement = 0.; let mut x_displacement = 0.; - let mut tail = Tail::new(Axis::One); + let mut tail = Tail::new(Axis::Two); // Selection box let mut inital_x_pos = 0.; -- 2.49.1 From 6e02c6b46ee171ccc6f2bf68cdc9a5df994d3167 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 1 Oct 2024 23:25:46 -0600 Subject: [PATCH 5/8] Fix more edgecases --- src/main.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 427095b..e9c9fc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,12 +116,13 @@ impl Tail { let prev = self.previous(index); - if self.head == index { + if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { + // Don't have lines that cross the whole screen while using 1D graph + // draw_circle(point.x, point.y,THICKNESS, color); + } else if self.head == index { // Draw the head differently draw_circle(point.x, point.y,THICKNESS*2., color); - } else if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { - // Don't have lines that cross the whole screen while using 1D graph - draw_circle(point.x, point.y,THICKNESS, color); + draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); } else if prev == self.points[self.head] { // Prevent the tail from connecting to the head draw_circle(point.x, point.y,THICKNESS, color); @@ -148,7 +149,7 @@ async fn main() { // Dot params let mut y_displacement = 0.; let mut x_displacement = 0.; - let mut tail = Tail::new(Axis::Two); + let mut tail = Tail::new(Axis::One); // Selection box let mut inital_x_pos = 0.; -- 2.49.1 From 33291108bd46f25d087be0522280edc1c7411a7b Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 2 Oct 2024 02:26:53 -0600 Subject: [PATCH 6/8] It works --- Cargo.lock | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/main.rs | 77 +++++++++++++++++++++++++------- 3 files changed, 186 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88409af..f1d0703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "adler2" @@ -32,6 +32,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bytemuck" version = "1.18.0" @@ -56,6 +62,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crc32fast" version = "1.4.2" @@ -122,12 +134,51 @@ dependencies = [ "png", ] +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + [[package]] name = "libc" version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +[[package]] +name = "libudev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "macroquad" version = "0.4.13" @@ -186,6 +237,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -213,13 +275,19 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "png" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -246,6 +314,7 @@ name = "quad" version = "0.1.0" dependencies = [ "macroquad", + "serialport", ] [[package]] @@ -263,6 +332,30 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serialport" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba776acc8c373b9175829206229366273225436845c04f9c20aab8099960e2e" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "core-foundation-sys", + "io-kit-sys", + "libudev", + "mach2", + "nix", + "scopeguard", + "unescaper", + "winapi", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -289,12 +382,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ttf-parser" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" +[[package]] +name = "unescaper" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" +dependencies = [ + "thiserror", +] + [[package]] name = "unicode-ident" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index 10010e4..0aec73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] macroquad = "0.4.13" +serialport = "4.5.1" diff --git a/src/main.rs b/src/main.rs index e9c9fc4..8bc3a7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use core::f32; -use std::io::stdin; +use std::{io::stdin, time::Duration}; use macroquad::prelude::*; const THICKNESS: f32 = 2.0; -const TAIL_LEN: usize = 250; +const TAIL_LEN: usize = 500; #[derive(Clone,Copy, PartialEq)] struct Point { @@ -61,10 +61,10 @@ struct Tail { px_per_s: f32, } impl Tail { - fn new(axis: Axis) -> Self { + fn new(axis: Axis, y_axis_offset: f32) -> Self { let (x_offset, y_offset) = match axis { - Axis::One => (0., screen_height() /2.), - Axis::Two => (screen_width() /2., screen_height() /2.), + Axis::One => (0., y_axis_offset), + Axis::Two => (screen_width() /2., y_axis_offset), }; Self { // start all the dots off screen @@ -73,7 +73,7 @@ impl Tail { axis, x_offset, y_offset, - px_per_s: 1000., + px_per_s: 750., } } fn place_next(&mut self, delta_time: f32, x_displace: f32, y_displace: f32) { @@ -147,9 +147,10 @@ impl Tail { #[macroquad::main("Graph")] async fn main() { // Dot params - let mut y_displacement = 0.; - let mut x_displacement = 0.; - let mut tail = Tail::new(Axis::One); + let realtive_y_origin =screen_height()*0.75; + // FIXME realtive_x_origin doesn't work yet + let realtive_x_origin =0.; + let mut tail = Tail::new(Axis::One, realtive_y_origin); // Selection box let mut inital_x_pos = 0.; @@ -158,17 +159,60 @@ async fn main() { // Tooling let mut show_debug = true; - let stdin = stdin(); - let handle= stdin.lock(); - + // Serial + let port = serialport::available_ports() + .expect("No ports found!") + .iter() + .filter(|x| x.port_name.contains("ACM")) + .fold(None, |_, x| { + let x = serialport::new(x.port_name.clone(), 57600) + .timeout(Duration::from_millis(10)) + .open().expect("Failed to open port"); + Some(x) + }); + let mut port = port.expect("Failed to find valid serial port"); + let mut serial_buf: Vec = vec![0; 8]; + let mut plot_delta_time = 0.; loop { clear_background(BLACK); + match tail.axis { + Axis::One => { + draw_line(realtive_x_origin,realtive_y_origin,screen_width(),realtive_y_origin,1.,DARKGRAY); + }, + Axis::Two => { + // horizontal (x) + draw_line(0.,realtive_y_origin,screen_width(),realtive_y_origin,1.,DARKGRAY); + // vertical (y) + draw_line(realtive_x_origin,realtive_y_origin, realtive_x_origin,screen_height(),1.,DARKGRAY); + }, + } + let frame_delta_time = get_frame_time(); + let mut y_displacement = 0.; + let mut x_displacement = 0.; + // keep track of the delta time since last plot + plot_delta_time += frame_delta_time; - let delta_time = get_frame_time(); + if let Ok(x) = port.read(serial_buf.as_mut_slice()) { + let s: String = serial_buf.iter().take(x-1).map(|x| *x as char).collect(); + if let Ok(parse) = s.parse::() { + y_displacement = parse; + + // only draw when requied + tail.place_next(plot_delta_time, x_displacement, -y_displacement); + plot_delta_time = 0.; + } + } - tail.place_next(delta_time, x_displacement, y_displacement); tail.draw(); + // Info about the cursor locaiton + let (mouse_x, mouse_y) = mouse_position(); + + let size = measure_text(&format!("x{mouse_x}, y{}",-1.*(mouse_y-realtive_y_origin)), None, 30,1.); + draw_rectangle(mouse_x, mouse_y, size.width, -(size.height+5.), WHITE); + draw_text(&format!("x{}, y{}", mouse_x+realtive_x_origin,-1.*(mouse_y-realtive_y_origin)), mouse_x, mouse_y-5., 30., BLACK); + + if is_key_down(KeyCode::S) { if y_displacement < screen_height()/2. { y_displacement += 10.; @@ -207,9 +251,10 @@ async fn main() { // toggle debug box if show_debug { let mut debug = DebugWindow::new(); - debug.add_line(format!("FPS {}, Latency {:.2}ms", get_fps(), delta_time * 1000.0)); - debug.add_line(format!("Points {}", tail.points())); + debug.add_line(format!("FPS {:03}, Latency {:.2}ms", get_fps(), frame_delta_time * 1000.0)); + debug.add_line(format!("Tail Length {TAIL_LEN}, px/s {:.2}", tail.px_per_s)); debug.add_line(format!("Cursor Pos {:?}", mouse_position())); + debug.add_line(format!("Serial Port {}, Baud Rate {}", port.name().unwrap_or("Unknown".to_string()), port.baud_rate().unwrap_or(0))); debug.draw(); } -- 2.49.1 From 3604a9405bb0fc279f4557f85153d50c12ab0898 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 2 Oct 2024 16:57:01 -0600 Subject: [PATCH 7/8] Changed tails to graphs Graphs have their own realtive coordinate system, so they can be abitrarially moved across the scren :) --- src/main.rs | 214 ++++++++++++++++++++++------------------------------ 1 file changed, 92 insertions(+), 122 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8bc3a7b..57bdd2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use core::f32; -use std::{io::stdin, time::Duration}; +use std::time::Duration; use macroquad::prelude::*; +use serialport::SerialPort; const THICKNESS: f32 = 2.0; const TAIL_LEN: usize = 500; @@ -49,47 +50,66 @@ enum Axis { One, Two } -struct Tail { + +/// # Graph +/// Holds 1 contiguous line. Can be graphed on 1 or 2 axis. +struct Graph { points: [Point; TAIL_LEN], head: usize, - axis: Axis, + axises: Axis, /// What the tail treats as 0 - x_offset: f32, - /// What the tail treats as 0 - y_offset: f32, + head_tracker: f32, + /// The graph's local origin + x_origin: f32, + /// The graph's local origin + y_origin: f32, /// How fast X scrolls when using a since axis tail px_per_s: f32, } -impl Tail { - fn new(axis: Axis, y_axis_offset: f32) -> Self { - let (x_offset, y_offset) = match axis { - Axis::One => (0., y_axis_offset), - Axis::Two => (screen_width() /2., y_axis_offset), +impl Graph { + fn new(axises: Axis) -> Self { + let (x_offset, y_offset) = match axises { + Axis::One => { + (0., screen_height()*0.5) + }, + Axis::Two => { + (screen_width()/2.,screen_height()/2.) + }, }; + Self { // start all the dots off screen points: [Point::new(-1.,-1.); TAIL_LEN], head: 0, - axis, - x_offset, - y_offset, + axises, + x_origin: x_offset, + y_origin: y_offset, px_per_s: 750., + head_tracker: 0., } } - fn place_next(&mut self, delta_time: f32, x_displace: f32, y_displace: f32) { - match self.axis { + fn draw_axises(&self) { + // Horzontal line + // (0, y_origin) -> (max, y_origin) + draw_line(0.,self.y_origin,screen_width(),self.y_origin,1.,DARKGRAY); + // Vertical line + // (x_origin, 0) -> (x_origin, max) + draw_line(self.x_origin,0.,self.x_origin,screen_height(),1.,DARKGRAY); + } + fn place_next(&mut self, delta_time: f32, x_displacement: f32, y_displacement: f32) { + match self.axises { Axis::One => { // x will scroll while y gets displaced - if self.x_offset >= screen_width() { - self.x_offset = 0.; + if self.head_tracker >= screen_width() { + self.head_tracker = 0.; } else { - self.x_offset = self.x_offset + (delta_time * self.px_per_s) + self.head_tracker = self.head_tracker + (delta_time * self.px_per_s) } - self.push(Point::new(self.x_offset, self.y_offset+y_displace)); + self.push(Point::new(self.head_tracker, self.y_origin-y_displacement)); }, Axis::Two => { // both x and y will get displaced - self.push(Point::new(self.x_offset+x_displace,self.y_offset+y_displace)) + self.push(Point::new(self.x_origin+x_displacement,self.y_origin-y_displacement)) }, } } @@ -139,27 +159,65 @@ impl Tail { }; self.points[prev] } - fn points(&self) -> usize { - self.points.len() - } } #[macroquad::main("Graph")] async fn main() { // Dot params - let realtive_y_origin =screen_height()*0.75; - // FIXME realtive_x_origin doesn't work yet - let realtive_x_origin =0.; - let mut tail = Tail::new(Axis::One, realtive_y_origin); + let mut graph = Graph::new(Axis::One); - // Selection box - let mut inital_x_pos = 0.; - let mut inital_y_pos = 0.; - // Tooling let mut show_debug = true; + let mut plot_delta_time = 0.; // Serial + let mut port = get_serial_port(); + let mut serial_buf: Vec = vec![0; 8]; + loop { + clear_background(BLACK); + + graph.draw_axises(); + + let frame_delta_time = get_frame_time(); + // keep track of the delta time since last plot + plot_delta_time += frame_delta_time; + + if let Ok(x) = port.read(serial_buf.as_mut_slice()) { + let s: String = serial_buf.iter().take(x-1).map(|x| *x as char).collect(); + if let Ok(parse) = s.parse::() { + // Only draw when requied + graph.place_next(plot_delta_time, 0., parse); + plot_delta_time = 0.; + } + } + + graph.draw(); + + if is_key_pressed(KeyCode::F3) { + show_debug = !show_debug; + } + // toggle debug box + if show_debug { + // Debug cursor information + let (mouse_x, mouse_y) = mouse_position(); + let size = measure_text(&format!("x{mouse_x}, y{}",-(mouse_y-graph.y_origin)), None, 30,1.); + draw_rectangle(mouse_x, mouse_y+(size.offset_y/4.), size.width, -(size.height), WHITE); + draw_text(&format!("x{}, y{}", mouse_x+graph.x_origin,-(mouse_y-graph.y_origin)), mouse_x, mouse_y, 30., BLACK); + + // Debug textbox + let mut debug = DebugWindow::new(); + debug.add_line(format!("FPS {:04}, Frametime {:05.2}ms", get_fps(), frame_delta_time * 1000.0)); + debug.add_line(format!("Tail Length {TAIL_LEN}, px/s {:.2}", graph.px_per_s)); + debug.add_line(format!("Cursor Pos {:?}", mouse_position())); + debug.add_line(format!("Serial Port {}, Baud Rate {}", port.name().unwrap_or("Unknown".to_string()), port.baud_rate().map_or(String::from("N/A"), |f| f.to_string()))); + debug.draw(); + } + + next_frame().await; + } +} + +fn get_serial_port() -> Box { let port = serialport::available_ports() .expect("No ports found!") .iter() @@ -170,95 +228,7 @@ async fn main() { .open().expect("Failed to open port"); Some(x) }); - let mut port = port.expect("Failed to find valid serial port"); - let mut serial_buf: Vec = vec![0; 8]; - let mut plot_delta_time = 0.; - loop { - clear_background(BLACK); - match tail.axis { - Axis::One => { - draw_line(realtive_x_origin,realtive_y_origin,screen_width(),realtive_y_origin,1.,DARKGRAY); - }, - Axis::Two => { - // horizontal (x) - draw_line(0.,realtive_y_origin,screen_width(),realtive_y_origin,1.,DARKGRAY); - // vertical (y) - draw_line(realtive_x_origin,realtive_y_origin, realtive_x_origin,screen_height(),1.,DARKGRAY); - }, - } - let frame_delta_time = get_frame_time(); - let mut y_displacement = 0.; - let mut x_displacement = 0.; - // keep track of the delta time since last plot - plot_delta_time += frame_delta_time; - - if let Ok(x) = port.read(serial_buf.as_mut_slice()) { - let s: String = serial_buf.iter().take(x-1).map(|x| *x as char).collect(); - if let Ok(parse) = s.parse::() { - y_displacement = parse; - - // only draw when requied - tail.place_next(plot_delta_time, x_displacement, -y_displacement); - plot_delta_time = 0.; - } - } - - tail.draw(); - - // Info about the cursor locaiton - let (mouse_x, mouse_y) = mouse_position(); - - let size = measure_text(&format!("x{mouse_x}, y{}",-1.*(mouse_y-realtive_y_origin)), None, 30,1.); - draw_rectangle(mouse_x, mouse_y, size.width, -(size.height+5.), WHITE); - draw_text(&format!("x{}, y{}", mouse_x+realtive_x_origin,-1.*(mouse_y-realtive_y_origin)), mouse_x, mouse_y-5., 30., BLACK); - - - if is_key_down(KeyCode::S) { - if y_displacement < screen_height()/2. { - y_displacement += 10.; - } - } - if is_key_down(KeyCode::W) { - if y_displacement > -(screen_height()/2.) { - y_displacement -= 10.; - } - } - if is_key_down(KeyCode::D) { - if x_displacement < screen_width()/2. { - x_displacement += 10.; - } - } - if is_key_down(KeyCode::A) { - if x_displacement > -(screen_width()/2.) { - x_displacement -= 10.; - } - } - if is_key_pressed(KeyCode::F3) { - show_debug = !show_debug; - } - // selection box - if is_mouse_button_pressed(MouseButton::Left) { - (inital_x_pos, inital_y_pos) = mouse_position(); - } - if is_mouse_button_down(MouseButton::Left) { - - let (x, y) = mouse_position(); - let width = x - inital_x_pos; - let height = y - inital_y_pos; - - draw_rectangle(inital_x_pos, inital_y_pos, width, height, Color { r: 0.2, g: 0.1, b: 1., a: 0.3 }); - } - // toggle debug box - if show_debug { - let mut debug = DebugWindow::new(); - debug.add_line(format!("FPS {:03}, Latency {:.2}ms", get_fps(), frame_delta_time * 1000.0)); - debug.add_line(format!("Tail Length {TAIL_LEN}, px/s {:.2}", tail.px_per_s)); - debug.add_line(format!("Cursor Pos {:?}", mouse_position())); - debug.add_line(format!("Serial Port {}, Baud Rate {}", port.name().unwrap_or("Unknown".to_string()), port.baud_rate().unwrap_or(0))); - debug.draw(); - } - - next_frame().await; - } + let port = port.expect("Failed to find valid serial port"); + port } -- 2.49.1 From 7cee5eb4c58df347ca0aa1005135f430d20fb636 Mon Sep 17 00:00:00 2001 From: Rushmore75 Date: Thu, 29 Jan 2026 13:13:59 -0700 Subject: [PATCH 8/8] make fanicer --- Cargo.toml | 1 + rust-toolchain.toml | 2 + src/main.rs | 400 +++++++++++++++++++++++++++++++++----------- 3 files changed, 306 insertions(+), 97 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 0aec73c..93af9a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] macroquad = "0.4.13" serialport = "4.5.1" + diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/main.rs b/src/main.rs index 57bdd2c..306a1d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,43 +1,61 @@ +#![feature(random)] use core::f32; -use std::time::Duration; +use std::{ + fs::read_dir, + path::PathBuf, + sync::mpsc::{self, Receiver, Sender}, + thread::{self, JoinHandle}, + time::Duration, +}; use macroquad::prelude::*; -use serialport::SerialPort; const THICKNESS: f32 = 2.0; -const TAIL_LEN: usize = 500; +const TAIL_LEN: usize = 200; +const BAUD_RATE: u32 = 57600; +static mut PORT: Option = None; -#[derive(Clone,Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] struct Point { x: f32, y: f32, } impl Point { fn new(x: f32, y: f32) -> Self { - Self {x,y} + Self { x, y } } } -struct DebugWindow { +struct DebugWindow<'a> { text: Vec, text_margin: f32, + font: Option<&'a Font>, } -impl DebugWindow { - fn new() -> Self { +impl<'a> DebugWindow<'a> { + fn new(font: Option<&'a Font>) -> Self { Self { text: Vec::new(), text_margin: 20.0, + font, } } fn add_line(&mut self, line: String) { self.text.push(line); } + fn get_params(&self) -> TextParams { + let params = TextParams { + color: WHITE, + font: self.font, + ..Default::default() + }; + params + } fn draw(&self) { - let params = TextParams { color: WHITE, ..Default::default()} ; + let params = self.get_params(); self.text.iter().enumerate().for_each(|(index, text)| { let x = self.text_margin; - let y = self.text_margin+20.*(index as f32 + 1.); + let y = self.text_margin + 20. * (index as f32 + 1.); // let measurement = measure_text(text, None, params.font_size, params.font_scale); // draw_rectangle(x,y,measurement.width,-measurement.height, Color { r: 1., g: 1., b: 1., a: 0.25 }); @@ -48,12 +66,13 @@ impl DebugWindow { enum Axis { One, - Two + Two, } /// # Graph /// Holds 1 contiguous line. Can be graphed on 1 or 2 axis. -struct Graph { +struct Graph<'a> { + font: Option<&'a Font>, points: [Point; TAIL_LEN], head: usize, axises: Axis, @@ -66,35 +85,79 @@ struct Graph { /// How fast X scrolls when using a since axis tail px_per_s: f32, } -impl Graph { - fn new(axises: Axis) -> Self { +impl<'a> Graph<'a> { + fn recalculate_origin(&mut self) { + let (x_offset, y_offset) = match self.axises { + Axis::One => (0., screen_height() / 2.), + Axis::Two => (screen_width() / 2., screen_height() / 2.), + }; + self.x_origin = x_offset; + self.y_origin = y_offset; + } + fn new(axises: Axis, font: Option<&'a Font>) -> Self { let (x_offset, y_offset) = match axises { - Axis::One => { - (0., screen_height()*0.5) - }, - Axis::Two => { - (screen_width()/2.,screen_height()/2.) - }, + Axis::One => (0., screen_height() / 2.), + Axis::Two => (screen_width() / 2., screen_height() / 2.), }; Self { + font, // start all the dots off screen - points: [Point::new(-1.,-1.); TAIL_LEN], + points: [Point::new(-1., -1.); TAIL_LEN], head: 0, axises, x_origin: x_offset, y_origin: y_offset, - px_per_s: 750., + px_per_s: 800., head_tracker: 0., } } + fn draw_time(&self) { + let ms_per_px = self.px_per_s / 1000.; + // how many ms per segment + let segment_len_ms = 100.; + let px_offset = ms_per_px * segment_len_ms; + let segments = screen_width() / px_offset; + + let mut params = TextParams::default(); + params.font = self.font; + params.font_size = 20; + params.color = DARKGRAY; + + let y2 = screen_height(); + for i in 0..=segments as i32 { + let x = i as f32 * px_offset; + draw_line(x, 0., x, y2, 2., DARKGRAY); + + draw_text_ex( + &format!("{}ms", segment_len_ms * i as f32), + x, + y2 / 2., + params.clone(), + ); + } + } fn draw_axises(&self) { // Horzontal line // (0, y_origin) -> (max, y_origin) - draw_line(0.,self.y_origin,screen_width(),self.y_origin,1.,DARKGRAY); + draw_line( + 0., + self.y_origin, + screen_width(), + self.y_origin, + 1., + DARKGRAY, + ); // Vertical line // (x_origin, 0) -> (x_origin, max) - draw_line(self.x_origin,0.,self.x_origin,screen_height(),1.,DARKGRAY); + draw_line( + self.x_origin, + 0., + self.x_origin, + screen_height(), + 1., + DARKGRAY, + ); } fn place_next(&mut self, delta_time: f32, x_displacement: f32, y_displacement: f32) { match self.axises { @@ -105,16 +168,22 @@ impl Graph { } else { self.head_tracker = self.head_tracker + (delta_time * self.px_per_s) } - self.push(Point::new(self.head_tracker, self.y_origin-y_displacement)); - }, + self.push(Point::new( + self.head_tracker, + self.y_origin - y_displacement, + )); + } Axis::Two => { // both x and y will get displaced - self.push(Point::new(self.x_origin+x_displacement,self.y_origin-y_displacement)) - }, - } - } + self.push(Point::new( + self.x_origin + x_displacement, + self.y_origin - y_displacement, + )) + } + } + } fn push(&mut self, dot: Point) { - if self.head >= self.points.len() -1 { + if self.head >= self.points.len() - 1 { self.head = 0 } else { self.head += 1 @@ -123,72 +192,144 @@ impl Graph { self.points[self.head] = dot; } fn draw(&self) { - self.points.into_iter().enumerate().for_each(|(index, point) |{ - // dots fade - let virtual_index = if index > self.head { - index-self.head - } else { - index + (self.points.len() - self.head) - }; - let faded = virtual_index as f32 / self.points.len() as f32; - let faded_inverse = 1. - faded; - let color = Color { r: faded_inverse, g: faded, b: 0.2, a: faded+0.1}; + self.points + .into_iter() + .enumerate() + .for_each(|(index, point)| { + // dots fade + let virtual_index = if index > self.head { + index - self.head + } else { + index + (self.points.len() - self.head) + }; + let faded = virtual_index as f32 / self.points.len() as f32; + let faded_inverse = 1. - faded; + let color = Color { + r: faded_inverse, + g: faded, + b: 0.2, + a: faded + 0.1, + }; - let prev = self.previous(index); + let prev = self.previous(index); - if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { - // Don't have lines that cross the whole screen while using 1D graph - // draw_circle(point.x, point.y,THICKNESS, color); - } else if self.head == index { - // Draw the head differently - draw_circle(point.x, point.y,THICKNESS*2., color); - draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); - } else if prev == self.points[self.head] { - // Prevent the tail from connecting to the head - draw_circle(point.x, point.y,THICKNESS, color); - } else { - draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); - } - }); + if point.x == 0. || point.y == 0. || prev.x == -1. || prev.y == -1. { + // Don't have lines that cross the whole screen while using 1D graph + // draw_circle(point.x, point.y,THICKNESS, color); + } else if self.head == index { + // Draw the head differently + draw_circle(point.x, point.y, THICKNESS * 2., color); + draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); + } else if prev == self.points[self.head] { + // Prevent the tail from connecting to the head + draw_circle(point.x, point.y, THICKNESS, color); + } else { + draw_line(point.x, point.y, prev.x, prev.y, THICKNESS, color); + } + }); } fn previous(&self, index: usize) -> Point { let prev = if index > 0 { - index -1 + index - 1 } else { - self.points.len() -1 + self.points.len() - 1 }; self.points[prev] } } +async fn get_font() -> Option { + #[cfg(target_os = "windows")] + let fonts_path = "C:\\Windows\\Fonts"; + + #[cfg(target_os = "linux")] + let fonts_path = "/usr/share/fonts/"; + + if let Ok(dir) = read_dir(fonts_path) { + let selection = dir + .filter_map(|f| f.ok()) + .map(|f| (f.file_name(), f.path())) + .map(|(a, b)| (a.into_string(), b)) + .filter(|(a, _)| a.is_ok()) + .map(|(a, b)| (a.unwrap(), b)) + // Arial is a good windows default, Adwaita is a good Linux default + .filter(|(a, _)| a.contains("Arial") | a.contains("Adwaita")) + .map(|(_, b)| b) + .collect::>(); + if selection.len() > 0 { + if let Ok(dir) = read_dir(&selection[0]) { + let ttfs = dir + .filter_map(|f| f.ok()) + .map(|f| (f.file_name(), f.path())) + .map(|(a, b)| (a.into_string(), b)) + .filter(|(a, _)| a.is_ok()) + .map(|(a, b)| (a.unwrap(), b)) + .filter(|(a, _)| a.contains("Regular")) + .filter(|(a, _)| a.contains("Mono")) + .filter(|(a, _)| a.contains("ttf")) + .map(|(_, b)| b) + .collect::>(); + + if ttfs.len() > 0 { + let selection = ttfs[0].to_str().expect("Bad path"); + let ttf = load_ttf_font(selection) + .await + .expect("Cannot load ttf {selection}"); + println!("Loaded \"{:?}\"", ttfs[0]); + return Some(ttf); + } else { + eprintln!( + "So sutable fonts found in the sub dir \"{:?}\"", + selection[0] + ); + } + } + } else { + eprintln!("No fonts found"); + } + } + None +} + #[macroquad::main("Graph")] async fn main() { - // Dot params - let mut graph = Graph::new(Axis::One); + let font = get_font().await; + + // Dot params + let mut graph = Graph::new(Axis::One, font.as_ref()); // Tooling let mut show_debug = true; let mut plot_delta_time = 0.; + let mut pause = false; // Serial - let mut port = get_serial_port(); - let mut serial_buf: Vec = vec![0; 8]; + let (rx, _handle) = get_serial_port_or_demo(); loop { clear_background(BLACK); + graph.recalculate_origin(); graph.draw_axises(); + graph.draw_time(); let frame_delta_time = get_frame_time(); // keep track of the delta time since last plot plot_delta_time += frame_delta_time; - if let Ok(x) = port.read(serial_buf.as_mut_slice()) { - let s: String = serial_buf.iter().take(x-1).map(|x| *x as char).collect(); - if let Ok(parse) = s.parse::() { - // Only draw when requied - graph.place_next(plot_delta_time, 0., parse); - plot_delta_time = 0.; - } + if !pause { + match rx.try_recv() { + Ok(x) => { + graph.place_next(plot_delta_time, 0., x as f32); + plot_delta_time = 0.; + } + Err(x) => match x { + mpsc::TryRecvError::Empty => {} + mpsc::TryRecvError::Disconnected => { + eprintln!("Sender hung-up."); + return; + } + }, + } } graph.draw(); @@ -196,39 +337,104 @@ async fn main() { if is_key_pressed(KeyCode::F3) { show_debug = !show_debug; } - // toggle debug box - if show_debug { - // Debug cursor information - let (mouse_x, mouse_y) = mouse_position(); - let size = measure_text(&format!("x{mouse_x}, y{}",-(mouse_y-graph.y_origin)), None, 30,1.); - draw_rectangle(mouse_x, mouse_y+(size.offset_y/4.), size.width, -(size.height), WHITE); - draw_text(&format!("x{}, y{}", mouse_x+graph.x_origin,-(mouse_y-graph.y_origin)), mouse_x, mouse_y, 30., BLACK); - - // Debug textbox - let mut debug = DebugWindow::new(); - debug.add_line(format!("FPS {:04}, Frametime {:05.2}ms", get_fps(), frame_delta_time * 1000.0)); - debug.add_line(format!("Tail Length {TAIL_LEN}, px/s {:.2}", graph.px_per_s)); - debug.add_line(format!("Cursor Pos {:?}", mouse_position())); - debug.add_line(format!("Serial Port {}, Baud Rate {}", port.name().unwrap_or("Unknown".to_string()), port.baud_rate().map_or(String::from("N/A"), |f| f.to_string()))); - debug.draw(); + if is_key_pressed(KeyCode::F1) { + pause = !pause; } + let (_, my) = mouse_wheel(); + graph.px_per_s += 10. * my; + + // toggle debug box + if show_debug { + let mut debug = DebugWindow::new(font.as_ref()); + let params = debug.get_params(); + // Debug cursor information + let (mouse_x, mouse_y) = mouse_position(); + let text = &format!("x{mouse_x}, y{}", -(mouse_y - graph.y_origin)); + let size = measure_text(text, params.font, params.font_size, params.font_scale); + draw_rectangle( + mouse_x, + mouse_y + (size.offset_y / 4.), + size.width, + -(size.height), + BLUE, + ); + draw_text_ex(text, mouse_x, mouse_y, params); + + // Debug textbox + debug.add_line(format!( + "FPS {:04}, Frametime {:05.2}ms", + get_fps(), + frame_delta_time * 1000.0 + )); + debug.add_line(format!( + "Tail Length {TAIL_LEN}, px/s {:.2}", + graph.px_per_s + )); + debug.add_line(format!("Cursor Pos {:?}", mouse_position())); + + if let Some(port_name) = unsafe { PORT.clone() } { + debug.add_line(format!("Serial Port {port_name}, Baud Rate {BAUD_RATE}")); + } + + debug.draw(); + } next_frame().await; } } -fn get_serial_port() -> Box { - let port = serialport::available_ports() - .expect("No ports found!") - .iter() - .filter(|x| x.port_name.contains("ACM")) - .fold(None, |_, x| { - let x = serialport::new(x.port_name.clone(), 57600) - .timeout(Duration::from_millis(10)) - .open().expect("Failed to open port"); - Some(x) - }); - let port = port.expect("Failed to find valid serial port"); - port +type INT = i32; + +fn get_serial_port_or_demo() -> (Receiver, JoinHandle<()>) { + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + + let handle = match serialport::available_ports() { + Ok(ok) => { + let x = ok + .iter() + .filter(|x| x.port_name.contains("ACM")) + .take(1) + .collect::>(); + if x.len() > 0 { + let port = x[0]; + unsafe { + PORT = Some(port.port_name.clone()); + } + + let mut x = serialport::new(port.port_name.clone(), BAUD_RATE) + .timeout(Duration::from_millis(10)) + .open() + .expect("Failed to open port"); + + let handle = thread::spawn(move || loop { + let mut serial_buf: Vec = vec![0; 8]; + if let Ok(x) = x.read(serial_buf.as_mut_slice()) { + for i in &serial_buf[..x - 1] { + // this data may need to be cleansed + let _err = tx.send(i.clone() as INT); + } + } + }); + + handle + } else { + dummy_port(tx) + } + } + Err(_err) => dummy_port(tx), + }; + + (rx, handle) } +fn dummy_port(tx: Sender) -> JoinHandle<()> { + let handle = thread::spawn(move || loop { + let a: u8 = std::random::random(..); + // center around y origin + let a: INT = a as INT - ((u8::MAX / 2) as INT); + + let _err = tx.send(a); + thread::sleep(Duration::from_millis(10)) + }); + handle +} -- 2.49.1