1use crate::Theme;
4use egui::{vec2, Color32, Pos2, Sense, Stroke, Vec2};
5use eulumdat::{diagram::PolarDiagram, Eulumdat};
6
7pub struct PolarWidget;
9
10impl PolarWidget {
11 pub fn show(ui: &mut egui::Ui, ldt: &Eulumdat, theme: &Theme) {
13 let available_size = ui.available_size();
14 let size = available_size.min_elem().min(600.0);
15 let (response, painter) = ui.allocate_painter(Vec2::splat(size), Sense::hover());
16
17 let rect = response.rect;
18 let center = rect.center();
19 let radius = (size / 2.0) * 0.85;
20
21 painter.rect_filled(rect, 0.0, theme.background);
23
24 let polar = PolarDiagram::from_eulumdat(ldt);
26
27 Self::draw_grid(&painter, center, radius, &polar, theme);
29
30 Self::draw_angle_labels(&painter, center, radius, theme);
32
33 Self::draw_curve(
35 &painter,
36 center,
37 radius,
38 &polar.c0_c180_curve.points,
39 polar.scale.scale_max,
40 theme.primary_curve,
41 );
42
43 if polar.show_c90_c270() {
44 Self::draw_curve(
45 &painter,
46 center,
47 radius,
48 &polar.c90_c270_curve.points,
49 polar.scale.scale_max,
50 theme.secondary_curve,
51 );
52 }
53
54 Self::draw_legend(ui, &polar, theme);
56 }
57
58 fn draw_grid(
59 painter: &egui::Painter,
60 center: Pos2,
61 radius: f32,
62 polar: &PolarDiagram,
63 theme: &Theme,
64 ) {
65 let grid_stroke = Stroke::new(1.0, theme.grid);
66 let axis_stroke = Stroke::new(1.5, theme.axis);
67
68 for &value in polar.scale.grid_values.iter() {
70 let r = radius * (value / polar.scale.scale_max) as f32;
71 painter.circle_stroke(center, r, grid_stroke);
72
73 let label_pos = center + vec2(r + 5.0, 0.0);
75 painter.text(
76 label_pos,
77 egui::Align2::LEFT_CENTER,
78 format!("{:.0}", value),
79 egui::FontId::proportional(10.0),
80 theme.text,
81 );
82 }
83
84 for angle_deg in (0..360).step_by(30) {
86 let angle_rad = (angle_deg as f32 - 90.0).to_radians();
87 let outer = center + radius * vec2(angle_rad.cos(), angle_rad.sin());
88 painter.line_segment([center, outer], grid_stroke);
89 }
90
91 painter.line_segment(
93 [center - vec2(radius, 0.0), center + vec2(radius, 0.0)],
94 axis_stroke,
95 );
96 painter.line_segment(
97 [center - vec2(0.0, radius), center + vec2(0.0, radius)],
98 axis_stroke,
99 );
100 }
101
102 fn draw_angle_labels(painter: &egui::Painter, center: Pos2, radius: f32, theme: &Theme) {
103 let labels = [
104 (0, "0°", egui::Align2::CENTER_BOTTOM),
105 (90, "90°", egui::Align2::LEFT_CENTER),
106 (180, "180°", egui::Align2::CENTER_TOP),
107 (270, "90°", egui::Align2::RIGHT_CENTER),
108 ];
109
110 for (angle_deg, label, align) in labels {
111 let angle_rad = (angle_deg as f32 - 90.0).to_radians();
112 let pos = center + (radius + 15.0) * vec2(angle_rad.cos(), angle_rad.sin());
113 painter.text(
114 pos,
115 align,
116 label,
117 egui::FontId::proportional(12.0),
118 theme.text,
119 );
120 }
121 }
122
123 fn draw_curve(
124 painter: &egui::Painter,
125 center: Pos2,
126 radius: f32,
127 points: &[eulumdat::diagram::PolarPoint],
128 scale_max: f64,
129 color: Color32,
130 ) {
131 if points.is_empty() {
132 return;
133 }
134
135 let stroke = Stroke::new(2.0, color);
136 let screen_points: Vec<Pos2> = points
137 .iter()
138 .map(|p| {
139 let r = (p.intensity / scale_max) as f32 * radius;
142 let angle_rad = (p.gamma as f32 - 90.0).to_radians();
143 center + r * vec2(angle_rad.cos(), angle_rad.sin())
144 })
145 .collect();
146
147 for pair in screen_points.windows(2) {
149 painter.line_segment([pair[0], pair[1]], stroke);
150 }
151 }
152
153 fn draw_legend(ui: &mut egui::Ui, polar: &PolarDiagram, theme: &Theme) {
154 ui.horizontal(|ui| {
155 let (rect, _) = ui.allocate_exact_size(vec2(20.0, 3.0), Sense::hover());
157 ui.painter().rect_filled(rect, 0.0, theme.primary_curve);
158 ui.label("C0-C180");
159
160 if polar.show_c90_c270() {
161 ui.add_space(20.0);
162 let (rect, _) = ui.allocate_exact_size(vec2(20.0, 3.0), Sense::hover());
163 ui.painter().rect_filled(rect, 0.0, theme.secondary_curve);
164 ui.label("C90-C270");
165 }
166
167 ui.add_space(20.0);
168 ui.label(format!("Max: {:.0} cd/klm", polar.scale.max_intensity));
169 });
170 }
171}