1use crate::error::{ChartError, ChartResult};
4use crate::legend::position::LegendPosition;
5use crate::legend::style::{LegendStyle, SymbolStyle};
6use crate::legend::traits::{Legend, LegendEntry};
7use embedded_graphics::{prelude::*, primitives::Rectangle};
8
9#[cfg(feature = "std")]
10use std::vec::Vec;
11
12#[cfg(all(feature = "no_std", not(feature = "std")))]
13extern crate alloc;
14
15#[cfg(all(feature = "no_std", not(feature = "std")))]
16use alloc::vec::Vec;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum LegendOrientation {
21 Vertical,
23 Horizontal,
25}
26
27#[derive(Debug, Clone)]
29pub enum LegendEntryType<C: PixelColor> {
30 Line {
32 color: C,
34 width: u32,
36 pattern: crate::style::LinePattern,
38 marker: Option<MarkerStyle<C>>,
40 },
41 Bar {
43 color: C,
45 border_color: Option<C>,
47 border_width: u32,
49 },
50 Pie {
52 color: C,
54 border_color: Option<C>,
56 border_width: u32,
58 },
59 Custom {
61 color: C,
63 shape: SymbolShape,
65 size: u32,
67 },
68}
69
70#[derive(Debug, Clone)]
72pub struct MarkerStyle<C: PixelColor> {
73 pub shape: MarkerShape,
75 pub color: C,
77 pub size: u32,
79 pub filled: bool,
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum MarkerShape {
86 Circle,
88 Square,
90 Triangle,
92 Diamond,
94 Cross,
96 Plus,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum SymbolShape {
103 Circle,
105 Square,
107 Triangle,
109 Diamond,
111 Star,
113 Cross,
115}
116
117#[derive(Debug, Clone)]
119pub struct StandardLegend<C: PixelColor> {
120 entries: heapless::Vec<StandardLegendEntry<C>, 16>,
122 position: LegendPosition,
124 orientation: LegendOrientation,
126 style: LegendStyle<C>,
128}
129
130#[derive(Debug, Clone)]
132pub struct StandardLegendEntry<C: PixelColor> {
133 label: heapless::String<64>,
135 entry_type: LegendEntryType<C>,
137 visible: bool,
139}
140
141#[derive(Debug, Clone)]
143pub struct CompactLegend<C: PixelColor> {
144 entries: heapless::Vec<CompactLegendEntry<C>, 8>,
146 position: LegendPosition,
148 orientation: LegendOrientation,
150 style: LegendStyle<C>,
152}
153
154impl<C: PixelColor> CompactLegend<C>
155where
156 C: From<embedded_graphics::pixelcolor::Rgb565>,
157{
158 pub fn new(position: LegendPosition) -> Self {
160 Self {
161 entries: heapless::Vec::new(),
162 position,
163 orientation: LegendOrientation::Vertical,
164 style: LegendStyle::compact(),
165 }
166 }
167
168 pub fn set_orientation(&mut self, orientation: LegendOrientation) {
170 self.orientation = orientation;
171 }
172
173 pub fn set_style(&mut self, style: LegendStyle<C>) {
175 self.style = style;
176 }
177
178 pub fn add_entry(&mut self, entry: CompactLegendEntry<C>) -> ChartResult<()> {
180 self.entries
181 .push(entry)
182 .map_err(|_| ChartError::ConfigurationError)?;
183 Ok(())
184 }
185}
186
187impl<C: PixelColor> crate::legend::traits::Legend<C> for CompactLegend<C>
188where
189 C: From<embedded_graphics::pixelcolor::Rgb565>,
190{
191 type Entry = CompactLegendEntry<C>;
192
193 fn position(&self) -> LegendPosition {
194 self.position
195 }
196
197 fn orientation(&self) -> LegendOrientation {
198 self.orientation
199 }
200
201 fn entries(&self) -> &[Self::Entry] {
202 &self.entries
203 }
204
205 fn entries_mut(&mut self) -> &mut [Self::Entry] {
206 &mut self.entries
207 }
208
209 fn add_entry(&mut self, entry: Self::Entry) -> ChartResult<()> {
210 self.entries
211 .push(entry)
212 .map_err(|_| ChartError::ConfigurationError)?;
213 Ok(())
214 }
215
216 fn remove_entry(&mut self, index: usize) -> ChartResult<()> {
217 if index < self.entries.len() {
218 self.entries.swap_remove(index);
219 Ok(())
220 } else {
221 Err(ChartError::InvalidConfiguration)
222 }
223 }
224
225 fn clear_entries(&mut self) {
226 self.entries.clear();
227 }
228
229 fn set_position(&mut self, position: LegendPosition) {
230 self.position = position;
231 }
232
233 fn set_orientation(&mut self, orientation: LegendOrientation) {
234 self.orientation = orientation;
235 }
236
237 fn calculate_size(&self) -> embedded_graphics::prelude::Size {
238 let entry_count = self.entries.len() as u32;
240 match self.orientation {
241 LegendOrientation::Vertical => {
242 embedded_graphics::prelude::Size::new(80, entry_count * 16 + 8)
243 }
244 LegendOrientation::Horizontal => {
245 embedded_graphics::prelude::Size::new(entry_count * 60 + 8, 20)
246 }
247 }
248 }
249}
250
251#[derive(Debug, Clone)]
253pub struct CompactLegendEntry<C: PixelColor> {
254 pub label: heapless::String<16>,
256 pub entry_type: LegendEntryType<C>,
258 pub visible: bool,
260}
261
262impl<C: PixelColor> CompactLegendEntry<C> {
263 pub fn new(label: &str, entry_type: LegendEntryType<C>) -> ChartResult<Self> {
265 let mut label_string = heapless::String::new();
266 label_string
267 .push_str(label)
268 .map_err(|_| ChartError::ConfigurationError)?;
269 Ok(Self {
270 label: label_string,
271 entry_type,
272 visible: true,
273 })
274 }
275}
276
277#[derive(Debug, Clone)]
279pub struct CustomLegend<C: PixelColor> {
280 entries: heapless::Vec<CustomLegendEntry<C>, 12>,
282 position: LegendPosition,
284 orientation: LegendOrientation,
286 style: LegendStyle<C>,
288 layout_params: CustomLayoutParams,
290}
291
292impl<C: PixelColor> CustomLegend<C>
293where
294 C: From<embedded_graphics::pixelcolor::Rgb565>,
295{
296 pub fn new(position: LegendPosition) -> Self {
298 Self {
299 entries: heapless::Vec::new(),
300 position,
301 orientation: LegendOrientation::Vertical,
302 style: LegendStyle::new(),
303 layout_params: CustomLayoutParams::default(),
304 }
305 }
306
307 pub fn set_orientation(&mut self, orientation: LegendOrientation) {
309 self.orientation = orientation;
310 }
311
312 pub fn set_style(&mut self, style: LegendStyle<C>) {
314 self.style = style;
315 }
316
317 pub fn set_layout_params(&mut self, params: CustomLayoutParams) {
319 self.layout_params = params;
320 }
321
322 pub fn add_entry(&mut self, entry: CustomLegendEntry<C>) -> ChartResult<()> {
324 self.entries
325 .push(entry)
326 .map_err(|_| ChartError::ConfigurationError)?;
327 Ok(())
328 }
329}
330
331impl<C: PixelColor> crate::legend::traits::Legend<C> for CustomLegend<C>
332where
333 C: From<embedded_graphics::pixelcolor::Rgb565>,
334{
335 type Entry = CustomLegendEntry<C>;
336
337 fn position(&self) -> LegendPosition {
338 self.position
339 }
340
341 fn orientation(&self) -> LegendOrientation {
342 self.orientation
343 }
344
345 fn entries(&self) -> &[Self::Entry] {
346 &self.entries
347 }
348
349 fn entries_mut(&mut self) -> &mut [Self::Entry] {
350 &mut self.entries
351 }
352
353 fn add_entry(&mut self, entry: Self::Entry) -> ChartResult<()> {
354 self.entries
355 .push(entry)
356 .map_err(|_| ChartError::ConfigurationError)?;
357 Ok(())
358 }
359
360 fn remove_entry(&mut self, index: usize) -> ChartResult<()> {
361 if index < self.entries.len() {
362 self.entries.swap_remove(index);
363 Ok(())
364 } else {
365 Err(ChartError::InvalidConfiguration)
366 }
367 }
368
369 fn clear_entries(&mut self) {
370 self.entries.clear();
371 }
372
373 fn set_position(&mut self, position: LegendPosition) {
374 self.position = position;
375 }
376
377 fn set_orientation(&mut self, orientation: LegendOrientation) {
378 self.orientation = orientation;
379 }
380
381 fn calculate_size(&self) -> embedded_graphics::prelude::Size {
382 let entry_count = self.entries.len() as u32;
384 let symbol_size = self.layout_params.symbol_size;
385 let entry_spacing = self.layout_params.entry_spacing;
386
387 match self.orientation {
388 LegendOrientation::Vertical => {
389 let width = symbol_size + 100; let height = entry_count * (symbol_size + entry_spacing) + 16;
391 embedded_graphics::prelude::Size::new(width, height)
392 }
393 LegendOrientation::Horizontal => {
394 let width = entry_count * (symbol_size + 80 + entry_spacing) + 16;
395 let height = symbol_size + 16;
396 embedded_graphics::prelude::Size::new(width, height)
397 }
398 }
399 }
400}
401
402#[derive(Debug, Clone)]
404pub struct CustomLegendEntry<C: PixelColor> {
405 label: heapless::String<32>,
407 entry_type: LegendEntryType<C>,
409 visible: bool,
411 #[allow(dead_code)]
413 offset: Point,
414 size_override: Option<Size>,
416}
417
418impl<C: PixelColor> CustomLegendEntry<C> {
419 pub fn new(label: &str, entry_type: LegendEntryType<C>) -> ChartResult<Self> {
421 let mut label_string = heapless::String::new();
422 label_string
423 .push_str(label)
424 .map_err(|_| ChartError::ConfigurationError)?;
425 Ok(Self {
426 label: label_string,
427 entry_type,
428 visible: true,
429 offset: embedded_graphics::prelude::Point::zero(),
430 size_override: None,
431 })
432 }
433}
434
435#[derive(Debug, Clone)]
437pub struct CustomLayoutParams {
438 pub entry_spacing: u32,
440 pub symbol_size: u32,
442 pub text_offset: Point,
444 pub auto_layout: bool,
446}
447
448impl<C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565>> StandardLegend<C> {
450 pub fn new(position: LegendPosition) -> Self {
452 Self {
453 entries: heapless::Vec::new(),
454 position,
455 orientation: LegendOrientation::Vertical,
456 style: LegendStyle::default(),
457 }
458 }
459
460 pub fn set_style(&mut self, style: LegendStyle<C>) {
462 self.style = style;
463 }
464
465 pub fn style(&self) -> &LegendStyle<C> {
467 &self.style
468 }
469}
470
471impl<C: PixelColor> Legend<C> for StandardLegend<C> {
472 type Entry = StandardLegendEntry<C>;
473
474 fn entries(&self) -> &[Self::Entry] {
475 &self.entries
476 }
477
478 fn entries_mut(&mut self) -> &mut [Self::Entry] {
479 &mut self.entries
480 }
481
482 fn add_entry(&mut self, entry: Self::Entry) -> ChartResult<()> {
483 self.entries
484 .push(entry)
485 .map_err(|_| crate::error::ChartError::ConfigurationError)
486 }
487
488 fn remove_entry(&mut self, index: usize) -> ChartResult<()> {
489 if index < self.entries.len() {
490 self.entries.remove(index);
491 Ok(())
492 } else {
493 Err(crate::error::ChartError::ConfigurationError)
494 }
495 }
496
497 fn clear_entries(&mut self) {
498 self.entries.clear();
499 }
500
501 fn position(&self) -> LegendPosition {
502 self.position
503 }
504
505 fn set_position(&mut self, position: LegendPosition) {
506 self.position = position;
507 }
508
509 fn orientation(&self) -> LegendOrientation {
510 self.orientation
511 }
512
513 fn set_orientation(&mut self, orientation: LegendOrientation) {
514 self.orientation = orientation;
515 }
516
517 fn calculate_size(&self) -> Size {
518 if self.entries.is_empty() {
519 return Size::zero();
520 }
521
522 let visible_entries: Vec<_> = self.entries.iter().filter(|e| e.visible).collect();
523 if visible_entries.is_empty() {
524 return Size::zero();
525 }
526
527 match self.orientation {
528 LegendOrientation::Vertical => {
529 let max_width = visible_entries
530 .iter()
531 .map(|e| e.calculate_size(&self.style).width)
532 .max()
533 .unwrap_or(0);
534 let total_height = visible_entries
535 .iter()
536 .map(|e| e.calculate_size(&self.style).height)
537 .sum::<u32>()
538 + (visible_entries.len().saturating_sub(1) as u32
539 * self.style.spacing.entry_spacing);
540 Size::new(max_width, total_height)
541 }
542 LegendOrientation::Horizontal => {
543 let total_width = visible_entries
544 .iter()
545 .map(|e| e.calculate_size(&self.style).width)
546 .sum::<u32>()
547 + (visible_entries.len().saturating_sub(1) as u32
548 * self.style.spacing.entry_spacing);
549 let max_height = visible_entries
550 .iter()
551 .map(|e| e.calculate_size(&self.style).height)
552 .max()
553 .unwrap_or(0);
554 Size::new(total_width, max_height)
555 }
556 }
557 }
558}
559
560impl<C: PixelColor> StandardLegendEntry<C> {
562 pub fn new(label: &str, entry_type: LegendEntryType<C>) -> ChartResult<Self> {
564 let label_string = heapless::String::try_from(label)
565 .map_err(|_| crate::error::ChartError::ConfigurationError)?;
566
567 Ok(Self {
568 label: label_string,
569 entry_type,
570 visible: true,
571 })
572 }
573}
574
575impl<C: PixelColor> LegendEntry<C> for StandardLegendEntry<C> {
576 fn label(&self) -> &str {
577 &self.label
578 }
579
580 fn set_label(&mut self, label: &str) -> ChartResult<()> {
581 self.label = heapless::String::try_from(label)
582 .map_err(|_| crate::error::ChartError::ConfigurationError)?;
583 Ok(())
584 }
585
586 fn entry_type(&self) -> &LegendEntryType<C> {
587 &self.entry_type
588 }
589
590 fn set_entry_type(&mut self, entry_type: LegendEntryType<C>) {
591 self.entry_type = entry_type;
592 }
593
594 fn is_visible(&self) -> bool {
595 self.visible
596 }
597
598 fn set_visible(&mut self, visible: bool) {
599 self.visible = visible;
600 }
601
602 fn calculate_size(&self, style: &LegendStyle<C>) -> Size {
603 let text_width = self.label.len() as u32 * style.text.char_width;
604 let total_width = style.spacing.symbol_width + style.spacing.symbol_text_gap + text_width;
605 Size::new(total_width, style.text.line_height)
606 }
607
608 fn render_symbol<D>(
609 &self,
610 bounds: Rectangle,
611 _style: &SymbolStyle<C>,
612 target: &mut D,
613 ) -> ChartResult<()>
614 where
615 D: DrawTarget<Color = C>,
616 {
617 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle as EgRectangle};
618
619 match &self.entry_type {
620 LegendEntryType::Line { color, .. } => {
621 let line_y = bounds.top_left.y + bounds.size.height as i32 / 2;
623 let line_start = Point::new(bounds.top_left.x + 2, line_y);
624 let line_end = Point::new(bounds.top_left.x + bounds.size.width as i32 - 2, line_y);
625
626 use embedded_graphics::primitives::Line;
627 Line::new(line_start, line_end)
628 .into_styled(PrimitiveStyle::with_stroke(*color, 1))
629 .draw(target)
630 .map_err(|_| crate::error::ChartError::RenderingError)?;
631 }
632 LegendEntryType::Bar { color, .. } | LegendEntryType::Pie { color, .. } => {
633 let rect_size = Size::new(bounds.size.width.min(16), bounds.size.height.min(12));
635 let rect_pos = Point::new(
636 bounds.top_left.x + (bounds.size.width as i32 - rect_size.width as i32) / 2,
637 bounds.top_left.y + (bounds.size.height as i32 - rect_size.height as i32) / 2,
638 );
639
640 EgRectangle::new(rect_pos, rect_size)
641 .into_styled(PrimitiveStyle::with_fill(*color))
642 .draw(target)
643 .map_err(|_| crate::error::ChartError::RenderingError)?;
644 }
645 LegendEntryType::Custom { color, shape, size } => {
646 let symbol_size = (*size).min(bounds.size.width).min(bounds.size.height);
647 let center = Point::new(
648 bounds.top_left.x + bounds.size.width as i32 / 2,
649 bounds.top_left.y + bounds.size.height as i32 / 2,
650 );
651
652 match shape {
653 SymbolShape::Circle => {
654 Circle::with_center(center, symbol_size)
655 .into_styled(PrimitiveStyle::with_fill(*color))
656 .draw(target)
657 .map_err(|_| crate::error::ChartError::RenderingError)?;
658 }
659 SymbolShape::Square => {
660 let rect_size = Size::new(symbol_size, symbol_size);
661 let rect_pos = Point::new(
662 center.x - symbol_size as i32 / 2,
663 center.y - symbol_size as i32 / 2,
664 );
665 EgRectangle::new(rect_pos, rect_size)
666 .into_styled(PrimitiveStyle::with_fill(*color))
667 .draw(target)
668 .map_err(|_| crate::error::ChartError::RenderingError)?;
669 }
670 _ => {
671 Circle::with_center(center, symbol_size)
673 .into_styled(PrimitiveStyle::with_fill(*color))
674 .draw(target)
675 .map_err(|_| crate::error::ChartError::RenderingError)?;
676 }
677 }
678 }
679 }
680
681 Ok(())
682 }
683}
684
685impl<C: PixelColor> LegendEntry<C> for CompactLegendEntry<C> {
686 fn label(&self) -> &str {
687 &self.label
688 }
689
690 fn set_label(&mut self, label: &str) -> ChartResult<()> {
691 self.label = heapless::String::try_from(label)
692 .map_err(|_| crate::error::ChartError::ConfigurationError)?;
693 Ok(())
694 }
695
696 fn entry_type(&self) -> &LegendEntryType<C> {
697 &self.entry_type
698 }
699
700 fn set_entry_type(&mut self, entry_type: LegendEntryType<C>) {
701 self.entry_type = entry_type;
702 }
703
704 fn is_visible(&self) -> bool {
705 self.visible
706 }
707
708 fn set_visible(&mut self, visible: bool) {
709 self.visible = visible;
710 }
711
712 fn calculate_size(&self, style: &LegendStyle<C>) -> Size {
713 let text_width = self.label.len() as u32 * style.text.char_width;
714 let total_width = style.spacing.symbol_width + style.spacing.symbol_text_gap + text_width;
715 Size::new(total_width, style.text.line_height)
716 }
717
718 fn render_symbol<D>(
719 &self,
720 bounds: Rectangle,
721 _style: &SymbolStyle<C>,
722 target: &mut D,
723 ) -> ChartResult<()>
724 where
725 D: DrawTarget<Color = C>,
726 {
727 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle as EgRectangle};
728
729 match &self.entry_type {
730 LegendEntryType::Line { color, .. } => {
731 let line_y = bounds.top_left.y + bounds.size.height as i32 / 2;
733 let line_start = Point::new(bounds.top_left.x + 2, line_y);
734 let line_end = Point::new(bounds.top_left.x + bounds.size.width as i32 - 2, line_y);
735
736 use embedded_graphics::primitives::Line;
737 Line::new(line_start, line_end)
738 .into_styled(PrimitiveStyle::with_stroke(*color, 1))
739 .draw(target)
740 .map_err(|_| crate::error::ChartError::RenderingError)?;
741 }
742 LegendEntryType::Bar { color, .. } | LegendEntryType::Pie { color, .. } => {
743 let rect_size = Size::new(bounds.size.width.min(16), bounds.size.height.min(12));
745 let rect_pos = Point::new(
746 bounds.top_left.x + (bounds.size.width as i32 - rect_size.width as i32) / 2,
747 bounds.top_left.y + (bounds.size.height as i32 - rect_size.height as i32) / 2,
748 );
749
750 EgRectangle::new(rect_pos, rect_size)
751 .into_styled(PrimitiveStyle::with_fill(*color))
752 .draw(target)
753 .map_err(|_| crate::error::ChartError::RenderingError)?;
754 }
755 LegendEntryType::Custom { color, shape, size } => {
756 let symbol_size = (*size).min(bounds.size.width).min(bounds.size.height);
757 let center = Point::new(
758 bounds.top_left.x + bounds.size.width as i32 / 2,
759 bounds.top_left.y + bounds.size.height as i32 / 2,
760 );
761
762 match shape {
763 SymbolShape::Circle => {
764 Circle::with_center(center, symbol_size)
765 .into_styled(PrimitiveStyle::with_fill(*color))
766 .draw(target)
767 .map_err(|_| crate::error::ChartError::RenderingError)?;
768 }
769 SymbolShape::Square => {
770 let rect_size = Size::new(symbol_size, symbol_size);
771 let rect_pos = Point::new(
772 center.x - symbol_size as i32 / 2,
773 center.y - symbol_size as i32 / 2,
774 );
775 EgRectangle::new(rect_pos, rect_size)
776 .into_styled(PrimitiveStyle::with_fill(*color))
777 .draw(target)
778 .map_err(|_| crate::error::ChartError::RenderingError)?;
779 }
780 _ => {
781 Circle::with_center(center, symbol_size)
783 .into_styled(PrimitiveStyle::with_fill(*color))
784 .draw(target)
785 .map_err(|_| crate::error::ChartError::RenderingError)?;
786 }
787 }
788 }
789 }
790
791 Ok(())
792 }
793}
794
795impl Default for LegendOrientation {
799 fn default() -> Self {
800 Self::Vertical
801 }
802}
803
804impl Default for CustomLayoutParams {
805 fn default() -> Self {
806 Self {
807 entry_spacing: 8,
808 symbol_size: 16,
809 text_offset: Point::new(20, 0),
810 auto_layout: true,
811 }
812 }
813}
814
815impl<C: PixelColor> LegendEntry<C> for CustomLegendEntry<C> {
817 fn label(&self) -> &str {
818 &self.label
819 }
820
821 fn set_label(&mut self, label: &str) -> ChartResult<()> {
822 self.label =
823 heapless::String::try_from(label).map_err(|_| ChartError::ConfigurationError)?;
824 Ok(())
825 }
826
827 fn entry_type(&self) -> &LegendEntryType<C> {
828 &self.entry_type
829 }
830
831 fn set_entry_type(&mut self, entry_type: LegendEntryType<C>) {
832 self.entry_type = entry_type;
833 }
834
835 fn is_visible(&self) -> bool {
836 self.visible
837 }
838
839 fn set_visible(&mut self, visible: bool) {
840 self.visible = visible;
841 }
842
843 fn calculate_size(&self, style: &LegendStyle<C>) -> Size {
844 let text_width = self.label.len() as u32 * style.text.char_width;
845 let total_width = style.spacing.symbol_width + style.spacing.symbol_text_gap + text_width;
846
847 if let Some(size_override) = self.size_override {
849 size_override
850 } else {
851 Size::new(total_width, style.text.line_height)
852 }
853 }
854
855 fn render_symbol<D>(
856 &self,
857 bounds: Rectangle,
858 _style: &SymbolStyle<C>,
859 target: &mut D,
860 ) -> ChartResult<()>
861 where
862 D: DrawTarget<Color = C>,
863 {
864 use embedded_graphics::primitives::{
865 Circle, Line, PrimitiveStyle, Rectangle as EgRectangle,
866 };
867
868 match &self.entry_type {
869 LegendEntryType::Line { color, .. } => {
870 let line_y = bounds.top_left.y + bounds.size.height as i32 / 2;
871 let line_start = Point::new(bounds.top_left.x + 2, line_y);
872 let line_end = Point::new(bounds.top_left.x + bounds.size.width as i32 - 2, line_y);
873
874 Line::new(line_start, line_end)
875 .into_styled(PrimitiveStyle::with_stroke(*color, 1))
876 .draw(target)
877 .map_err(|_| ChartError::RenderingError)?;
878 }
879 LegendEntryType::Bar { color, .. } | LegendEntryType::Pie { color, .. } => {
880 let rect_size = Size::new(bounds.size.width.min(16), bounds.size.height.min(12));
881 let rect_pos = Point::new(
882 bounds.top_left.x + (bounds.size.width as i32 - rect_size.width as i32) / 2,
883 bounds.top_left.y + (bounds.size.height as i32 - rect_size.height as i32) / 2,
884 );
885
886 EgRectangle::new(rect_pos, rect_size)
887 .into_styled(PrimitiveStyle::with_fill(*color))
888 .draw(target)
889 .map_err(|_| ChartError::RenderingError)?;
890 }
891 LegendEntryType::Custom { color, shape, size } => {
892 let symbol_size = (*size).min(bounds.size.width).min(bounds.size.height);
893 let center = Point::new(
894 bounds.top_left.x + bounds.size.width as i32 / 2,
895 bounds.top_left.y + bounds.size.height as i32 / 2,
896 );
897
898 match shape {
899 SymbolShape::Circle => {
900 Circle::with_center(center, symbol_size)
901 .into_styled(PrimitiveStyle::with_fill(*color))
902 .draw(target)
903 .map_err(|_| ChartError::RenderingError)?;
904 }
905 SymbolShape::Square => {
906 let half_size = symbol_size / 2;
907 let rect_pos =
908 Point::new(center.x - half_size as i32, center.y - half_size as i32);
909 EgRectangle::new(rect_pos, Size::new(symbol_size, symbol_size))
910 .into_styled(PrimitiveStyle::with_fill(*color))
911 .draw(target)
912 .map_err(|_| ChartError::RenderingError)?;
913 }
914 _ => {
915 Circle::with_center(center, symbol_size)
917 .into_styled(PrimitiveStyle::with_fill(*color))
918 .draw(target)
919 .map_err(|_| ChartError::RenderingError)?;
920 }
921 }
922 }
923 }
924
925 Ok(())
926 }
927}