Skip to content

Commit cae5cc2

Browse files
committed
implement working color picker for all curves
1 parent 25e5563 commit cae5cc2

File tree

8 files changed

+394
-150
lines changed

8 files changed

+394
-150
lines changed

Cargo.lock

Lines changed: 83 additions & 103 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ preferences = { git = "https://github.com/andybarron/preferences-rs" }
2323
rand = "0.8.5"
2424
realfft = "3.3.0"
2525
regex = "1"
26-
rfd = "0.14"
26+
rfd = "0.15.0"
2727
safe-transmute = "0.11"
2828
serde = { version = "1.0", features = ["derive"] }
2929
serialport = { git = "https://github.com/serialport/serialport-rs", features = ["serde"] }

src/color_picker.rs

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
use eframe::egui::{
2+
self, lerp, pos2, remap_clamp, vec2, Align2, Color32, Mesh, Pos2, Response, Sense, Shape,
3+
Stroke, Ui, Vec2,
4+
};
5+
6+
// Ten colors that are distinguishable and suitable for colorblind people
7+
pub const COLORS: [Color32; 10] = [
8+
Color32::WHITE, // White
9+
Color32::from_rgb(230, 159, 0), // Orange
10+
Color32::from_rgb(86, 180, 233), // Sky Blue
11+
Color32::from_rgb(0, 158, 115), // Bluish Green
12+
Color32::from_rgb(240, 228, 66), // Yellow
13+
Color32::from_rgb(0, 114, 178), // Blue
14+
Color32::from_rgb(213, 94, 0), // Vermilion (Red-Orange)
15+
Color32::from_rgb(204, 121, 167), // Reddish Purple
16+
Color32::from_rgb(121, 94, 56), // Brown
17+
Color32::from_rgb(0, 204, 204), // Cyan
18+
];
19+
20+
fn contrast_color(color: Color32) -> Color32 {
21+
let intensity = (color.r() as f32 + color.g() as f32 + color.b() as f32) / 3.0 / 255.0;
22+
if intensity < 0.5 {
23+
Color32::WHITE
24+
} else {
25+
Color32::BLACK
26+
}
27+
}
28+
29+
pub fn color_picker_widget(
30+
ui: &mut Ui,
31+
label: &str,
32+
color: &mut [Color32],
33+
index: usize,
34+
) -> Response {
35+
// Draw the square
36+
ui.horizontal(|ui| {
37+
// Define the desired square size (same as checkbox size)
38+
let square_size = ui.spacing().interact_size.y * 0.8;
39+
40+
// Allocate a square of the same size as the checkbox
41+
let (rect, response) =
42+
ui.allocate_exact_size(egui::vec2(square_size, square_size), Sense::click());
43+
44+
// Highlight stroke when hovered
45+
let stroke = if response.hovered() {
46+
Stroke::new(2.0, Color32::WHITE) // White stroke when hovered
47+
} else {
48+
Stroke::NONE // No stroke otherwise
49+
};
50+
51+
// Draw the color square with possible hover outline
52+
ui.painter().rect(rect, 2.0, color[index], stroke);
53+
ui.label(label);
54+
response
55+
})
56+
.inner
57+
}
58+
pub fn color_picker_window(ctx: &egui::Context, color: &mut Color32, value: &mut f32) -> bool {
59+
let mut save_button = false;
60+
61+
let _window_response = egui::Window::new("Color Menu")
62+
// .fixed_pos(Pos2 { x: 800.0, y: 450.0 })
63+
.fixed_size(Vec2 { x: 100.0, y: 100.0 })
64+
.anchor(Align2::CENTER_CENTER, Vec2 { x: 0.0, y: 0.0 })
65+
.collapsible(false)
66+
.show(ctx, |ui| {
67+
// We will create two horizontal rows with five squares each
68+
let square_size = ui.spacing().interact_size.y * 0.8;
69+
70+
ui.vertical(|ui| {
71+
// First row (5 squares)
72+
ui.horizontal(|ui| {
73+
for color_option in &COLORS[0..5] {
74+
let (rect, response) = ui.allocate_exact_size(
75+
egui::vec2(square_size, square_size),
76+
Sense::click(),
77+
);
78+
79+
// Handle click to set selected color
80+
if response.clicked() {
81+
*color = *color_option;
82+
}
83+
84+
// Stroke highlighting for hover
85+
let stroke = if response.hovered() {
86+
Stroke::new(2.0, Color32::WHITE)
87+
} else {
88+
Stroke::NONE
89+
};
90+
91+
// Draw the color square
92+
ui.painter().rect(rect, 2.0, *color_option, stroke);
93+
}
94+
});
95+
96+
// Second row (5 squares)
97+
ui.horizontal(|ui| {
98+
for color_option in &COLORS[5..10] {
99+
let (rect, response) = ui.allocate_exact_size(
100+
egui::vec2(square_size, square_size),
101+
Sense::click(),
102+
);
103+
104+
// Handle click to set selected color
105+
if response.clicked() {
106+
*color = *color_option;
107+
}
108+
109+
// Stroke highlighting for hover
110+
let stroke = if response.hovered() {
111+
Stroke::new(2.0, Color32::WHITE)
112+
} else {
113+
Stroke::NONE
114+
};
115+
116+
// Draw the color square
117+
ui.painter().rect(rect, 2.0, *color_option, stroke);
118+
}
119+
});
120+
121+
// Now, create the 1D color bar slider below the grid
122+
ui.separator(); // Optional visual separator between grid and color bar
123+
// Add a 1D color slider below the color grid
124+
let response = color_slider_1d(ui, value, |t| {
125+
// Generate hue-based colors
126+
let hue = t * 360.0; // Convert t from [0.0, 1.0] to [0.0, 360.0]
127+
hsv_to_rgb(hue, 1.0, 1.0) // Full saturation and value
128+
});
129+
if response.clicked() || response.changed() || response.dragged() {
130+
// Update the selected color based on the slider position
131+
*color = hsv_to_rgb(*value * 360.0, 1.0, 1.0); // Update color
132+
}
133+
ui.add_space(5.0);
134+
ui.centered_and_justified(|ui| {
135+
if ui.button("Exit").clicked() {
136+
save_button = true;
137+
}
138+
});
139+
});
140+
});
141+
142+
save_button
143+
}
144+
145+
// Function to create a 1D color slider
146+
fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color32) -> Response {
147+
const N: usize = 100; // Number of segments
148+
149+
let desired_size = vec2(ui.spacing().slider_width, ui.spacing().interact_size.y);
150+
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());
151+
152+
if let Some(mpos) = response.interact_pointer_pos() {
153+
*value = remap_clamp(mpos.x, rect.left()..=rect.right(), 0.0..=1.0);
154+
}
155+
156+
if ui.is_rect_visible(rect) {
157+
let visuals = ui.style().interact(&response);
158+
159+
// Fill the color gradient
160+
let mut mesh = Mesh::default();
161+
for i in 0..=N {
162+
let t = i as f32 / (N as f32);
163+
let color = color_at(t);
164+
let x = lerp(rect.left()..=rect.right(), t);
165+
mesh.colored_vertex(pos2(x, rect.top()), color);
166+
mesh.colored_vertex(pos2(x, rect.bottom()), color);
167+
if i < N {
168+
mesh.add_triangle((2 * i + 0) as u32, (2 * i + 1) as u32, (2 * i + 2) as u32);
169+
mesh.add_triangle((2 * i + 1) as u32, (2 * i + 2) as u32, (2 * i + 3) as u32);
170+
}
171+
}
172+
ui.painter().add(Shape::mesh(mesh));
173+
174+
ui.painter().rect_stroke(rect, 0.0, visuals.bg_stroke); // outline
175+
176+
// Show where the slider is at:
177+
let x = lerp(rect.left()..=rect.right(), *value);
178+
let r = rect.height() / 4.0;
179+
let picked_color = color_at(*value);
180+
ui.painter().add(Shape::convex_polygon(
181+
vec![
182+
pos2(x, rect.center().y), // tip
183+
pos2(x + r, rect.bottom()), // right bottom
184+
pos2(x - r, rect.bottom()), // left bottom
185+
],
186+
picked_color,
187+
Stroke::new(visuals.fg_stroke.width, contrast_color(picked_color)),
188+
));
189+
}
190+
191+
response
192+
}
193+
194+
// Convert HSV color to RGB
195+
fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> Color32 {
196+
let c = value * saturation;
197+
let x = c * (1.0 - ((hue / 60.0) % 2.0 - 1.0).abs());
198+
let m = value - c;
199+
200+
let (r, g, b) = if hue < 60.0 {
201+
(c, x, 0.0)
202+
} else if hue < 120.0 {
203+
(x, c, 0.0)
204+
} else if hue < 180.0 {
205+
(0.0, c, x)
206+
} else if hue < 240.0 {
207+
(0.0, x, c)
208+
} else if hue < 300.0 {
209+
(x, 0.0, c)
210+
} else {
211+
(c, 0.0, x)
212+
};
213+
214+
Color32::from_rgb(
215+
((r + m) * 255.0) as u8,
216+
((g + m) * 255.0) as u8,
217+
((b + m) * 255.0) as u8,
218+
)
219+
}
220+
221+
// Function to interpolate between two colors
222+
fn lerp_color(c1: Color32, c2: Color32, t: f32) -> Color32 {
223+
let r = (c1.r() as f32 * (1.0 - t) + c2.r() as f32 * t).round() as u8;
224+
let g = (c1.g() as f32 * (1.0 - t) + c2.g() as f32 * t).round() as u8;
225+
let b = (c1.b() as f32 * (1.0 - t) + c2.b() as f32 * t).round() as u8;
226+
Color32::from_rgb(r, g, b)
227+
}

src/data.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::color_picker::COLORS;
2+
use eframe::egui::Color32;
13
use std::fmt;
24
use std::time::{SystemTime, UNIX_EPOCH};
35

@@ -45,7 +47,6 @@ impl Default for Packet {
4547
#[derive(Clone, Debug)]
4648
pub struct DataContainer {
4749
pub time: Vec<u128>,
48-
pub names: Vec<String>,
4950
pub absolute_time: Vec<u128>,
5051
pub dataset: Vec<Vec<f32>>,
5152
pub raw_traffic: Vec<Packet>,
@@ -55,7 +56,6 @@ impl Default for DataContainer {
5556
fn default() -> DataContainer {
5657
DataContainer {
5758
time: vec![],
58-
names: vec!["Column 0".to_string()],
5959
absolute_time: vec![],
6060
dataset: vec![vec![]],
6161
raw_traffic: vec![],

0 commit comments

Comments
 (0)