1use crate::aes::Aesthetic;
2use crate::annotate::Annotation;
3use crate::build::BuiltPlot;
4use crate::facet::Facet;
5use crate::guide::{axis, legend};
6use crate::render::backend::{DrawBackend, LineStyle, Linetype, RectStyle, TextAnchor, TextStyle};
7use crate::render::RenderError;
8
9pub struct PlotRenderer;
11
12impl PlotRenderer {
13 pub fn render(built: &BuiltPlot, backend: &mut dyn DrawBackend) -> Result<(), RenderError> {
14 if !built.facet.is_none() && !built.panels.is_empty() {
15 Self::render_faceted(built, backend)
16 } else {
17 Self::render_single(built, backend)
18 }
19 }
20
21 fn render_single(built: &BuiltPlot, backend: &mut dyn DrawBackend) -> Result<(), RenderError> {
22 let theme = &built.theme;
23 let plot_area = backend.plot_area();
24 let total_area = backend.total_area();
25
26 if theme.plot_background.visible {
28 if let Some(fill) = theme.plot_background.fill {
29 backend.draw_rect(
30 (total_area.x, total_area.y),
31 (
32 total_area.x + total_area.width,
33 total_area.y + total_area.height,
34 ),
35 &RectStyle {
36 fill: Some(fill),
37 stroke: None,
38 stroke_width: 0.0,
39 alpha: 1.0,
40 clip: false,
41 },
42 )?;
43 }
44 }
45
46 if theme.panel_background.visible {
48 if let Some(fill) = theme.panel_background.fill {
49 backend.draw_rect(
50 (plot_area.x, plot_area.y),
51 (
52 plot_area.x + plot_area.width,
53 plot_area.y + plot_area.height,
54 ),
55 &RectStyle {
56 fill: Some(fill),
57 stroke: theme.panel_background.color,
58 stroke_width: theme.panel_background.width,
59 alpha: 1.0,
60 clip: false,
61 },
62 )?;
63 }
64 }
65
66 if theme.panel_border.visible {
68 let style = LineStyle {
69 color: theme.panel_border.color,
70 width: theme.panel_border.width,
71 alpha: 1.0,
72 linetype: Linetype::Solid,
73 };
74 let x0 = plot_area.x;
75 let y0 = plot_area.y;
76 let x1 = plot_area.x + plot_area.width;
77 let y1 = plot_area.y + plot_area.height;
78 backend.draw_line(&[(x0, y0), (x1, y0)], &style)?;
79 backend.draw_line(&[(x1, y0), (x1, y1)], &style)?;
80 backend.draw_line(&[(x1, y1), (x0, y1)], &style)?;
81 backend.draw_line(&[(x0, y1), (x0, y0)], &style)?;
82 }
83
84 let x_scale = built.scales.get(&Aesthetic::X);
86 let y_scale = built.scales.get(&Aesthetic::Y);
87
88 let (h_scale, v_scale) = if built.coord.is_flipped() {
89 (y_scale, x_scale)
90 } else {
91 (x_scale, y_scale)
92 };
93
94 if let (true, Some(xs), Some(ys)) = (built.coord.is_radar(), x_scale, y_scale) {
95 crate::guide::polar::draw_radar_guides(
97 xs,
98 ys,
99 built.coord.as_ref(),
100 theme,
101 &plot_area,
102 backend,
103 )?;
104 } else if let (Some(hs), Some(vs)) = (h_scale, v_scale) {
105 if built.coord.gridlines() && !theme.panel_ontop {
107 axis::draw_gridlines(hs, vs, built.coord.as_ref(), theme, &plot_area, backend)?;
108 }
109
110 axis::draw_x_axis(hs, built.coord.as_ref(), theme, &plot_area, backend)?;
112 axis::draw_y_axis(vs, built.coord.as_ref(), theme, &plot_area, backend)?;
113
114 if let Some(sec) = built.scales.sec_axis(&Aesthetic::Y) {
116 axis::draw_sec_y_axis(vs, sec, built.coord.as_ref(), theme, &plot_area, backend)?;
117 }
118 }
119
120 for layer in built.layers.iter().filter(|l| l.data.nrows() > 0) {
124 layer.geom.draw(
125 &layer.data,
126 built.coord.as_ref(),
127 &built.scales,
128 theme,
129 backend,
130 )?;
131 }
132
133 if theme.panel_ontop && built.coord.gridlines() {
135 if let (Some(hs), Some(vs)) = (h_scale, v_scale) {
136 axis::draw_gridlines(hs, vs, built.coord.as_ref(), theme, &plot_area, backend)?;
137 }
138 }
139
140 Self::draw_annotations(
142 &built.annotations,
143 &built.scales,
144 built.coord.as_ref(),
145 &plot_area,
146 backend,
147 )?;
148
149 let title_ref = Self::title_ref_rect(theme, &plot_area, &total_area);
151 if let Some(ref title) = built.labels.title {
152 let (tx, anchor) = Self::hjust_pos(&theme.title, &title_ref);
153 let x_axis_top = built
156 .scales
157 .get(&Aesthetic::X)
158 .map(|s| s.axis_position_opposite())
159 .unwrap_or(false);
160 let x_axis_lift = if x_axis_top {
161 theme.axis_ticks_length + theme.axis_text_x.size + theme.legend_spacing
162 } else {
163 0.0
164 };
165 let title_y = plot_area.y - theme.title.size * 0.9 - x_axis_lift;
166 let family = if theme.title.family.is_empty() {
167 None
168 } else {
169 Some(theme.title.family.clone())
170 };
171 backend.draw_text(
172 title,
173 (tx, title_y.max(theme.title.size)),
174 &TextStyle {
175 color: theme.title.color,
176 size: theme.title.size,
177 anchor,
178 angle: 0.0,
179 family,
180 face: theme.title.face,
181 },
182 )?;
183 }
184
185 if let Some(ref subtitle) = built.labels.subtitle {
187 let (sx, anchor) = Self::hjust_pos(&theme.subtitle, &title_ref);
188 let subtitle_y = plot_area.y - 2.0;
189 let family = if theme.subtitle.family.is_empty() {
190 None
191 } else {
192 Some(theme.subtitle.family.clone())
193 };
194 backend.draw_text(
195 subtitle,
196 (sx, subtitle_y.max(theme.title.size + theme.subtitle.size)),
197 &TextStyle {
198 color: theme.subtitle.color,
199 size: theme.subtitle.size,
200 anchor,
201 angle: 0.0,
202 family,
203 face: theme.subtitle.face,
204 },
205 )?;
206 }
207
208 legend::draw_legend(
210 &built.scales,
211 theme,
212 &plot_area,
213 backend,
214 &built.guide_legend,
215 &built.suppressed_aes,
216 )?;
217
218 if let Some(ref caption) = built.labels.caption {
220 let (cx, anchor) = Self::hjust_pos(&theme.caption, &title_ref);
221 let caption_y = total_area.y + total_area.height - theme.caption.size * 0.5;
222 let family = if theme.caption.family.is_empty() {
223 None
224 } else {
225 Some(theme.caption.family.clone())
226 };
227 backend.draw_text(
228 caption,
229 (cx, caption_y),
230 &TextStyle {
231 color: theme.caption.color,
232 size: theme.caption.size,
233 anchor,
234 angle: 0.0,
235 family,
236 face: theme.caption.face,
237 },
238 )?;
239 }
240
241 Self::draw_tag(&built.labels, theme, &total_area, backend)?;
243
244 Ok(())
245 }
246
247 fn hjust_pos(
250 el: &crate::theme::elements::ElementText,
251 area: &crate::render::Rect,
252 ) -> (f64, TextAnchor) {
253 let x = area.x + el.hjust.clamp(0.0, 1.0) * area.width;
254 let anchor = if el.hjust <= 0.02 {
255 TextAnchor::Start
256 } else if el.hjust >= 0.98 {
257 TextAnchor::End
258 } else {
259 TextAnchor::Middle
260 };
261 (x, anchor)
262 }
263
264 fn title_ref_rect(
267 theme: &crate::theme::Theme,
268 plot_area: &crate::render::Rect,
269 total_area: &crate::render::Rect,
270 ) -> crate::render::Rect {
271 match theme.title_position {
272 crate::theme::TitlePosition::Panel => plot_area.clone(),
273 crate::theme::TitlePosition::Plot => crate::render::Rect {
274 x: total_area.x + theme.plot_margin.left,
275 y: plot_area.y,
276 width: (total_area.width - theme.plot_margin.left - theme.plot_margin.right)
277 .max(1.0),
278 height: plot_area.height,
279 },
280 }
281 }
282
283 fn draw_tag(
285 labels: &crate::plot::Labels,
286 theme: &crate::theme::Theme,
287 total_area: &crate::render::Rect,
288 backend: &mut dyn DrawBackend,
289 ) -> Result<(), RenderError> {
290 if let Some(ref tag) = labels.tag {
291 let family = if theme.title.family.is_empty() {
292 None
293 } else {
294 Some(theme.title.family.clone())
295 };
296 use crate::theme::TagPosition::*;
297 let pad = theme.title.size;
298 let (left, right) = (total_area.x + pad, total_area.x + total_area.width - pad);
299 let (top, bottom) = (total_area.y + pad, total_area.y + total_area.height - pad);
300 let (x, y, anchor) = match theme.tag_position {
301 TopLeft => (left, top, TextAnchor::Start),
302 TopRight => (right, top, TextAnchor::End),
303 BottomLeft => (left, bottom, TextAnchor::Start),
304 BottomRight => (right, bottom, TextAnchor::End),
305 };
306 backend.draw_text(
307 tag,
308 (x, y),
309 &TextStyle {
310 color: theme.title.color,
311 size: theme.title.size,
312 anchor,
313 angle: 0.0,
314 family,
315 face: theme.title.face,
316 },
317 )?;
318 }
319 Ok(())
320 }
321
322 fn render_faceted(built: &BuiltPlot, backend: &mut dyn DrawBackend) -> Result<(), RenderError> {
323 let theme = &built.theme;
324 let plot_area = backend.plot_area();
325 let total_area = backend.total_area();
326
327 if theme.plot_background.visible {
329 if let Some(fill) = theme.plot_background.fill {
330 backend.draw_rect(
331 (total_area.x, total_area.y),
332 (
333 total_area.x + total_area.width,
334 total_area.y + total_area.height,
335 ),
336 &RectStyle {
337 fill: Some(fill),
338 stroke: None,
339 stroke_width: 0.0,
340 alpha: 1.0,
341 clip: false,
342 },
343 )?;
344 }
345 }
346
347 let ncol = match &built.facet {
349 Facet::Wrap { ncol, .. } => {
350 ncol.unwrap_or_else(|| (built.panels.len() as f64).sqrt().ceil() as usize)
351 }
352 Facet::Grid { .. } => built.panels.iter().map(|p| p.col).max().unwrap_or(0) + 1,
353 Facet::None => 1,
354 };
355 let nrow = built.panels.len().div_ceil(ncol);
356
357 let strip_height = theme.strip_text.size + 8.0;
358 let gap_x = theme.get_panel_spacing_x();
359 let gap_y = theme.get_panel_spacing_y();
360
361 let space = match &built.facet {
364 Facet::Grid { space, .. } => space.clone(),
365 _ => crate::facet::FacetSpace::Fixed,
366 };
367 let extent = |pi: usize, col: &str| -> Option<f64> {
368 let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
369 for df in &built.panels_data[pi] {
370 if let Some(c) = df.column(col) {
371 for v in c {
372 if let Some(f) = v.as_f64() {
373 lo = lo.min(f);
374 hi = hi.max(f);
375 }
376 }
377 }
378 }
379 (lo <= hi).then_some(hi - lo)
380 };
381 let avail_w = plot_area.width - gap_x * (ncol as f64 - 1.0);
382 let col_widths: Vec<f64> = if space.free_x() && ncol > 0 {
383 let mut ranges = vec![0.0f64; ncol];
384 for (pi, panel) in built.panels.iter().enumerate() {
385 if let Some(r) = extent(pi, "x") {
386 ranges[panel.col] = ranges[panel.col].max(r);
387 }
388 }
389 let total: f64 = ranges.iter().sum();
390 if total > 0.0 {
391 ranges.iter().map(|r| avail_w * (r / total)).collect()
392 } else {
393 vec![avail_w / ncol as f64; ncol]
394 }
395 } else {
396 vec![avail_w / ncol.max(1) as f64; ncol.max(1)]
397 };
398 let mut col_x = vec![plot_area.x; ncol.max(1)];
399 {
400 let mut acc = plot_area.x;
401 for c in 0..ncol {
402 col_x[c] = acc;
403 acc += col_widths[c] + gap_x;
404 }
405 }
406
407 let avail_h = plot_area.height - gap_y * (nrow as f64 - 1.0) - strip_height * nrow as f64;
408 let row_heights: Vec<f64> = if space.free_y() && nrow > 0 {
409 let mut ranges = vec![0.0f64; nrow];
410 for (pi, panel) in built.panels.iter().enumerate() {
411 if let Some(r) = extent(pi, "y") {
412 ranges[panel.row] = ranges[panel.row].max(r);
413 }
414 }
415 let total: f64 = ranges.iter().sum();
416 if total > 0.0 {
417 ranges.iter().map(|r| avail_h * (r / total)).collect()
418 } else {
419 vec![avail_h / nrow as f64; nrow]
420 }
421 } else {
422 vec![avail_h / nrow.max(1) as f64; nrow.max(1)]
423 };
424 let mut row_y = vec![plot_area.y; nrow.max(1)];
425 {
426 let mut acc = plot_area.y;
427 for r in 0..nrow {
428 row_y[r] = acc;
429 acc += row_heights[r] + strip_height + gap_y;
430 }
431 }
432
433 for (pi, panel) in built.panels.iter().enumerate() {
434 let panel_width = col_widths[panel.col];
435 let panel_height = row_heights[panel.row];
436 let px = col_x[panel.col];
437 let py = row_y[panel.row];
438
439 let panel_rect = crate::render::Rect {
440 x: px,
441 y: py + strip_height,
442 width: panel_width,
443 height: panel_height,
444 };
445
446 if theme.strip_background.visible {
448 backend.draw_rect(
449 (px, py),
450 (px + panel_width, py + strip_height),
451 &RectStyle {
452 fill: theme.strip_background.fill,
453 stroke: theme.strip_background.color,
454 stroke_width: theme.strip_background.width,
455 alpha: 1.0,
456 clip: false,
457 },
458 )?;
459 }
460
461 if theme.strip_text.visible {
463 let label = panel.col_label.as_deref().unwrap_or(&panel.label);
464 let family = if theme.strip_text.family.is_empty() {
465 None
466 } else {
467 Some(theme.strip_text.family.clone())
468 };
469 backend.draw_text(
470 label,
471 (px + panel_width / 2.0, py + strip_height / 2.0),
472 &TextStyle {
473 color: theme.strip_text.color,
474 size: theme.strip_text.size,
475 anchor: TextAnchor::Middle,
476 angle: 0.0,
477 family,
478 face: theme.strip_text.face,
479 },
480 )?;
481 }
482
483 if theme.panel_background.visible {
485 if let Some(fill) = theme.panel_background.fill {
486 backend.draw_rect(
487 (panel_rect.x, panel_rect.y),
488 (
489 panel_rect.x + panel_rect.width,
490 panel_rect.y + panel_rect.height,
491 ),
492 &RectStyle {
493 fill: Some(fill),
494 stroke: theme.panel_background.color,
495 stroke_width: theme.panel_background.width,
496 alpha: 1.0,
497 clip: false,
498 },
499 )?;
500 }
501 }
502
503 if theme.panel_border.visible {
505 let style = LineStyle {
506 color: theme.panel_border.color,
507 width: theme.panel_border.width,
508 alpha: 1.0,
509 linetype: Linetype::Solid,
510 };
511 let x0 = panel_rect.x;
512 let y0 = panel_rect.y;
513 let x1 = panel_rect.x + panel_rect.width;
514 let y1 = panel_rect.y + panel_rect.height;
515 backend.draw_line(&[(x0, y0), (x1, y0)], &style)?;
516 backend.draw_line(&[(x1, y0), (x1, y1)], &style)?;
517 backend.draw_line(&[(x1, y1), (x0, y1)], &style)?;
518 backend.draw_line(&[(x0, y1), (x0, y0)], &style)?;
519 }
520
521 let panel_scale_set = if pi < built.panel_scales.len() {
523 &built.panel_scales[pi]
524 } else {
525 &built.scales
526 };
527
528 let x_scale = panel_scale_set.get(&Aesthetic::X);
530 let y_scale = panel_scale_set.get(&Aesthetic::Y);
531
532 if let (Some(xs), Some(ys)) = (x_scale, y_scale) {
533 if built.coord.gridlines() {
534 axis::draw_gridlines(
535 xs,
536 ys,
537 built.coord.as_ref(),
538 theme,
539 &panel_rect,
540 backend,
541 )?;
542 }
543
544 if panel.row == nrow - 1 || pi + ncol >= built.panels.len() {
546 axis::draw_x_axis(xs, built.coord.as_ref(), theme, &panel_rect, backend)?;
547 }
548
549 if panel.col == 0 {
551 axis::draw_y_axis(ys, built.coord.as_ref(), theme, &panel_rect, backend)?;
552 }
553 }
554
555 if pi < built.panels_data.len() {
557 for (li, layer_data) in built.panels_data[pi].iter().enumerate() {
558 if li < built.layers.len() && layer_data.nrows() > 0 {
559 let mut panel_backend = PanelBackendAdapter {
560 inner: backend,
561 panel_rect: panel_rect.clone(),
562 };
563 built.layers[li].geom.draw(
564 layer_data,
565 built.coord.as_ref(),
566 panel_scale_set,
567 theme,
568 &mut panel_backend,
569 )?;
570 }
571 }
572 }
573 }
574
575 let title_ref = Self::title_ref_rect(theme, &plot_area, &total_area);
577 if let Some(ref title) = built.labels.title {
578 let (tx, anchor) = Self::hjust_pos(&theme.title, &title_ref);
579 let title_y = plot_area.y - theme.title.size * 0.9;
580 let family = if theme.title.family.is_empty() {
581 None
582 } else {
583 Some(theme.title.family.clone())
584 };
585 backend.draw_text(
586 title,
587 (tx, title_y.max(theme.title.size)),
588 &TextStyle {
589 color: theme.title.color,
590 size: theme.title.size,
591 anchor,
592 angle: 0.0,
593 family,
594 face: theme.title.face,
595 },
596 )?;
597 }
598
599 if let Some(ref subtitle) = built.labels.subtitle {
601 let (sx, anchor) = Self::hjust_pos(&theme.subtitle, &title_ref);
602 let subtitle_y = plot_area.y - 2.0;
603 let family = if theme.subtitle.family.is_empty() {
604 None
605 } else {
606 Some(theme.subtitle.family.clone())
607 };
608 backend.draw_text(
609 subtitle,
610 (sx, subtitle_y.max(theme.title.size + theme.subtitle.size)),
611 &TextStyle {
612 color: theme.subtitle.color,
613 size: theme.subtitle.size,
614 anchor,
615 angle: 0.0,
616 family,
617 face: theme.subtitle.face,
618 },
619 )?;
620 }
621
622 if let Some(ref caption) = built.labels.caption {
624 let (cx, anchor) = Self::hjust_pos(&theme.caption, &title_ref);
625 let caption_y = total_area.y + total_area.height - theme.caption.size * 0.5;
626 let family = if theme.caption.family.is_empty() {
627 None
628 } else {
629 Some(theme.caption.family.clone())
630 };
631 backend.draw_text(
632 caption,
633 (cx, caption_y),
634 &TextStyle {
635 color: theme.caption.color,
636 size: theme.caption.size,
637 anchor,
638 angle: 0.0,
639 family,
640 face: theme.caption.face,
641 },
642 )?;
643 }
644
645 Self::draw_annotations(
647 &built.annotations,
648 &built.scales,
649 built.coord.as_ref(),
650 &plot_area,
651 backend,
652 )?;
653
654 legend::draw_legend(
656 &built.scales,
657 theme,
658 &plot_area,
659 backend,
660 &built.guide_legend,
661 &built.suppressed_aes,
662 )?;
663
664 Self::draw_tag(&built.labels, theme, &total_area, backend)?;
666
667 Ok(())
668 }
669
670 fn draw_annotations(
671 annotations: &[Annotation],
672 scales: &crate::scale::ScaleSet,
673 coord: &dyn crate::coord::Coord,
674 plot_area: &crate::render::Rect,
675 backend: &mut dyn DrawBackend,
676 ) -> Result<(), RenderError> {
677 use crate::data::Value;
678
679 let x_scale = scales.get(&Aesthetic::X);
680 let y_scale = scales.get(&Aesthetic::Y);
681
682 for ann in annotations {
683 match ann {
684 Annotation::Text {
685 label,
686 x,
687 y,
688 size,
689 color,
690 } => {
691 let nx = x_scale.map(|s| s.map(&Value::Float(*x))).unwrap_or(0.0);
692 let ny = y_scale.map(|s| s.map(&Value::Float(*y))).unwrap_or(0.0);
693 let pos = coord.transform((nx, ny), plot_area);
694 backend.draw_text(
695 label,
696 pos,
697 &TextStyle {
698 color: *color,
699 size: *size,
700 anchor: TextAnchor::Middle,
701 angle: 0.0,
702 family: None,
703 face: crate::render::backend::FontFace::Plain,
704 },
705 )?;
706 }
707 Annotation::Rect {
708 xmin,
709 xmax,
710 ymin,
711 ymax,
712 fill,
713 alpha,
714 } => {
715 let nx0 = x_scale.map(|s| s.map(&Value::Float(*xmin))).unwrap_or(0.0);
716 let nx1 = x_scale.map(|s| s.map(&Value::Float(*xmax))).unwrap_or(1.0);
717 let ny0 = y_scale.map(|s| s.map(&Value::Float(*ymin))).unwrap_or(0.0);
718 let ny1 = y_scale.map(|s| s.map(&Value::Float(*ymax))).unwrap_or(1.0);
719 let tl = coord.transform((nx0, ny1), plot_area);
720 let br = coord.transform((nx1, ny0), plot_area);
721 backend.draw_rect(
722 tl,
723 br,
724 &RectStyle {
725 fill: Some(*fill),
726 stroke: None,
727 stroke_width: 0.0,
728 alpha: *alpha,
729 clip: false,
730 },
731 )?;
732 }
733 Annotation::Segment {
734 x,
735 y,
736 xend,
737 yend,
738 color,
739 width,
740 } => {
741 let nx0 = x_scale.map(|s| s.map(&Value::Float(*x))).unwrap_or(0.0);
742 let ny0 = y_scale.map(|s| s.map(&Value::Float(*y))).unwrap_or(0.0);
743 let nx1 = x_scale.map(|s| s.map(&Value::Float(*xend))).unwrap_or(1.0);
744 let ny1 = y_scale.map(|s| s.map(&Value::Float(*yend))).unwrap_or(1.0);
745 let p0 = coord.transform((nx0, ny0), plot_area);
746 let p1 = coord.transform((nx1, ny1), plot_area);
747 backend.draw_line(
748 &[p0, p1],
749 &LineStyle {
750 color: *color,
751 alpha: 1.0,
752 width: *width,
753 linetype: Linetype::Solid,
754 },
755 )?;
756 }
757 }
758 }
759 Ok(())
760 }
761}
762
763struct PanelBackendAdapter<'a> {
765 inner: &'a mut dyn DrawBackend,
766 panel_rect: crate::render::Rect,
767}
768
769impl<'a> DrawBackend for PanelBackendAdapter<'a> {
770 fn draw_circle(
771 &mut self,
772 center: (f64, f64),
773 radius: f64,
774 style: &crate::render::backend::PointStyle,
775 ) -> Result<(), RenderError> {
776 self.inner.draw_circle(center, radius, style)
777 }
778 fn draw_line(
779 &mut self,
780 points: &[(f64, f64)],
781 style: &crate::render::backend::LineStyle,
782 ) -> Result<(), RenderError> {
783 self.inner.draw_line(points, style)
784 }
785 fn draw_rect(
786 &mut self,
787 top_left: (f64, f64),
788 bottom_right: (f64, f64),
789 style: &RectStyle,
790 ) -> Result<(), RenderError> {
791 self.inner.draw_rect(top_left, bottom_right, style)
792 }
793 fn draw_text(
794 &mut self,
795 text: &str,
796 pos: (f64, f64),
797 style: &TextStyle,
798 ) -> Result<(), RenderError> {
799 self.inner.draw_text(text, pos, style)
800 }
801 fn draw_polygon(
802 &mut self,
803 points: &[(f64, f64)],
804 style: &RectStyle,
805 ) -> Result<(), RenderError> {
806 self.inner.draw_polygon(points, style)
807 }
808 fn draw_shape(
809 &mut self,
810 center: (f64, f64),
811 radius: f64,
812 style: &crate::render::backend::PointStyle,
813 ) -> Result<(), RenderError> {
814 self.inner.draw_shape(center, radius, style)
815 }
816 fn plot_area(&self) -> crate::render::Rect {
817 self.panel_rect.clone()
818 }
819 fn set_tooltip(&mut self, tooltip: Option<String>) {
820 self.inner.set_tooltip(tooltip)
821 }
822 fn set_mark_axis(&mut self, key: Option<String>) {
823 self.inner.set_mark_axis(key)
824 }
825 fn set_mark_series(&mut self, series: Option<String>) {
826 self.inner.set_mark_series(series)
827 }
828 fn set_mark_value(&mut self, value: Option<String>) {
829 self.inner.set_mark_value(value)
830 }
831 fn total_area(&self) -> crate::render::Rect {
832 self.inner.total_area()
833 }
834}