diff --git a/Cargo.lock b/Cargo.lock index d1792a3..5972d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,6 +512,12 @@ dependencies = [ "syn 3.0.3", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "byteorder-lite" version = "0.1.0" @@ -772,6 +778,18 @@ dependencies = [ "libc", ] +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + [[package]] name = "core_maths" version = "0.1.1" @@ -884,6 +902,7 @@ dependencies = [ "dotenvy", "hass-rs", "paste", + "plotters", "serde", "serde_json", "slint", @@ -957,6 +976,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1060,6 +1100,18 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" +[[package]] +name = "dwrote" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "either" version = "1.17.0" @@ -1267,12 +1319,43 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "foldhash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "font-kit" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" +dependencies = [ + "bitflags 2.13.1", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + [[package]] name = "font-types" version = "0.11.3" @@ -1300,7 +1383,7 @@ dependencies = [ "log", "slotmap", "tinyvec", - "ttf-parser", + "ttf-parser 0.25.1", ] [[package]] @@ -1361,6 +1444,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "funty" version = "2.0.0" @@ -1531,6 +1625,16 @@ dependencies = [ "r-efi 6.0.0", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gif" version = "0.14.2" @@ -1873,7 +1977,7 @@ dependencies = [ "derive_more", "i-slint-common", "icu_normalizer", - "image", + "image 0.25.10", "itertools 0.14.0", "linked_hash_set", "lyon_extra", @@ -1911,7 +2015,7 @@ dependencies = [ "i-slint-common", "i-slint-core-macros", "icu_normalizer", - "image", + "image 0.25.10", "lyon_algorithms", "lyon_extra", "lyon_geom", @@ -2227,6 +2331,20 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-traits", + "png 0.17.16", +] + [[package]] name = "image" version = "0.25.10" @@ -2237,11 +2355,11 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.2", "image-webp", "moxcms", "num-traits", - "png", + "png 0.18.1", "qoi", "ravif", "rayon", @@ -2425,6 +2543,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + [[package]] name = "js-sys" version = "0.3.103" @@ -2732,7 +2856,7 @@ dependencies = [ "objc2-core-foundation", "objc2-foundation 0.3.2", "once_cell", - "png", + "png 0.18.1", "thiserror 2.0.20", "windows-sys 0.61.2", ] @@ -3311,6 +3435,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.55" @@ -3405,6 +3535,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +dependencies = [ + "rustc_version", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3478,6 +3627,65 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "chrono", + "font-kit", + "image 0.24.9", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser 0.20.0", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-bitmap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" +dependencies = [ + "gif 0.12.0", + "image 0.24.9", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "png" version = "0.18.1" @@ -3867,6 +4075,17 @@ dependencies = [ "bitflags 2.13.1", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.20", +] + [[package]] name = "regex" version = "1.13.1" @@ -3902,7 +4121,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9be183ad6a216aa96f33e4c8033b0988b8b3ea6fd2359d19af5bac4643fd8e81" dependencies = [ - "gif", + "gif 0.14.2", "image-webp", "log", "pico-args", @@ -4069,7 +4288,7 @@ dependencies = [ "core_maths", "log", "smallvec", - "ttf-parser", + "ttf-parser 0.25.1", "unicode-bidi-mirroring", "unicode-ccc", "unicode-properties", @@ -4698,7 +4917,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", + "png 0.18.1", "tiny-skia-path", ] @@ -4885,6 +5104,12 @@ dependencies = [ "slint", ] +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + [[package]] name = "ttf-parser" version = "0.25.1" @@ -5097,7 +5322,7 @@ dependencies = [ "strict-num", "svgtypes", "tiny-skia-path", - "ttf-parser", + "ttf-parser 0.25.1", "unicode-bidi", "unicode-script", "unicode-vo", @@ -5298,6 +5523,22 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -5307,6 +5548,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.62.2" @@ -5627,6 +5874,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wit-bindgen" version = "0.57.1" diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index 7326ae7..6fe27c7 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -16,6 +16,7 @@ thiserror = "2.0.20" paste = "1.0.15" ureq = { version = "3.4.0", default-features = false, features = ["json", "rustls"] } serde = { version = "1.0.229", features = ["derive"] } +plotters = "0.3.7" [features] dev = ["slint/backend-winit-x11"] diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 566c879..ccd5a64 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -6,7 +6,9 @@ mod ui; use tokio::sync::mpsc::unbounded_channel; +use plotters::prelude::*; use slint::ComponentHandle; +use slint::SharedPixelBuffer; use crate::{ error::AppError, @@ -22,6 +24,8 @@ async fn main() -> Result<(), AppError> { let app = ui::AppWindow::new()?; + app.on_render_plot(|iteration| render_plot(iteration).unwrap()); + let (c2s_tx, c2s_rx) = unbounded_channel::(); let (s2c_tx, s2c_rx) = unbounded_channel::(); @@ -35,3 +39,63 @@ async fn main() -> Result<(), AppError> { app.run().map_err(Into::into) } + +fn render_plot(i: i32) -> Result> { + let mut pixel_buffer = SharedPixelBuffer::new(640, 480); + let size = (pixel_buffer.width(), pixel_buffer.height()); + + let backend = BitMapBackend::with_buffer(pixel_buffer.make_mut_bytes(), size); + let root = backend.into_drawing_area(); + + root.fill(&WHITE)?; + + let mut chart = ChartBuilder::on(&root) + .caption( + format!("Koch's Snowflake (n_iter = {})", i), + ("sans-serif", 50), + ) + .build_cartesian_2d(-2.0..2.0, -1.5..1.5)?; + + let mut snowflake_vertices = { + let mut current: Vec<(f64, f64)> = vec![ + (0.0, 1.0), + ((3.0f64).sqrt() / 2.0, -0.5), + (-(3.0f64).sqrt() / 2.0, -0.5), + ]; + for _ in 0..i { + current = snowflake_iter(¤t[..]); + } + current + }; + + chart.draw_series(std::iter::once(Polygon::new( + snowflake_vertices.clone(), + RED.mix(0.2), + )))?; + + snowflake_vertices.push(snowflake_vertices[0]); + chart.draw_series(std::iter::once(PathElement::new(snowflake_vertices, RED)))?; + + root.present()?; + drop(chart); + drop(root); + + Ok(slint::Image::from_rgb8(pixel_buffer)) +} + +fn snowflake_iter(points: &[(f64, f64)]) -> Vec<(f64, f64)> { + let mut ret = vec![]; + for i in 0..points.len() { + let (start, end) = (points[i], points[(i + 1) % points.len()]); + let t = ((end.0 - start.0) / 3.0, (end.1 - start.1) / 3.0); + let s = ( + t.0 * 0.5 - t.1 * (0.75f64).sqrt(), + t.1 * 0.5 + (0.75f64).sqrt() * t.0, + ); + ret.push(start); + ret.push((start.0 + t.0, start.1 + t.1)); + ret.push((start.0 + t.0 + s.0, start.1 + t.1 + s.1)); + ret.push((start.0 + t.0 * 2.0, start.1 + t.1 * 2.0)); + } + ret +} diff --git a/dashboard/ui/energy.slint b/dashboard/ui/energy.slint index 6c5a6ae..2acbb26 100644 --- a/dashboard/ui/energy.slint +++ b/dashboard/ui/energy.slint @@ -1,3 +1,23 @@ + +import { Horizontal, Slider, Vertical } from "material/material.slint"; export component Energy inherits Rectangle { - // .. + + in-out property pitch: 0.15; + in-out property yaw: 0.5; + + pure callback render_plot(int) -> image; + + Vertical { + Image { + source: root.render_plot(amplitude-slider.value); + } + + Horizontal { + amplitude-slider := Slider { + minimum: 0; + maximum: 10; + value: 0; + } + } + } } diff --git a/dashboard/ui/main.slint b/dashboard/ui/main.slint index 7f5c38e..57eea9e 100644 --- a/dashboard/ui/main.slint +++ b/dashboard/ui/main.slint @@ -125,6 +125,8 @@ export component AppWindow inherits Window { height: 600px; default-font-size: 16px; + pure callback render_plot(int) -> image; + in-out property color-scheme <=> Palette.color-scheme; in property date-time; @@ -195,7 +197,9 @@ export component AppWindow inherits Window { calendar-events: calendar-events; } - if tab-bar.current-index == 2: Energy { } + if tab-bar.current-index == 2: Energy { + render-plot(iteration) => render-plot(iteration); + } Rectangle { tab-bar := TabBar {