1use std::marker::PhantomData;
2
3use super::builder::LabelAreaPosition;
4use super::context::ChartContext;
5use crate::coord::cartesian::{Cartesian2d, MeshLine};
6use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter};
7use crate::drawing::DrawingAreaErrorKind;
8use crate::style::{
9 AsRelative, Color, FontDesc, FontFamily, FontStyle, IntoTextStyle, RGBColor, ShapeStyle,
10 SizeDesc, TextStyle,
11};
12
13use plotters_backend::DrawingBackend;
14
15pub struct SecondaryMeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> {
17 style: MeshStyle<'a, 'b, X, Y, DB>,
18}
19
20impl<'a, 'b, XT, YT, X: Ranged<ValueType = XT>, Y: Ranged<ValueType = YT>, DB: DrawingBackend>
21 SecondaryMeshStyle<'a, 'b, X, Y, DB>
22where
23 X: ValueFormatter<XT>,
24 Y: ValueFormatter<YT>,
25{
26 pub(super) fn new(target: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self {
27 let mut style = target.configure_mesh();
28 style.draw_x_mesh = false;
29 style.draw_y_mesh = false;
30 Self { style }
31 }
32
33 pub fn axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self {
36 self.style.axis_style(style);
37 self
38 }
39
40 pub fn x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self {
45 self.style.x_label_offset(value);
46 self
47 }
48
49 pub fn y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self {
54 self.style.y_label_offset(value);
55 self
56 }
57
58 pub fn x_labels(&mut self, value: usize) -> &mut Self {
61 self.style.x_labels(value);
62 self
63 }
64
65 pub fn y_labels(&mut self, value: usize) -> &mut Self {
68 self.style.y_labels(value);
69 self
70 }
71
72 pub fn x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self {
75 self.style.x_label_formatter(fmt);
76 self
77 }
78
79 pub fn y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self {
82 self.style.y_label_formatter(fmt);
83 self
84 }
85
86 pub fn axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
89 self.style
90 .axis_desc_style(style.into_text_style(&self.style.parent_size));
91 self
92 }
93
94 pub fn x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self {
97 self.style.x_desc(desc);
98 self
99 }
100
101 pub fn y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self {
104 self.style.y_desc(desc);
105 self
106 }
107
108 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> {
110 self.style.draw()
111 }
112
113 pub fn label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
115 self.style.label_style(style);
116 self
117 }
118
119 pub fn set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self {
122 let size = value.in_pixels(&self.style.parent_size);
123 self.style.x_tick_size = [size, size];
124 self.style.y_tick_size = [size, size];
125 self
126 }
127
128 pub fn set_tick_mark_size<S: SizeDesc>(
129 &mut self,
130 pos: LabelAreaPosition,
131 value: S,
132 ) -> &mut Self {
133 *match pos {
134 LabelAreaPosition::Top => &mut self.style.x_tick_size[0],
135 LabelAreaPosition::Bottom => &mut self.style.x_tick_size[1],
136 LabelAreaPosition::Left => &mut self.style.y_tick_size[0],
137 LabelAreaPosition::Right => &mut self.style.y_tick_size[1],
138 } = value.in_pixels(&self.style.parent_size);
139 self
140 }
141}
142
143pub struct MeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> {
145 pub(super) parent_size: (u32, u32),
146 pub(super) draw_x_mesh: bool,
147 pub(super) draw_y_mesh: bool,
148 pub(super) draw_x_axis: bool,
149 pub(super) draw_y_axis: bool,
150 pub(super) x_label_offset: i32,
151 pub(super) y_label_offset: i32,
152 pub(super) n_x_labels: usize,
153 pub(super) n_y_labels: usize,
154 pub(super) axis_desc_style: Option<TextStyle<'b>>,
155 pub(super) x_desc: Option<String>,
156 pub(super) y_desc: Option<String>,
157 pub(super) bold_line_style: Option<ShapeStyle>,
158 pub(super) light_line_style: Option<ShapeStyle>,
159 pub(super) axis_style: Option<ShapeStyle>,
160 pub(super) x_label_style: Option<TextStyle<'b>>,
161 pub(super) y_label_style: Option<TextStyle<'b>>,
162 pub(super) format_x: &'b dyn Fn(&X::ValueType) -> String,
163 pub(super) format_y: &'b dyn Fn(&Y::ValueType) -> String,
164 pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>>,
165 pub(super) _phantom_data: PhantomData<(X, Y)>,
166 pub(super) x_tick_size: [i32; 2],
167 pub(super) y_tick_size: [i32; 2],
168}
169
170impl<'a, 'b, X, Y, XT, YT, DB> MeshStyle<'a, 'b, X, Y, DB>
171where
172 X: Ranged<ValueType = XT> + ValueFormatter<XT>,
173 Y: Ranged<ValueType = YT> + ValueFormatter<YT>,
174 DB: DrawingBackend,
175{
176 pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self {
177 let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area());
178
179 let mut x_tick_size = [base_tick_size, base_tick_size];
180 let mut y_tick_size = [base_tick_size, base_tick_size];
181
182 for idx in 0..2 {
183 if chart.is_overlapping_drawing_area(chart.x_label_area[idx].as_ref()) {
184 x_tick_size[idx] = -x_tick_size[idx];
185 }
186 if chart.is_overlapping_drawing_area(chart.y_label_area[idx].as_ref()) {
187 y_tick_size[idx] = -y_tick_size[idx];
188 }
189 }
190
191 MeshStyle {
192 parent_size: chart.drawing_area.dim_in_pixel(),
193 axis_style: None,
194 x_label_offset: 0,
195 y_label_offset: 0,
196 draw_x_mesh: true,
197 draw_y_mesh: true,
198 draw_x_axis: true,
199 draw_y_axis: true,
200 n_x_labels: 10,
201 n_y_labels: 10,
202 bold_line_style: None,
203 light_line_style: None,
204 x_label_style: None,
205 y_label_style: None,
206 format_x: &X::format,
207 format_y: &Y::format,
208 target: Some(chart),
209 _phantom_data: PhantomData,
210 x_desc: None,
211 y_desc: None,
212 axis_desc_style: None,
213 x_tick_size,
214 y_tick_size,
215 }
216 }
217}
218
219impl<'a, 'b, X, Y, DB> MeshStyle<'a, 'b, X, Y, DB>
220where
221 X: Ranged,
222 Y: Ranged,
223 DB: DrawingBackend,
224{
225 pub fn set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self {
228 let size = value.in_pixels(&self.parent_size);
229 self.x_tick_size = [size, size];
230 self.y_tick_size = [size, size];
231 self
232 }
233
234 pub fn set_tick_mark_size<S: SizeDesc>(
240 &mut self,
241 pos: LabelAreaPosition,
242 value: S,
243 ) -> &mut Self {
244 *match pos {
245 LabelAreaPosition::Top => &mut self.x_tick_size[0],
246 LabelAreaPosition::Bottom => &mut self.x_tick_size[1],
247 LabelAreaPosition::Left => &mut self.y_tick_size[0],
248 LabelAreaPosition::Right => &mut self.y_tick_size[1],
249 } = value.in_pixels(&self.parent_size);
250 self
251 }
252
253 pub fn x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self {
258 self.x_label_offset = value.in_pixels(&self.parent_size);
259 self
260 }
261
262 pub fn y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self {
267 self.y_label_offset = value.in_pixels(&self.parent_size);
268 self
269 }
270
271 pub fn disable_x_mesh(&mut self) -> &mut Self {
273 self.draw_x_mesh = false;
274 self
275 }
276
277 pub fn disable_y_mesh(&mut self) -> &mut Self {
279 self.draw_y_mesh = false;
280 self
281 }
282
283 pub fn disable_x_axis(&mut self) -> &mut Self {
285 self.draw_x_axis = false;
286 self
287 }
288
289 pub fn disable_y_axis(&mut self) -> &mut Self {
291 self.draw_y_axis = false;
292 self
293 }
294
295 pub fn disable_mesh(&mut self) -> &mut Self {
297 self.disable_x_mesh().disable_y_mesh()
298 }
299
300 pub fn disable_axes(&mut self) -> &mut Self {
302 self.disable_x_axis().disable_y_axis()
303 }
304
305 pub fn axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self {
308 self.axis_style = Some(style.into());
309 self
310 }
311 pub fn x_labels(&mut self, value: usize) -> &mut Self {
314 self.n_x_labels = value;
315 self
316 }
317
318 pub fn y_labels(&mut self, value: usize) -> &mut Self {
321 self.n_y_labels = value;
322 self
323 }
324
325 pub fn bold_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self {
328 self.bold_line_style = Some(style.into());
329 self
330 }
331
332 pub fn light_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self {
335 self.light_line_style = Some(style.into());
336 self
337 }
338
339 pub fn label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
342 let style = style.into_text_style(&self.parent_size);
343 self.x_label_style = Some(style.clone());
344 self.y_label_style = Some(style);
345 self
346 }
347
348 pub fn x_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
351 self.x_label_style = Some(style.into_text_style(&self.parent_size));
352 self
353 }
354
355 pub fn y_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
358 self.y_label_style = Some(style.into_text_style(&self.parent_size));
359 self
360 }
361
362 pub fn x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self {
365 self.format_x = fmt;
366 self
367 }
368
369 pub fn y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self {
372 self.format_y = fmt;
373 self
374 }
375
376 pub fn axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self {
379 self.axis_desc_style = Some(style.into_text_style(&self.parent_size));
380 self
381 }
382
383 pub fn x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self {
386 self.x_desc = Some(desc.into());
387 self
388 }
389
390 pub fn y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self {
393 self.y_desc = Some(desc.into());
394 self
395 }
396
397 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> {
399 let target = self.target.take().unwrap();
400
401 let default_mesh_color_1 = RGBColor(0, 0, 0).mix(0.2);
402 let default_mesh_color_2 = RGBColor(0, 0, 0).mix(0.1);
403 let default_axis_color = RGBColor(0, 0, 0);
404 let default_label_font = FontDesc::new(
405 FontFamily::SansSerif,
406 f64::from((12i32).percent().max(12).in_pixels(&self.parent_size)),
407 FontStyle::Normal,
408 );
409
410 let bold_style = self
411 .bold_line_style
412 .clone()
413 .unwrap_or_else(|| (&default_mesh_color_1).into());
414 let light_style = self
415 .light_line_style
416 .clone()
417 .unwrap_or_else(|| (&default_mesh_color_2).into());
418 let axis_style = self
419 .axis_style
420 .clone()
421 .unwrap_or_else(|| (&default_axis_color).into());
422
423 let x_label_style = self
424 .x_label_style
425 .clone()
426 .unwrap_or_else(|| default_label_font.clone().into());
427
428 let y_label_style = self
429 .y_label_style
430 .clone()
431 .unwrap_or_else(|| default_label_font.into());
432
433 let axis_desc_style = self
434 .axis_desc_style
435 .clone()
436 .unwrap_or_else(|| x_label_style.clone());
437
438 target.draw_mesh(
439 (
440 LightPoints::new(self.n_y_labels, self.n_y_labels * 10),
441 LightPoints::new(self.n_x_labels, self.n_x_labels * 10),
442 ),
443 &light_style,
444 &x_label_style,
445 &y_label_style,
446 |_| None,
447 self.draw_x_mesh,
448 self.draw_y_mesh,
449 self.x_label_offset,
450 self.y_label_offset,
451 false,
452 false,
453 &axis_style,
454 &axis_desc_style,
455 self.x_desc.clone(),
456 self.y_desc.clone(),
457 self.x_tick_size,
458 self.y_tick_size,
459 )?;
460
461 target.draw_mesh(
462 (BoldPoints(self.n_y_labels), BoldPoints(self.n_x_labels)),
463 &bold_style,
464 &x_label_style,
465 &y_label_style,
466 |m| match m {
467 MeshLine::XMesh(_, _, v) => {
468 if self.draw_x_axis {
469 Some((self.format_x)(v))
470 } else {
471 None
472 }
473 }
474 MeshLine::YMesh(_, _, v) => {
475 if self.draw_y_axis {
476 Some((self.format_y)(v))
477 } else {
478 None
479 }
480 }
481 },
482 self.draw_x_mesh,
483 self.draw_y_mesh,
484 self.x_label_offset,
485 self.y_label_offset,
486 self.draw_x_axis,
487 self.draw_y_axis,
488 &axis_style,
489 &axis_desc_style,
490 None,
491 None,
492 self.x_tick_size,
493 self.y_tick_size,
494 )
495 }
496}