1use crate::coord::Coord;
5use crate::render::backend::{DrawBackend, LineStyle, TextAnchor, TextStyle};
6use crate::render::{Rect, RenderError};
7use crate::scale::Scale;
8use crate::theme::Theme;
9
10pub fn draw_radar_guides(
12 x_scale: &dyn Scale,
13 y_scale: &dyn Scale,
14 coord: &dyn Coord,
15 theme: &Theme,
16 area: &Rect,
17 backend: &mut dyn DrawBackend,
18) -> Result<(), RenderError> {
19 let x_breaks = x_scale.breaks();
20 let y_breaks = y_scale.breaks();
21 let ring_pos: Vec<f64> = if x_scale.is_discrete() && x_breaks.len() >= 3 {
23 x_breaks.iter().map(|(p, _)| *p).collect()
24 } else {
25 (0..72).map(|k| k as f64 / 72.0).collect()
26 };
27 let centre = coord.transform((0.0, f64::NEG_INFINITY), area);
28
29 let grid = theme.get_panel_grid_major_y();
30 if grid.visible {
31 let style = LineStyle {
32 color: grid.color,
33 width: grid.width,
34 alpha: 1.0,
35 linetype: grid.linetype.clone(),
36 ..Default::default()
37 };
38 for (ny, _) in &y_breaks {
39 if !(0.0..=1.0 + 1e-9).contains(ny) {
40 continue;
41 }
42 let mut ring: Vec<(f64, f64)> = ring_pos
43 .iter()
44 .map(|&p| coord.transform((p, *ny), area))
45 .collect();
46 if let Some(&first) = ring.first() {
47 ring.push(first);
48 }
49 backend.draw_line(&ring, &style)?;
50 }
51 }
52 let spoke = theme.get_panel_grid_major_x();
53 if spoke.visible {
54 let style = LineStyle {
55 color: spoke.color,
56 width: spoke.width,
57 alpha: 1.0,
58 linetype: spoke.linetype.clone(),
59 ..Default::default()
60 };
61 for (p, _) in &x_breaks {
62 backend.draw_line(&[centre, coord.transform((*p, 1.0), area)], &style)?;
63 }
64 }
65
66 let family = |f: &str| (!f.is_empty()).then(|| f.to_string());
67 if theme.axis_text_x.visible {
68 for (p, label) in &x_breaks {
69 let outer = coord.transform((*p, 1.0), area);
70 let (dx, dy) = (outer.0 - centre.0, outer.1 - centre.1);
71 let len = (dx * dx + dy * dy).sqrt().max(1e-9);
72 let pad = theme.axis_text_x.size * 0.9;
73 let pos = (outer.0 + dx / len * pad, outer.1 + dy / len * pad);
74 let anchor = if dx.abs() < len * 0.1 {
75 TextAnchor::Middle
76 } else if dx > 0.0 {
77 TextAnchor::Start
78 } else {
79 TextAnchor::End
80 };
81 backend.draw_text(
82 label,
83 pos,
84 &TextStyle {
85 color: theme.axis_text_x.color,
86 size: theme.axis_text_x.size,
87 anchor,
88 angle: 0.0,
89 family: family(&theme.axis_text_x.family),
90 face: theme.axis_text_x.face,
91 bg_colour: None,
92 bg_r: 0.0,
93 },
94 )?;
95 }
96 }
97 if theme.axis_text_y.visible {
98 let p0 = x_breaks.first().map(|(p, _)| *p).unwrap_or(0.0);
100 for (ny, label) in &y_breaks {
101 if !(0.0..=1.0 + 1e-9).contains(ny) {
102 continue;
103 }
104 let (x, y) = coord.transform((p0, *ny), area);
105 backend.draw_text(
106 label,
107 (x + 3.0, y),
108 &TextStyle {
109 color: theme.axis_text_y.color,
110 size: theme.axis_text_y.size * 0.85,
111 anchor: TextAnchor::Start,
112 angle: 0.0,
113 family: family(&theme.axis_text_y.family),
114 face: theme.axis_text_y.face,
115 bg_colour: None,
116 bg_r: 0.0,
117 },
118 )?;
119 }
120 }
121 Ok(())
122}