iced_audio/graphics/tick_marks/
radial.rs1use iced_graphics::widget::canvas::{self, Fill, Frame, LineCap, Path, Stroke};
2use iced_graphics::Primitive;
3use iced_native::{Color, Point, Size, Vector};
4
5use super::PrimitiveCache;
6use crate::core::Normal;
7use crate::native::tick_marks;
8use crate::style::tick_marks::{Appearance, Shape};
9
10#[allow(clippy::too_many_arguments)]
11fn draw_radial_circles(
12 frame: &mut Frame,
13 offset_radius: f32,
14 start_angle: f32,
15 angle_span: f32,
16 tick_marks: &[Normal],
17 color: Color,
18 radius: f32,
19 inverse: bool,
20) {
21 let path = Path::circle(Point::new(0.0, -offset_radius), radius);
22
23 if inverse {
24 for tick_mark in tick_marks {
25 let angle = start_angle + tick_mark.scale_inv(angle_span);
26
27 frame.with_save(|frame| {
28 if !(-0.001..=0.001).contains(&angle) {
29 frame.rotate(angle);
30 }
31
32 frame.fill(
33 &path,
34 Fill {
35 style: canvas::Style::Solid(color),
36 ..Fill::default()
37 },
38 );
39 });
40 }
41 } else {
42 for tick_mark in tick_marks {
43 let angle = start_angle + tick_mark.scale(angle_span);
44
45 frame.with_save(|frame| {
46 if !(-0.001..=0.001).contains(&angle) {
47 frame.rotate(angle);
48 }
49
50 frame.fill(
51 &path,
52 Fill {
53 style: canvas::Style::Solid(color),
54 ..Fill::default()
55 },
56 );
57 });
58 }
59 }
60}
61
62#[allow(clippy::too_many_arguments)]
63fn draw_radial_lines(
64 frame: &mut Frame,
65 offset_radius: f32,
66 start_angle: f32,
67 angle_span: f32,
68 tick_marks: &[Normal],
69 color: Color,
70 width: f32,
71 length: f32,
72 inverse: bool,
73) {
74 let path = Path::line(
75 Point::new(0.0, -offset_radius),
76 Point::new(0.0, -offset_radius - length),
77 );
78
79 if inverse {
80 for tick_mark in tick_marks {
81 let angle = start_angle + tick_mark.scale_inv(angle_span);
82
83 frame.with_save(|frame| {
84 if !(-0.001..=0.001).contains(&angle) {
85 frame.rotate(angle);
86 }
87
88 frame.stroke(
89 &path,
90 Stroke {
91 width,
92 style: canvas::Style::Solid(color),
93 line_cap: LineCap::Butt,
94 ..Stroke::default()
95 },
96 );
97 });
98 }
99 } else {
100 for tick_mark in tick_marks {
101 let angle = start_angle + tick_mark.scale(angle_span);
102
103 frame.with_save(|frame| {
104 if !(-0.001..=0.001).contains(&angle) {
105 frame.rotate(angle);
106 }
107
108 frame.stroke(
109 &path,
110 Stroke {
111 width,
112 style: canvas::Style::Solid(color),
113 line_cap: LineCap::Butt,
114 ..Stroke::default()
115 },
116 );
117 });
118 }
119 }
120}
121
122#[inline]
123#[allow(clippy::too_many_arguments)]
124fn draw_tier(
125 frame: &mut Frame,
126 offset_radius: f32,
127 start_angle: f32,
128 angle_span: f32,
129 tick_marks: Option<&Vec<Normal>>,
130 shape: &Shape,
131 inside: bool,
132 inverse: bool,
133) {
134 if let Some(tick_marks) = tick_marks {
135 match shape {
136 Shape::None => (),
137 Shape::Line {
138 length,
139 width,
140 color,
141 } => {
142 let length = *length;
143 let width = *width;
144
145 if inside {
146 draw_radial_lines(
147 frame,
148 offset_radius - length,
149 start_angle,
150 angle_span,
151 tick_marks,
152 *color,
153 width,
154 length,
155 inverse,
156 );
157 } else {
158 draw_radial_lines(
159 frame,
160 offset_radius,
161 start_angle,
162 angle_span,
163 tick_marks,
164 *color,
165 width,
166 length,
167 inverse,
168 );
169 }
170 }
171 Shape::Circle { diameter, color } => {
172 let radius = (*diameter) / 2.0;
173
174 if inside {
175 draw_radial_circles(
176 frame,
177 offset_radius - radius,
178 start_angle,
179 angle_span,
180 tick_marks,
181 *color,
182 radius,
183 inverse,
184 );
185 } else {
186 draw_radial_circles(
187 frame,
188 offset_radius + radius,
189 start_angle,
190 angle_span,
191 tick_marks,
192 *color,
193 radius,
194 inverse,
195 );
196 }
197 }
198 }
199 }
200}
201
202fn max_length(style: &Appearance) -> f32 {
203 let length_1 = match style.tier_1 {
204 Shape::None => 0.0,
205 Shape::Line { length, .. } => length,
206 Shape::Circle { diameter, .. } => diameter,
207 };
208
209 let length_2 = match style.tier_1 {
210 Shape::None => 0.0,
211 Shape::Line { length, .. } => length,
212 Shape::Circle { diameter, .. } => diameter,
213 };
214
215 let length_3 = match style.tier_1 {
216 Shape::None => 0.0,
217 Shape::Line { length, .. } => length,
218 Shape::Circle { diameter, .. } => diameter,
219 };
220
221 length_1.max(length_2).max(length_3)
222}
223
224#[allow(clippy::too_many_arguments)]
237pub fn draw_radial_tick_marks(
238 center: Point,
239 radius: f32,
240 start_angle: f32,
241 angle_span: f32,
242 inside: bool,
243 tick_marks: &tick_marks::Group,
244 style: &Appearance,
245 inverse: bool,
246 cache: &PrimitiveCache,
247) -> Primitive {
248 cache.cached_radial(
249 center,
250 radius,
251 start_angle,
252 angle_span,
253 inside,
254 tick_marks,
255 *style,
256 inverse,
257 || {
258 let frame_radius = if inside {
259 radius
260 } else {
261 radius + max_length(style)
262 };
263
264 let frame_size = frame_radius * 2.0;
265
266 let mut frame = Frame::new(Size::new(frame_size, frame_size));
267
268 frame.translate(Vector::new(frame_radius, frame_radius));
269
270 draw_tier(
271 &mut frame,
272 radius,
273 start_angle,
274 angle_span,
275 tick_marks.tier_1(),
276 &style.tier_1,
277 inside,
278 inverse,
279 );
280 draw_tier(
281 &mut frame,
282 radius,
283 start_angle,
284 angle_span,
285 tick_marks.tier_2(),
286 &style.tier_2,
287 inside,
288 inverse,
289 );
290 draw_tier(
291 &mut frame,
292 radius,
293 start_angle,
294 angle_span,
295 tick_marks.tier_3(),
296 &style.tier_3,
297 inside,
298 inverse,
299 );
300
301 Primitive::Translate {
302 translation: Vector::new(
303 center.x - frame_radius,
304 center.y - frame_radius,
305 ),
306 content: Box::new(frame.into_geometry().into_primitive()),
307 }
308 },
309 )
310}