graphs
1
Cargo.lock
generated
@ -229,6 +229,7 @@ dependencies = [
|
||||
"embedded-sprites",
|
||||
"heapless 0.9.2",
|
||||
"profont",
|
||||
"rand",
|
||||
"tinybmp",
|
||||
]
|
||||
|
||||
|
||||
@ -4,12 +4,11 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
#buoyant = "0.6.0-alpha.0"
|
||||
buoyant = "0.5.3"
|
||||
#buoyant = {path = "buoyant" }
|
||||
embedded-graphics = "0.8.1"
|
||||
embedded-graphics-simulator = "0.8.0"
|
||||
embedded-sprites = "0.2.0"
|
||||
heapless = "0.9.2"
|
||||
profont = "0.7.0"
|
||||
rand = "0.9.2"
|
||||
tinybmp = "0.6.0"
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@ -24,7 +24,7 @@
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="7.6922777"
|
||||
inkscape:cx="29.510115"
|
||||
inkscape:cx="29.575115"
|
||||
inkscape:cy="28.27511"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1032"
|
||||
@ -40,7 +40,7 @@
|
||||
id="layer1"
|
||||
transform="translate(0.08272917,0.15224179)">
|
||||
<rect
|
||||
style="fill:#242424;fill-opacity:1;stroke:none;stroke-width:1.6006;stroke-dasharray:6.40237, 3.20118, 1.6006, 3.20118;paint-order:markers stroke fill"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.6006;stroke-dasharray:6.40237, 3.20118, 1.6006, 3.20118;paint-order:markers stroke fill"
|
||||
id="rect1"
|
||||
width="11.90625"
|
||||
height="11.90625"
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@ -26,8 +26,8 @@
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="9.5273334"
|
||||
inkscape:cx="19.83766"
|
||||
inkscape:cy="22.724092"
|
||||
inkscape:cx="19.890141"
|
||||
inkscape:cy="22.829053"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1032"
|
||||
inkscape:window-x="0"
|
||||
@ -35,7 +35,7 @@
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<svg:rect
|
||||
style="fill:#242424;stroke-width:6.22986;stroke-dasharray:none;paint-order:markers stroke fill"
|
||||
style="fill:#000000;stroke-width:6.22986;stroke-dasharray:none;paint-order:markers stroke fill;fill-opacity:1"
|
||||
id="rect1"
|
||||
width="28.421053"
|
||||
height="28.421053"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
@ -44,7 +44,7 @@
|
||||
id="layer1"
|
||||
transform="matrix(0.10139507,0,0,0.10139507,4.4930246,0.14783758)">
|
||||
<rect
|
||||
style="fill:#080808;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect4"
|
||||
width="98.624123"
|
||||
height="197.24825"
|
||||
@ -85,7 +85,7 @@
|
||||
inkscape:transform-center-y="12.403106"
|
||||
transform="matrix(-0.82051985,0,0,-0.88816373,26.031162,164.76177)" />
|
||||
<circle
|
||||
style="fill:#1c1c1c;fill-opacity:1;stroke:#080808;stroke-width:5.96404155;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
style="fill:#1c1c1c;fill-opacity:1;stroke:#000000;stroke-width:5.96404155;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="path2"
|
||||
cx="5.0000005"
|
||||
cy="97.166092"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
@ -44,7 +44,7 @@
|
||||
id="layer1"
|
||||
transform="matrix(0.10139507,0,0,0.10139507,4.4930246,0.14783758)">
|
||||
<rect
|
||||
style="fill:#080808;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect4"
|
||||
width="98.624123"
|
||||
height="197.24825"
|
||||
@ -85,7 +85,7 @@
|
||||
inkscape:transform-center-y="12.403106"
|
||||
transform="matrix(-0.82051985,0,0,-0.88816373,26.031162,164.76177)" />
|
||||
<circle
|
||||
style="fill:#1c1c1c;fill-opacity:1;stroke:#080808;stroke-width:5.97976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
style="fill:#1c1c1c;fill-opacity:1;stroke:#000000;stroke-width:5.97976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="path2"
|
||||
cx="5.0000005"
|
||||
cy="97.166092"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
@ -44,7 +44,7 @@
|
||||
id="layer1"
|
||||
transform="matrix(0.10139507,0,0,0.10139507,4.4930246,0.14783758)">
|
||||
<rect
|
||||
style="fill:#080808;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:95.2216;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect4"
|
||||
width="98.624123"
|
||||
height="197.24825"
|
||||
@ -85,7 +85,7 @@
|
||||
inkscape:transform-center-y="12.403106"
|
||||
transform="matrix(-0.82051985,0,0,-0.88816373,26.031162,164.76177)" />
|
||||
<circle
|
||||
style="fill:#f6ab00;fill-opacity:1;stroke:#080808;stroke-width:5.97976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
style="fill:#f6ab00;fill-opacity:1;stroke:#000000;stroke-width:5.97976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="path2"
|
||||
cx="5.0000005"
|
||||
cy="97.166092"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@ -26,8 +26,8 @@
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="10.216728"
|
||||
inkscape:cx="24.861188"
|
||||
inkscape:cy="27.748609"
|
||||
inkscape:cx="24.910128"
|
||||
inkscape:cy="27.74861"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1032"
|
||||
inkscape:window-x="0"
|
||||
@ -49,7 +49,7 @@ div[style*="background-color: rgb(135, 135, 135)"] {
|
||||
inkscape:label="Layer 1"
|
||||
transform="matrix(2.0390253,0,0,2.0390253,-3.3768322,-2.3378069)">
|
||||
<svg:rect
|
||||
style="fill:#242424;fill-opacity:1;stroke-width:5.46432;paint-order:markers stroke fill"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:5.46432;paint-order:markers stroke fill"
|
||||
id="rect1"
|
||||
width="22.069368"
|
||||
height="22.069368"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@ -24,7 +24,7 @@
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="12.529602"
|
||||
inkscape:cx="23.624054"
|
||||
inkscape:cx="23.66396"
|
||||
inkscape:cy="30.687327"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1032"
|
||||
@ -33,7 +33,7 @@
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<rect
|
||||
style="fill:#242424;stroke-width:12.8688;stroke-dasharray:51.4753, 25.7377, 12.8688, 25.7377;paint-order:markers stroke fill;fill-opacity:1"
|
||||
style="fill:#000000;stroke-width:12.8688;stroke-dasharray:51.4753, 25.7377, 12.8688, 25.7377;paint-order:markers stroke fill;fill-opacity:1"
|
||||
id="rect1"
|
||||
width="45"
|
||||
height="45"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@ -3,10 +3,11 @@ use embedded_graphics::prelude::RgbColor;
|
||||
use embedded_graphics::prelude::WebColors;
|
||||
|
||||
pub const BACKGROUND_COLOR: Rgb565 = Rgb565::BLACK;
|
||||
pub const FRAME_BACKGROUD_COLOR: Rgb565 = Rgb565::new(1, 2, 1);
|
||||
//pub const FRAME_BACKGROUD_COLOR: Rgb565 = Rgb565::new(1, 2, 1);
|
||||
pub const FRAME_BACKGROUD_COLOR: Rgb565 = Rgb565::new(0, 0, 0);
|
||||
pub const FRAME_STROKE_COLOR: Rgb565 = Rgb565::new(4, 9, 4);
|
||||
|
||||
pub const MAIN_TEXT_COLOR: Rgb565 = Rgb565::WHITE;
|
||||
pub const SUB_TEXT_COLOR: Rgb565 = Rgb565::CSS_DARK_GRAY;
|
||||
|
||||
pub const FRAME_STROKE: u32 = 3;
|
||||
pub const FRAME_STROKE: u32 = 1;
|
||||
|
||||
255
src/graph.rs
Normal file
@ -0,0 +1,255 @@
|
||||
use buoyant::primitives::Interpolate;
|
||||
use embedded_graphics::Pixel;
|
||||
use embedded_graphics::image::GetPixel;
|
||||
use embedded_graphics::mono_font::MonoTextStyle;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::Dimensions;
|
||||
use embedded_graphics::prelude::DrawTarget;
|
||||
use embedded_graphics::prelude::Drawable;
|
||||
use embedded_graphics::prelude::Point;
|
||||
use embedded_graphics::prelude::PointsIter;
|
||||
use embedded_graphics::prelude::Primitive;
|
||||
use embedded_graphics::prelude::RgbColor;
|
||||
use embedded_graphics::prelude::Size;
|
||||
use embedded_graphics::prelude::WebColors;
|
||||
use embedded_graphics::primitives::Line;
|
||||
use embedded_graphics::primitives::PrimitiveStyle;
|
||||
use embedded_graphics::primitives::PrimitiveStyleBuilder;
|
||||
use embedded_graphics::primitives::Rectangle;
|
||||
use embedded_graphics::text::Text;
|
||||
use heapless::format;
|
||||
use profont::PROFONT_10_POINT;
|
||||
|
||||
fn map_float(x: f32, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> f32 {
|
||||
((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min
|
||||
}
|
||||
|
||||
const DEFAULT_LUT: [Rgb565; 5] = [
|
||||
Rgb565::new(0, 16, 11),
|
||||
Rgb565::new(11, 20, 17),
|
||||
Rgb565::new(23, 20, 18),
|
||||
Rgb565::new(31, 24, 12),
|
||||
Rgb565::new(31, 41, 0),
|
||||
];
|
||||
|
||||
fn rgb565_interpolate(a: Rgb565, b: Rgb565, x: f32) -> Rgb565 {
|
||||
Rgb565::new(
|
||||
(a.r() as f32 * (1. - x)) as u8 + (b.r() as f32 * x) as u8,
|
||||
(a.g() as f32 * (1. - x)) as u8 + (b.g() as f32 * x) as u8,
|
||||
(a.b() as f32 * (1. - x)) as u8 + (b.b() as f32 * x) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
fn color_lut(mut x: f32, colors: &[Rgb565]) -> Rgb565 {
|
||||
if x == 1. {
|
||||
return colors[colors.len() - 1];
|
||||
}
|
||||
|
||||
if x == 0. {
|
||||
return colors[0];
|
||||
}
|
||||
|
||||
x *= (colors.len() - 1) as f32;
|
||||
|
||||
let index = x.floor();
|
||||
let interp = x - index;
|
||||
|
||||
rgb565_interpolate(colors[index as usize], colors[index as usize + 1], interp)
|
||||
}
|
||||
|
||||
pub fn graph_data<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
data: &[f32],
|
||||
target: &mut T,
|
||||
) {
|
||||
let min = data.iter().copied().reduce(f32::min).unwrap_or(0.);
|
||||
let max = data.iter().copied().reduce(f32::max).unwrap_or(0.);
|
||||
let size = Size::new(
|
||||
target.bounding_box().size.width,
|
||||
target.bounding_box().size.height,
|
||||
);
|
||||
|
||||
// Draw data as WHITE line
|
||||
let mut start = Point::new(
|
||||
0,
|
||||
map_float(data[0], min, max, size.height as f32, 0.) as i32,
|
||||
);
|
||||
for (i, x) in data.iter().skip(1).enumerate() {
|
||||
let point = Point::new(
|
||||
map_float(i as f32, 0., data.len() as f32 - 1., 0., size.width as f32) as i32,
|
||||
map_float(*x, min, max, size.height as f32, 0.) as i32,
|
||||
);
|
||||
let _ = Line::new(start, point)
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 2))
|
||||
.draw(target);
|
||||
start = point;
|
||||
}
|
||||
|
||||
for x in 0..size.width {
|
||||
// Start coloring from up to bottom
|
||||
let mut met_curve = false;
|
||||
|
||||
for y in 0..size.height {
|
||||
let position = Point::new(x as i32, y as i32);
|
||||
let pixel = target.pixel(position).unwrap();
|
||||
let height_factor = map_float(y as f32, 0., size.height as f32, 1., 0.);
|
||||
let height_color = color_lut(height_factor, &DEFAULT_LUT);
|
||||
|
||||
if pixel == Rgb565::WHITE {
|
||||
let _ = Pixel(position, height_color).draw(target);
|
||||
met_curve = true;
|
||||
} else if met_curve && (x as i32 - y as i32) % 7 == 0 {
|
||||
let _ = Pixel(
|
||||
position,
|
||||
rgb565_interpolate(height_color, Rgb565::BLACK, 1. - height_factor),
|
||||
)
|
||||
.draw(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_indicator<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
data: &[f32],
|
||||
target: &mut T,
|
||||
) {
|
||||
let size = target.bounding_box().size;
|
||||
let (min_index, min) = data
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.reduce(|a, b| if a.1 < b.1 { a } else { b })
|
||||
.unwrap_or((0, 0.));
|
||||
|
||||
let min_x = map_float(
|
||||
min_index as f32,
|
||||
0.,
|
||||
data.len() as f32,
|
||||
0.,
|
||||
size.width as f32,
|
||||
) as i32;
|
||||
|
||||
// let _ = Line::new(Point::new(min_x, 0), Point::new(min_x, size.height as i32))
|
||||
// .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1))
|
||||
// .draw(target);
|
||||
for y in 0..size.height {
|
||||
if (y / 2) % 2 == 0 {
|
||||
let position = Point::new(min_x, y as i32);
|
||||
let _ = Pixel(
|
||||
position,
|
||||
rgb565_interpolate(Rgb565::RED, Rgb565::BLACK, 0.6),
|
||||
)
|
||||
.draw(target);
|
||||
|
||||
// let position = Point::new(min_x + 1, y as i32);
|
||||
// let _ = Pixel(
|
||||
// position,
|
||||
// rgb565_interpolate(Rgb565::RED, Rgb565::BLACK, 0.6),
|
||||
// )
|
||||
// .draw(target);
|
||||
}
|
||||
}
|
||||
|
||||
let minimum_text = "Minimum";
|
||||
let font = &PROFONT_10_POINT;
|
||||
|
||||
let text_start = if min_x < (size.width / 2) as i32 {
|
||||
5
|
||||
} else {
|
||||
-((minimum_text.len() + 1) as i32 * font.character_size.width as i32 + 3)
|
||||
};
|
||||
|
||||
let style = MonoTextStyle::new(
|
||||
&PROFONT_10_POINT,
|
||||
rgb565_interpolate(Rgb565::WHITE, Rgb565::BLACK, 0.8),
|
||||
);
|
||||
let _ = Text::new(
|
||||
minimum_text,
|
||||
Point::new(min_x + text_start as i32, 10),
|
||||
style,
|
||||
)
|
||||
.draw(target);
|
||||
|
||||
let value = format!(16; "{:.1}", min).unwrap();
|
||||
let _ = Text::new(
|
||||
value.as_str(),
|
||||
Point::new(
|
||||
min_x + text_start as i32,
|
||||
10 + font.character_size.height as i32,
|
||||
),
|
||||
style,
|
||||
)
|
||||
.draw(target);
|
||||
}
|
||||
|
||||
pub fn max_indicator<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
data: &[f32],
|
||||
target: &mut T,
|
||||
) {
|
||||
let size = target.bounding_box().size;
|
||||
let (max_index, max) = data
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.reduce(|a, b| if a.1 > b.1 { a } else { b })
|
||||
.unwrap_or((0, 0.));
|
||||
|
||||
let max_x = map_float(
|
||||
max_index as f32,
|
||||
0.,
|
||||
data.len() as f32,
|
||||
0.,
|
||||
size.width as f32,
|
||||
) as i32;
|
||||
|
||||
for y in 0..size.height {
|
||||
if (y / 2) % 2 == 0 {
|
||||
let position = Point::new(max_x, y as i32);
|
||||
let _ = Pixel(
|
||||
position,
|
||||
rgb565_interpolate(Rgb565::GREEN, Rgb565::BLACK, 0.6),
|
||||
)
|
||||
.draw(target);
|
||||
|
||||
// let position = Point::new(max_x + 1, y as i32);
|
||||
// let _ = Pixel(
|
||||
// position,
|
||||
// rgb565_interpolate(Rgb565::GREEN, Rgb565::BLACK, 0.6),
|
||||
// )
|
||||
// .draw(target);
|
||||
}
|
||||
}
|
||||
|
||||
let maximum_text = "Maximum";
|
||||
let font = &PROFONT_10_POINT;
|
||||
|
||||
let text_start = if max_x < (size.width / 2) as i32 {
|
||||
5
|
||||
} else {
|
||||
-((maximum_text.len() + 1) as i32 * font.character_size.width as i32 + 3)
|
||||
};
|
||||
|
||||
let style = MonoTextStyle::new(
|
||||
&PROFONT_10_POINT,
|
||||
rgb565_interpolate(Rgb565::WHITE, Rgb565::BLACK, 0.8),
|
||||
);
|
||||
let _ = Text::new(
|
||||
maximum_text,
|
||||
Point::new(
|
||||
max_x + text_start as i32,
|
||||
size.height as i32 - font.character_size.height as i32 * 2,
|
||||
),
|
||||
style,
|
||||
)
|
||||
.draw(target);
|
||||
|
||||
let value = format!(16; "{:.1}", max).unwrap();
|
||||
let _ = Text::new(
|
||||
value.as_str(),
|
||||
Point::new(
|
||||
max_x + text_start as i32,
|
||||
size.height as i32 - font.character_size.height as i32,
|
||||
),
|
||||
style,
|
||||
)
|
||||
.draw(target);
|
||||
}
|
||||
52
src/main.rs
@ -1,6 +1,7 @@
|
||||
//#![feature(unsafe_cell_access)]
|
||||
|
||||
mod colors;
|
||||
mod graph;
|
||||
mod images;
|
||||
mod views;
|
||||
|
||||
@ -8,7 +9,11 @@ use std::cell::UnsafeCell;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use buoyant::view::prelude::*;
|
||||
use embedded_graphics::framebuffer::Framebuffer;
|
||||
use embedded_graphics::framebuffer::buffer_size;
|
||||
use embedded_graphics::image::ImageRaw;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::pixelcolor::raw::LittleEndian;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::OutputSettings;
|
||||
use embedded_graphics_simulator::SimulatorDisplay;
|
||||
@ -16,6 +21,10 @@ use embedded_graphics_simulator::Window;
|
||||
use heapless::format;
|
||||
use tinybmp::Bmp;
|
||||
|
||||
use crate::graph::graph_data;
|
||||
use crate::graph::max_indicator;
|
||||
use crate::graph::min_indicator;
|
||||
|
||||
const BACKGROUND_COLOR: Rgb565 = Rgb565::BLACK;
|
||||
const DEFAULT_COLOR: Rgb565 = Rgb565::WHITE;
|
||||
|
||||
@ -23,22 +32,50 @@ fn main() {
|
||||
images::prepare_images();
|
||||
|
||||
let mut window = Window::new("Hello World", &OutputSettings::default());
|
||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(320, 240));
|
||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(240, 240));
|
||||
display.clear(BACKGROUND_COLOR);
|
||||
|
||||
// views::menu::menu_view()
|
||||
// .as_drawable(display.size(), DEFAULT_COLOR)
|
||||
// .draw(&mut display)
|
||||
// .unwrap();
|
||||
|
||||
views::detail::detailed_view(MenuIndicatorType::Temperature(38.3), Tendency::Steady)
|
||||
views::menu::menu_view()
|
||||
.as_drawable(display.size(), DEFAULT_COLOR)
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
|
||||
// views::detail::detailed_view(MenuIndicatorType::Temperature(38.3), Tendency::Steady)
|
||||
// .as_drawable(display.size(), DEFAULT_COLOR)
|
||||
// .draw(&mut display)
|
||||
// .unwrap();
|
||||
|
||||
let mut dummy_data = [0.; 100];
|
||||
for i in 0..dummy_data.len() {
|
||||
dummy_data[i] = rand::random::<f32>();
|
||||
}
|
||||
|
||||
let smoothed = dummy_data
|
||||
.windows(30)
|
||||
.map(|x| x.iter().sum::<f32>() / 10.)
|
||||
.collect::<Vec<f32>>();
|
||||
|
||||
let mut fb = Framebuffer::<
|
||||
Rgb565,
|
||||
_,
|
||||
LittleEndian,
|
||||
240,
|
||||
{ 240 - 80 },
|
||||
{ buffer_size::<Rgb565>(240, 240) },
|
||||
>::new();
|
||||
|
||||
// graph_data(&smoothed, &mut fb);
|
||||
// min_indicator(&smoothed, &mut fb);
|
||||
//max_indicator(&smoothed, &mut fb);
|
||||
|
||||
let img_raw = ImageRaw::<Rgb565, LittleEndian>::new(fb.data(), 240);
|
||||
let image = embedded_graphics::image::Image::new(&img_raw, Point::new(0, 60));
|
||||
//image.draw(&mut display).unwrap();
|
||||
|
||||
window.show_static(&display);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MenuIndicatorType {
|
||||
Temperature(f32),
|
||||
Humidity(f32),
|
||||
@ -77,6 +114,7 @@ impl MenuIndicatorType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Tendency {
|
||||
Rising,
|
||||
Steady,
|
||||
|
||||
@ -6,12 +6,18 @@ use buoyant::view::Text;
|
||||
use buoyant::view::VStack;
|
||||
use buoyant::view::View;
|
||||
use buoyant::view::ViewExt;
|
||||
use buoyant::view::ZStack;
|
||||
use buoyant::view::shape::Rectangle;
|
||||
use buoyant::view::shape::ShapeExt;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use profont::PROFONT_18_POINT;
|
||||
use profont::PROFONT_24_POINT;
|
||||
|
||||
use crate::MenuIndicatorType;
|
||||
use crate::Tendency;
|
||||
use crate::colors::BACKGROUND_COLOR;
|
||||
use crate::colors::FRAME_BACKGROUD_COLOR;
|
||||
use crate::colors::FRAME_STROKE;
|
||||
use crate::colors::FRAME_STROKE_COLOR;
|
||||
use crate::colors::MAIN_TEXT_COLOR;
|
||||
use crate::colors::SUB_TEXT_COLOR;
|
||||
@ -20,33 +26,36 @@ use crate::views::icon::icon_box_view;
|
||||
|
||||
pub fn detailed_view(indicator: MenuIndicatorType, tendency: Tendency) -> impl View<Rgb565> {
|
||||
VStack::new((
|
||||
// Header
|
||||
HStack::new((
|
||||
icon_box_view(FRAME_STROKE_COLOR, indicator.get_corresponding_icon()),
|
||||
Spacer::default().flex_frame().with_max_width(10),
|
||||
tendency_indicator(tendency),
|
||||
Text::new(indicator.get_value_str(), &PROFONT_24_POINT)
|
||||
.foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(indicator.get_corresponding_unit_string(), &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
ZStack::new((
|
||||
header(indicator, tendency),
|
||||
Rectangle
|
||||
.corner_radius(10)
|
||||
.stroked(FRAME_STROKE)
|
||||
.foreground_color(BACKGROUND_COLOR)
|
||||
.flex_frame()
|
||||
.with_infinite_max_height()
|
||||
.with_vertical_alignment(VerticalAlignment::Bottom)
|
||||
.with_max_height(25),
|
||||
Spacer::default(),
|
||||
Text::new("Temperature", &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
.flex_frame()
|
||||
.with_infinite_max_height()
|
||||
.with_vertical_alignment(VerticalAlignment::Bottom)
|
||||
.with_max_height(25),
|
||||
Spacer::default().flex_frame().with_max_width(10),
|
||||
.with_max_height(53),
|
||||
)),
|
||||
// Window
|
||||
Spacer::default()
|
||||
.flex_frame()
|
||||
.with_infinite_max_width()
|
||||
.with_infinite_max_height(),
|
||||
Spacer::default(),
|
||||
))
|
||||
.with_spacing(2)
|
||||
.with_alignment(HorizontalAlignment::Leading)
|
||||
}
|
||||
|
||||
pub fn header(indicator: MenuIndicatorType, tendency: Tendency) -> impl View<Rgb565> {
|
||||
// Header
|
||||
HStack::new((
|
||||
icon_box_view(FRAME_STROKE_COLOR, indicator.get_corresponding_icon()),
|
||||
Spacer::default().flex_frame().with_max_width(10),
|
||||
Spacer::default(),
|
||||
tendency_indicator(tendency),
|
||||
Text::new(indicator.get_value_str(), &PROFONT_24_POINT).foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(indicator.get_corresponding_unit_string(), &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
.flex_frame()
|
||||
.with_infinite_max_height()
|
||||
.with_vertical_alignment(VerticalAlignment::Bottom)
|
||||
.with_max_height(25),
|
||||
Spacer::default(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -4,12 +4,15 @@ use buoyant::view::ZStack;
|
||||
use buoyant::view::shape::Rectangle;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
|
||||
use crate::BACKGROUND_COLOR;
|
||||
use crate::get_image;
|
||||
use crate::images::StaticImage;
|
||||
|
||||
pub fn icon_box_view(box_color: Rgb565, icon: &'static StaticImage) -> impl View<Rgb565> {
|
||||
ZStack::new((
|
||||
Rectangle.corner_radius(10).foreground_color(box_color),
|
||||
Rectangle
|
||||
.corner_radius(10)
|
||||
.foreground_color(BACKGROUND_COLOR),
|
||||
buoyant::view::Image::new(get_image!(icon)),
|
||||
))
|
||||
.flex_frame()
|
||||
|
||||
@ -23,19 +23,19 @@ pub fn menu_view() -> impl View<Rgb565> {
|
||||
main_menu_indicator(MenuIndicatorType::Temperature(31.5), Tendency::Falling),
|
||||
main_menu_indicator(MenuIndicatorType::Humidity(36.2), Tendency::Steady),
|
||||
))
|
||||
.with_spacing(5),
|
||||
.with_spacing(2),
|
||||
HStack::new((
|
||||
main_menu_indicator(MenuIndicatorType::Co2(1329), Tendency::Rising),
|
||||
main_menu_indicator(MenuIndicatorType::Voc(29), Tendency::Falling),
|
||||
))
|
||||
.with_spacing(5),
|
||||
.with_spacing(2),
|
||||
))
|
||||
.with_spacing(5)
|
||||
.with_spacing(2)
|
||||
}
|
||||
|
||||
fn main_menu_indicator(indicator_type: MenuIndicatorType, tendency: Tendency) -> impl View<Rgb565> {
|
||||
Rectangle
|
||||
.corner_radius(10)
|
||||
.corner_radius(5)
|
||||
.stroked(FRAME_STROKE)
|
||||
.foreground_color(FRAME_STROKE_COLOR)
|
||||
.background(Alignment::Center, || {
|
||||
|
||||