1use crate::Theme;
4use egui::{pos2, vec2, Pos2, Rect, Sense, Stroke};
5use eulumdat::{diagram::CartesianDiagram, Eulumdat};
6
7pub struct CartesianWidget;
9
10impl CartesianWidget {
11 pub fn show(ui: &mut egui::Ui, ldt: &Eulumdat, theme: &Theme) {
13 let available_size = ui.available_size();
14 let width = available_size.x.min(800.0);
15 let height = (width * 0.6).min(available_size.y - 50.0);
16
17 let (response, painter) = ui.allocate_painter(vec2(width, height), Sense::hover());
18 let rect = response.rect;
19
20 let margin = vec2(60.0, 40.0);
22 let plot_rect = Rect::from_min_max(rect.min + margin, rect.max - vec2(20.0, 30.0));
23
24 painter.rect_filled(rect, 0.0, theme.background);
26
27 let cartesian = CartesianDiagram::from_eulumdat(ldt, width as f64, height as f64, 8);
29
30 Self::draw_grid(&painter, plot_rect, &cartesian, theme);
32
33 for curve in cartesian.curves.iter() {
35 let color = theme.c_plane_color(curve.c_angle, cartesian.curves.len());
36 Self::draw_curve(&painter, plot_rect, curve, &cartesian, color);
37 }
38
39 Self::draw_axes(&painter, plot_rect, &cartesian, theme);
41
42 Self::draw_legend(ui, &cartesian, theme);
44 }
45
46 fn draw_grid(
47 painter: &egui::Painter,
48 rect: Rect,
49 _cartesian: &CartesianDiagram,
50 theme: &Theme,
51 ) {
52 let grid_stroke = Stroke::new(1.0, theme.grid);
53
54 for gamma in (0..=180).step_by(30) {
56 let x = rect.left() + (gamma as f32 / 180.0) * rect.width();
57 painter.line_segment([pos2(x, rect.top()), pos2(x, rect.bottom())], grid_stroke);
58 }
59
60 let num_lines = 5;
62 for i in 0..=num_lines {
63 let y = rect.bottom() - (i as f32 / num_lines as f32) * rect.height();
64 painter.line_segment([pos2(rect.left(), y), pos2(rect.right(), y)], grid_stroke);
65 }
66 }
67
68 fn draw_curve(
69 painter: &egui::Painter,
70 rect: Rect,
71 curve: &eulumdat::diagram::CartesianCurve,
72 cartesian: &CartesianDiagram,
73 color: egui::Color32,
74 ) {
75 if curve.points.is_empty() {
76 return;
77 }
78
79 let stroke = Stroke::new(2.0, color);
80 let max_intensity = cartesian.scale.scale_max;
81
82 let screen_points: Vec<Pos2> = curve
83 .points
84 .iter()
85 .map(|p| {
86 let x = rect.left() + (p.gamma as f32 / 180.0) * rect.width();
87 let y = rect.bottom() - (p.intensity as f32 / max_intensity as f32) * rect.height();
88 pos2(x, y.max(rect.top()))
89 })
90 .collect();
91
92 for pair in screen_points.windows(2) {
93 painter.line_segment([pair[0], pair[1]], stroke);
94 }
95 }
96
97 fn draw_axes(painter: &egui::Painter, rect: Rect, cartesian: &CartesianDiagram, theme: &Theme) {
98 let axis_stroke = Stroke::new(1.5, theme.axis);
99
100 painter.line_segment(
102 [
103 pos2(rect.left(), rect.bottom()),
104 pos2(rect.right(), rect.bottom()),
105 ],
106 axis_stroke,
107 );
108
109 painter.line_segment(
111 [
112 pos2(rect.left(), rect.top()),
113 pos2(rect.left(), rect.bottom()),
114 ],
115 axis_stroke,
116 );
117
118 for gamma in (0..=180).step_by(30) {
120 let x = rect.left() + (gamma as f32 / 180.0) * rect.width();
121 painter.text(
122 pos2(x, rect.bottom() + 15.0),
123 egui::Align2::CENTER_TOP,
124 format!("{}°", gamma),
125 egui::FontId::proportional(10.0),
126 theme.text,
127 );
128 }
129
130 let num_labels = 5;
132 for i in 0..=num_labels {
133 let value = cartesian.scale.scale_max * (i as f64 / num_labels as f64);
134 let y = rect.bottom() - (i as f32 / num_labels as f32) * rect.height();
135 painter.text(
136 pos2(rect.left() - 10.0, y),
137 egui::Align2::RIGHT_CENTER,
138 format!("{:.0}", value),
139 egui::FontId::proportional(10.0),
140 theme.text,
141 );
142 }
143
144 painter.text(
146 pos2(rect.center().x, rect.bottom() + 30.0),
147 egui::Align2::CENTER_TOP,
148 "Gamma (°)",
149 egui::FontId::proportional(12.0),
150 theme.text,
151 );
152 }
153
154 fn draw_legend(ui: &mut egui::Ui, cartesian: &CartesianDiagram, theme: &Theme) {
155 ui.horizontal_wrapped(|ui| {
156 for curve in &cartesian.curves {
157 let color = theme.c_plane_color(curve.c_angle, cartesian.curves.len());
158 let (rect, _) = ui.allocate_exact_size(vec2(15.0, 3.0), Sense::hover());
159 ui.painter().rect_filled(rect, 0.0, color);
160 ui.label(format!("C{:.0}°", curve.c_angle));
161 ui.add_space(10.0);
162 }
163 });
164 }
165}