1use super::*;
2
3impl Scale {
5 pub fn linear(domain_min: f64, domain_max: f64, range_min: f64, range_max: f64) -> Self {
11 Self {
12 kind: ScaleKind::Linear,
13 domain_min,
14 domain_max,
15 range_min,
16 range_max,
17 categories: Vec::new(),
18 log_base: 10.0,
19 band: 0.0,
20 count: 0,
21 }
22 }
23
24 pub fn log(
30 domain_min: f64,
31 domain_max: f64,
32 range_min: f64,
33 range_max: f64,
34 base: f64,
35 ) -> Self {
36 Self {
37 kind: ScaleKind::Log,
38 domain_min,
39 domain_max,
40 range_min,
41 range_max,
42 categories: Vec::new(),
43 log_base: base,
44 band: 0.0,
45 count: 0,
46 }
47 }
48
49 pub fn category(categories: Vec<String>, range_min: f64, range_max: f64, band: f64) -> Self {
59 let count: usize = categories.len();
60 Self {
61 kind: ScaleKind::Category,
62 domain_min: 0.0,
63 domain_max: count.saturating_sub(1) as f64,
64 range_min,
65 range_max,
66 categories,
67 log_base: 10.0,
68 band,
69 count,
70 }
71 }
72
73 pub fn category_count(count: usize, range_min: f64, range_max: f64, band: f64) -> Self {
84 Self {
85 kind: ScaleKind::Category,
86 domain_min: 0.0,
87 domain_max: count.saturating_sub(1) as f64,
88 range_min,
89 range_max,
90 categories: Vec::new(),
91 log_base: 10.0,
92 band,
93 count,
94 }
95 }
96
97 pub fn map(&self, value: f64) -> f64 {
111 match self.get_kind() {
112 ScaleKind::Category => self.map_ordinal(value),
113 ScaleKind::Log => {
114 let base: f64 = self.get_log_base().max(1.000_001);
115 let low: f64 = self.get_domain_min().max(f64::MIN_POSITIVE).log(base);
116 let high: f64 = self.get_domain_max().max(f64::MIN_POSITIVE).log(base);
117 let position: f64 = if (high - low).abs() < f64::EPSILON {
118 self.get_range_min()
119 } else {
120 let t: f64 = (value.max(f64::MIN_POSITIVE).log(base) - low) / (high - low);
121 self.get_range_min() + t * (self.get_range_max() - self.get_range_min())
122 };
123 position.clamp(
124 self.get_range_min().min(self.get_range_max()),
125 self.get_range_max().max(self.get_range_min()),
126 )
127 }
128 _ => {
129 let span: f64 = self.get_domain_max() - self.get_domain_min();
130 if span.abs() < f64::EPSILON {
131 return (self.get_range_min() + self.get_range_max()) * 0.5;
132 }
133 let t: f64 = (value - self.get_domain_min()) / span;
134 let position: f64 =
135 self.get_range_min() + t * (self.get_range_max() - self.get_range_min());
136 position.clamp(
137 self.get_range_min().min(self.get_range_max()),
138 self.get_range_max().max(self.get_range_min()),
139 )
140 }
141 }
142 }
143
144 fn map_ordinal(&self, value: f64) -> f64 {
154 if self.get_count() <= 1 {
155 return (self.get_range_min() + self.get_range_max()) * 0.5;
156 }
157 let span: f64 = self.get_domain_max() - self.get_domain_min();
158 if span.abs() < f64::EPSILON {
159 return self.get_range_min();
160 }
161 let t: f64 = (value - self.get_domain_min()) / span;
162 let position: f64 =
163 self.get_range_min() + t * (self.get_range_max() - self.get_range_min());
164 position.clamp(
165 self.get_range_min().min(self.get_range_max()),
166 self.get_range_max().max(self.get_range_min()),
167 )
168 }
169
170 pub fn invert(&self, pixel: f64) -> f64 {
180 match self.get_kind() {
181 ScaleKind::Log => {
182 let base: f64 = self.get_log_base().max(1.000_001);
183 let span: f64 = self.get_range_max() - self.get_range_min();
184 if span.abs() < f64::EPSILON {
185 return self.get_domain_min();
186 }
187 let t: f64 = (pixel - self.get_range_min()) / span;
188 let low: f64 = self.get_domain_min().max(f64::MIN_POSITIVE).log(base);
189 let high: f64 = self.get_domain_max().max(f64::MIN_POSITIVE).log(base);
190 (low + t * (high - low)).powf(base)
191 }
192 _ => {
193 let span: f64 = self.get_range_max() - self.get_range_min();
194 if span.abs() < f64::EPSILON {
195 return self.get_domain_min();
196 }
197 let t: f64 = (pixel - self.get_range_min()) / span;
198 self.get_domain_min() + t * (self.get_domain_max() - self.get_domain_min())
199 }
200 }
201 }
202
203 pub fn band_width(&self) -> f64 {
211 if self.get_kind() != ScaleKind::Category {
212 return 0.0;
213 }
214 self.get_band()
215 }
216
217 pub fn is_ordinal(&self) -> bool {
223 self.get_kind() == ScaleKind::Category
224 }
225}
226
227impl AxisLayout {
229 pub fn position_of(&self, value: f64) -> f64 {
239 self.get_scale().map(value)
240 }
241
242 pub fn category_at(&self, pixel: f64) -> Option<usize> {
252 if !self.get_scale().is_ordinal() {
253 return None;
254 }
255 let value: f64 = self.get_scale().invert(pixel);
256 let index: isize = value.round() as isize;
257 let count: usize = if self.get_scale().get_categories().is_empty() {
261 self.get_scale().get_count()
262 } else {
263 self.get_scale().get_categories().len()
264 };
265 if index < 0 || index as usize >= count {
266 return None;
267 }
268 Some(index as usize)
269 }
270}
271
272impl GridLayout {
274 pub fn x_axis(&self, index: usize) -> Option<&AxisLayout> {
285 self.get_x_axes()
286 .get(index)
287 .or_else(|| self.get_x_axes().first())
288 }
289
290 pub fn y_axis(&self, index: usize) -> Option<&AxisLayout> {
301 self.get_y_axes()
302 .get(index)
303 .or_else(|| self.get_y_axes().first())
304 }
305}
306
307impl ChartLayout {
309 pub fn compute(
330 option: &ChartOption,
331 width: f64,
332 height: f64,
333 measurer: &TextMeasurer,
334 ) -> ChartLayout {
335 Self::compute_with_maps(option, width, height, measurer, &MapRegistry::new())
336 }
337
338 pub fn compute_with_maps(
355 option: &ChartOption,
356 width: f64,
357 height: f64,
358 measurer: &TextMeasurer,
359 maps: &MapRegistry,
360 ) -> ChartLayout {
361 let mut layout: ChartLayout = ChartLayout {
362 width,
363 height,
364 grids: Vec::new(),
365 series: Vec::new(),
366 geo: Vec::new(),
367 title_rect: None,
368 legend_rect: None,
369 plot_rect: BBox::from_min_size(0.0, 0.0, width, height),
370 };
371
372 let title_space: f64 = layout.measure_title(option, measurer);
374 let legend_space: f64 = layout.measure_legend(option, measurer);
375
376 layout.plot_rect = BBox::from_min_size(
377 0.0,
378 title_space,
379 width,
380 (height - title_space - legend_space).max(0.0),
381 );
382
383 layout.grids = layout.measure_grids(option, &layout.plot_rect, measurer);
384 layout.series = layout.measure_series(option, &layout.grids);
385 layout.geo = layout.measure_geo(option, maps);
386 layout
387 }
388
389 fn measure_title(&mut self, option: &ChartOption, measurer: &TextMeasurer) -> f64 {
400 let Some(title) = &option.title else {
401 return 0.0;
402 };
403 if !title.show || title.text.is_empty() {
404 return 0.0;
405 }
406 let mut space: f64 = 0.0;
407 let font_size: f64 = title
408 .text_style
409 .as_ref()
410 .and_then(|style: &LabelOption| style.font_size)
411 .unwrap_or(DEFAULT_TITLE_FONT_SIZE);
412 let metrics: TextMetrics = measurer.measure(&title.text, &font_string(font_size));
413 space += metrics.height;
414 if !title.subtext.is_empty() {
415 let subtext_size: f64 = title
416 .subtext_style
417 .as_ref()
418 .and_then(|style: &LabelOption| style.font_size)
419 .unwrap_or(DEFAULT_SUBTITLE_FONT_SIZE);
420 let subtext: TextMetrics = measurer.measure(&title.subtext, &font_string(subtext_size));
421 space += subtext.height + title.item_gap.unwrap_or(4.0);
422 }
423 space += 8.0;
424 self.set_title_rect(Some(BBox::from_min_size(0.0, 0.0, self.get_width(), space)));
425 space
426 }
427
428 fn measure_legend(&mut self, option: &ChartOption, measurer: &TextMeasurer) -> f64 {
439 let Some(legend) = &option.legend else {
440 return 0.0;
441 };
442 if !legend.show {
443 return 0.0;
444 }
445 let font_size: f64 = legend
446 .text_style
447 .as_ref()
448 .and_then(|style: &LabelOption| style.font_size)
449 .unwrap_or(DEFAULT_LEGEND_FONT_SIZE);
450 let mut widest: f64 = 0.0_f64;
451 for name in &legend.data {
452 let metrics: TextMetrics = measurer.measure(name, &font_string(font_size));
453 widest = widest.max(metrics.width);
454 }
455 let item_width: f64 = legend.item_width.unwrap_or(25.0);
456 let item_gap: f64 = legend.item_gap.unwrap_or(10.0);
457 let item_height: f64 = legend.item_height.unwrap_or(14.0);
458 let space: f64 = (item_height.max(font_size) + 8.0).min(self.get_height() * 0.5);
459 self.set_legend_rect(Some(BBox::from_min_size(
460 0.0,
461 0.0,
462 widest + item_width + item_gap,
463 space,
464 )));
465 space
466 }
467
468 fn measure_grids(
480 &self,
481 option: &ChartOption,
482 plot: &BBox,
483 measurer: &TextMeasurer,
484 ) -> Vec<GridLayout> {
485 let grid_option: GridOption = option.grid.clone().unwrap_or_default();
486 let mut grids: Vec<GridLayout> = Vec::new();
487
488 let count: usize = 1;
491 for index in 0..count {
492 let rect: BBox = self.resolve_grid_rect(&grid_option, plot);
493 let mut x_axes: Vec<AxisLayout> = Vec::new();
494 let mut y_axes: Vec<AxisLayout> = Vec::new();
495
496 for (axis_index, axis) in option.x_axis.iter().enumerate() {
497 if axis.grid_index.unwrap_or(0) != index {
498 continue;
499 }
500 x_axes.push(self.measure_x_axis(axis, axis_index, &rect, &option.series, measurer));
501 }
502 for (axis_index, axis) in option.y_axis.iter().enumerate() {
503 if axis.grid_index.unwrap_or(0) != index {
504 continue;
505 }
506 y_axes.push(self.measure_y_axis(axis, axis_index, &rect, &option.series, measurer));
507 }
508
509 if x_axes.is_empty() {
512 x_axes.push(self.default_x_axis(&rect, &option.series, measurer));
513 }
514 if y_axes.is_empty() {
515 y_axes.push(self.default_y_axis(&rect, &option.series));
516 }
517
518 grids.push(GridLayout {
519 index,
520 rect,
521 x_axes,
522 y_axes,
523 });
524 }
525 grids
526 }
527
528 fn measure_geo(&self, option: &ChartOption, maps: &MapRegistry) -> Vec<GeoLayout> {
543 let mut names: Vec<(String, f64)> = Vec::new();
544 let remember: fn(&mut Vec<(String, f64)>, &str, f64) =
545 |names: &mut Vec<(String, f64)>, name: &str, aspect: f64| {
546 if !names.iter().any(|(known, _): &(String, f64)| known == name) {
547 names.push((name.to_string(), aspect));
548 }
549 };
550 for geo in &option.geo {
551 let Some(name) = &geo.map_name else {
552 continue;
553 };
554 remember(
555 &mut names,
556 name,
557 geo.aspect_scale.unwrap_or(DEFAULT_ASPECT_SCALE),
558 );
559 }
560 for series in &option.series {
561 if series.kind != SeriesType::Map {
562 continue;
563 }
564 let Some(name) = map_series_name(series, option) else {
565 continue;
566 };
567 remember(&mut names, &name, map_series_aspect(series, option));
568 }
569 let mut layouts: Vec<GeoLayout> = Vec::new();
570 for (name, aspect) in names {
571 let Some(data) = maps.get(&name) else {
572 continue;
573 };
574 layouts.push(GeoLayout::compute(data, self.get_plot_rect(), aspect));
575 }
576 layouts
577 }
578
579 fn resolve_grid_rect(&self, option: &GridOption, plot: &BBox) -> BBox {
590 let left: f64 = resolve_edge(
591 option.left.as_ref(),
592 DEFAULT_GRID_LEFT * plot.width(),
593 plot.width(),
594 );
595 let right: f64 = resolve_edge(
596 option.right.as_ref(),
597 DEFAULT_GRID_RIGHT * plot.width(),
598 plot.width(),
599 );
600 let top: f64 = resolve_edge(option.top.as_ref(), DEFAULT_GRID_TOP, plot.height());
601 let bottom: f64 = resolve_edge(option.bottom.as_ref(), DEFAULT_GRID_BOTTOM, plot.height());
602 let min_x: f64 = plot.min_x + left;
603 let min_y: f64 = plot.min_y + top;
604 let max_x: f64 = plot.max_x - right;
605 let max_y: f64 = plot.max_y - bottom;
606 BBox::from_corners(min_x, min_y, max_x.max(min_x), max_y.max(min_y))
607 }
608
609 fn measure_x_axis(
623 &self,
624 axis: &AxisOption,
625 index: usize,
626 rect: &BBox,
627 series: &[SeriesOption],
628 measurer: &TextMeasurer,
629 ) -> AxisLayout {
630 let side: AxisSide = match axis.position {
631 Some(Position::Top) => AxisSide::Top,
632 _ => AxisSide::Bottom,
633 };
634 let offset: f64 = match side {
635 AxisSide::Top => rect.min_y,
636 _ => rect.max_y,
637 };
638 let (scale, ticks) = self.build_scale(axis, ScaleKind::Linear, rect, series, true);
639 let (label_space, _, label_interval) =
640 self.measure_label_space(&ticks, axis, measurer, false, rect.width());
641 let name_space: f64 = if axis.name.is_some() { 20.0 } else { 0.0 };
642 AxisLayout {
643 index,
644 kind: axis.axis_type,
645 scale,
646 ticks,
647 side,
648 offset,
649 label_space,
650 name: axis.name.clone(),
651 name_space,
652 show: axis.show,
653 labels: Vec::new(),
654 label_interval,
655 }
656 }
657
658 fn measure_y_axis(
672 &self,
673 axis: &AxisOption,
674 index: usize,
675 rect: &BBox,
676 series: &[SeriesOption],
677 measurer: &TextMeasurer,
678 ) -> AxisLayout {
679 let side: AxisSide = match axis.position {
680 Some(Position::Right) => AxisSide::Right,
681 _ => AxisSide::Left,
682 };
683 let offset: f64 = match side {
684 AxisSide::Right => rect.max_x,
685 _ => rect.min_x,
686 };
687 let (scale, ticks) = self.build_scale(axis, ScaleKind::Linear, rect, series, false);
688 let (label_space, _, label_interval) =
689 self.measure_label_space(&ticks, axis, measurer, true, rect.height());
690 let name_space: f64 = if axis.name.is_some() { 20.0 } else { 0.0 };
691 AxisLayout {
692 index,
693 kind: axis.axis_type,
694 scale,
695 ticks,
696 side,
697 offset,
698 label_space,
699 name: axis.name.clone(),
700 name_space,
701 show: axis.show,
702 labels: Vec::new(),
703 label_interval,
704 }
705 }
706
707 fn default_x_axis(
724 &self,
725 rect: &BBox,
726 series: &[SeriesOption],
727 measurer: &TextMeasurer,
728 ) -> AxisLayout {
729 let categories: Vec<String> = collect_categories(series);
730 let count: usize = if categories.is_empty() {
731 category_count(series)
732 } else {
733 categories.len()
734 }
735 .max(1);
736 let band: f64 = rect.width() / count as f64;
737 let inset: f64 = band * 0.5;
738 let scale: Scale = if categories.is_empty() {
739 Scale::category_count(count, rect.min_x + inset, rect.max_x - inset, band)
740 } else {
741 Scale::category(categories, rect.min_x + inset, rect.max_x - inset, band)
742 };
743 let ticks: Vec<AxisTick> = category_ticks(&scale);
744 let default_axis: AxisOption = AxisOption::category();
747 let (label_space, _, label_interval) =
748 self.measure_label_space(&ticks, &default_axis, measurer, false, rect.width());
749 AxisLayout {
750 index: 0,
751 kind: AxisType::Category,
752 scale,
753 ticks,
754 side: AxisSide::Bottom,
755 offset: rect.max_y,
756 label_space,
757 name: None,
758 name_space: 0.0,
759 show: true,
760 labels: Vec::new(),
761 label_interval,
762 }
763 }
764
765 fn default_y_axis(&self, rect: &BBox, series: &[SeriesOption]) -> AxisLayout {
776 let extent: (f64, f64) = value_extent(series, None);
777 let (domain_min, domain_max) = nice_extent(extent.0, extent.1, DEFAULT_SPLIT_NUMBER);
778 let scale: Scale = Scale::linear(domain_min, domain_max, rect.max_y, rect.min_y);
779 let ticks: Vec<AxisTick> = place_ticks(
780 &scale,
781 value_ticks(domain_min, domain_max, DEFAULT_SPLIT_NUMBER),
782 );
783 AxisLayout {
784 index: 0,
785 kind: AxisType::Value,
786 scale,
787 ticks,
788 side: AxisSide::Left,
789 offset: rect.min_x,
790 label_space: 40.0,
791 name: None,
792 name_space: 0.0,
793 show: true,
794 labels: Vec::new(),
795 label_interval: 1,
796 }
797 }
798
799 fn measure_label_space(
817 &self,
818 ticks: &[AxisTick],
819 axis: &AxisOption,
820 measurer: &TextMeasurer,
821 vertical: bool,
822 extent: f64,
823 ) -> (f64, f64, usize) {
824 let font_size: f64 = axis
825 .axis_label
826 .as_ref()
827 .and_then(|label: &LabelOption| label.font_size)
828 .unwrap_or(DEFAULT_AXIS_LABEL_FONT_SIZE);
829 let font: String = font_string(font_size);
830 let mut widest: f64 = 0.0_f64;
831 let mut tallest: f64 = 0.0_f64;
832 for tick in ticks {
833 let metrics: TextMetrics = measurer.measure(&tick.label, &font);
834 widest = widest.max(metrics.width);
835 tallest = tallest.max(metrics.height);
836 }
837 let tick_length: f64 = if axis
838 .axis_tick
839 .as_ref()
840 .map(|tick: &AxisTickOption| tick.show)
841 .unwrap_or(false)
842 {
843 DEFAULT_AXIS_TICK_LENGTH
844 } else {
845 0.0
846 };
847 let space: f64 =
848 (if vertical { widest } else { tallest }) + tick_length + DEFAULT_AXIS_LABEL_GAP;
849
850 let label_extent: f64 = if vertical { tallest } else { widest };
853 let spacing: f64 = if ticks.is_empty() {
854 extent
855 } else {
856 extent / ticks.len() as f64
857 };
858 let interval = if label_extent > 0.0 && spacing > 0.0 {
859 ((label_extent / spacing).ceil() as usize).max(1)
860 } else {
861 1
862 };
863 (space, interval as f64, interval)
864 }
865
866 fn build_scale(
880 &self,
881 axis: &AxisOption,
882 default_kind: ScaleKind,
883 rect: &BBox,
884 series: &[SeriesOption],
885 horizontal: bool,
886 ) -> (Scale, Vec<AxisTick>) {
887 let (range_min, range_max) = if horizontal {
888 (rect.min_x, rect.max_x)
889 } else {
890 (rect.max_y, rect.min_y)
891 };
892
893 let kind: ScaleKind = match axis.axis_type {
894 AxisType::Category => ScaleKind::Category,
895 AxisType::Value => ScaleKind::Linear,
896 AxisType::Time => ScaleKind::Time,
897 AxisType::Log => ScaleKind::Log,
898 };
899 let _: ScaleKind = default_kind;
900
901 match kind {
902 ScaleKind::Category => {
903 let categories: Vec<String> = if !axis.data.is_empty() {
904 axis.data
905 .iter()
906 .map(|value: &DataValue| value.as_text().unwrap_or_default().to_string())
907 .collect()
908 } else {
909 collect_categories(series)
910 };
911 let count: usize = if categories.is_empty() {
912 category_count(series)
913 } else {
914 categories.len()
915 };
916 let band: f64 = (range_max - range_min).abs() / count.max(1) as f64;
917 let inset: f64 = band * 0.5;
918 let scale: Scale = if categories.is_empty() {
919 Scale::category_count(count, range_min + inset, range_max - inset, band)
920 } else {
921 Scale::category(categories, range_min + inset, range_max - inset, band)
922 };
923 let ticks: Vec<AxisTick> = category_ticks(&scale);
924 (scale, ticks)
925 }
926 ScaleKind::Log => {
927 let extent: (f64, f64) = value_extent(series, Some(axis));
928 let (domain_min, domain_max) =
929 nice_extent(extent.0, extent.1, DEFAULT_SPLIT_NUMBER);
930 let base: f64 = axis.log_base.unwrap_or(10.0);
931 let scale: Scale = Scale::log(
932 domain_min.max(f64::MIN_POSITIVE),
933 domain_max.max(f64::MIN_POSITIVE),
934 range_min,
935 range_max,
936 base,
937 );
938 let ticks: Vec<AxisTick> =
939 log_ticks(domain_min, domain_max, base, range_min, range_max);
940 (scale, ticks)
941 }
942 _ => {
943 let extent: (f64, f64) = value_extent(series, Some(axis));
944 let mut domain_min: f64 = axis.min.unwrap_or(extent.0);
945 let mut domain_max: f64 = axis.max.unwrap_or(extent.1);
946 if domain_min == domain_max {
947 domain_min -= 1.0;
948 domain_max += 1.0;
949 }
950 let (nice_min, nice_max, interval) =
954 nice_scale(domain_min, domain_max, DEFAULT_SPLIT_NUMBER);
955 if axis.min.is_none() {
956 domain_min = nice_min;
957 }
958 if axis.max.is_none() {
959 domain_max = nice_max;
960 }
961 let scale: Scale = Scale::linear(domain_min, domain_max, range_min, range_max);
962 let ticks: Vec<AxisTick> =
963 place_ticks(&scale, ticks_at_interval(domain_min, domain_max, interval));
964 (scale, ticks)
965 }
966 }
967 }
968
969 fn measure_series(&self, option: &ChartOption, grids: &[GridLayout]) -> Vec<SeriesLayout> {
980 let mut layouts: Vec<SeriesLayout> = Vec::with_capacity(option.series.len());
981 for (index, series) in option.series.iter().enumerate() {
982 if !series.show {
983 continue;
984 }
985 let grid_index: usize = series
986 .x_axis_index
987 .or(series.y_axis_index)
988 .map(|_| 0)
989 .unwrap_or(0);
990 let grid: Option<&GridLayout> = grids.get(grid_index);
991 let (points, stack_index, stack_size, band_width) = match series.kind {
992 SeriesType::Pie
995 | SeriesType::Funnel
996 | SeriesType::Radar
997 | SeriesType::Gauge
998 | SeriesType::Map => (self.place_values(series), 0, 1, 0.0),
999 _ => match grid {
1000 Some(grid) => {
1001 let x_axis: Option<&AxisLayout> =
1002 grid.x_axis(series.x_axis_index.unwrap_or(0));
1003 let y_axis: Option<&AxisLayout> =
1004 grid.y_axis(series.y_axis_index.unwrap_or(0));
1005 match (x_axis, y_axis) {
1006 (Some(x_axis), Some(y_axis)) => {
1007 let points: Vec<SeriesPoint> =
1008 self.place_points(series, x_axis, y_axis);
1009 let (stack_index, stack_size) = stack_position(option, index);
1010 (points, stack_index, stack_size, x_axis.scale.band_width())
1011 }
1012 _ => (Vec::new(), 0, 1, 0.0),
1013 }
1014 }
1015 None => (Vec::new(), 0, 1, 0.0),
1016 },
1017 };
1018 layouts.push(SeriesLayout {
1019 index,
1020 kind: series.kind,
1021 name: series.name.clone(),
1022 grid_index,
1023 points,
1024 stack: series.stack.clone(),
1025 stack_index,
1026 stack_size,
1027 plot_rect: self.get_plot_rect(),
1028 band_width,
1029 });
1030 }
1031 layouts
1032 }
1033
1034 fn place_values(&self, series: &SeriesOption) -> Vec<SeriesPoint> {
1046 series
1047 .data
1048 .iter()
1049 .enumerate()
1050 .map(|(data_index, value): (usize, &DataValue)| {
1051 let (name, number) = match value {
1052 DataValue::List(items) => (
1053 items
1054 .first()
1055 .and_then(|item: &DataValue| item.as_text())
1056 .unwrap_or_default()
1057 .to_string(),
1058 items
1059 .get(1)
1060 .and_then(|item: &DataValue| item.to_number())
1061 .unwrap_or(0.0),
1062 ),
1063 DataValue::Object(_) => (
1064 value
1065 .get(DATA_FIELD_NAME)
1066 .and_then(|item: &DataValue| item.as_text())
1067 .unwrap_or_default()
1068 .to_string(),
1069 value
1070 .get(DATA_FIELD_VALUE)
1071 .and_then(|item: &DataValue| item.to_number())
1072 .unwrap_or(0.0),
1073 ),
1074 other => (
1075 other.as_text().unwrap_or_default().to_string(),
1076 other.to_number().unwrap_or(0.0),
1077 ),
1078 };
1079 SeriesPoint {
1080 x: 0.0,
1081 y: 0.0,
1082 value: number,
1083 name,
1084 data_index,
1085 }
1086 })
1087 .collect()
1088 }
1089
1090 fn place_points(
1101 &self,
1102 series: &SeriesOption,
1103 x_axis: &AxisLayout,
1104 y_axis: &AxisLayout,
1105 ) -> Vec<SeriesPoint> {
1106 let mut points: Vec<SeriesPoint> = Vec::with_capacity(series.data.len());
1107 let category_axis: bool = x_axis.scale.is_ordinal();
1108 for (data_index, value) in series.data.iter().enumerate() {
1109 let (name, x_value, y_value) = match value {
1110 DataValue::List(items) => {
1111 let first: Option<f64> =
1112 items.first().and_then(|item: &DataValue| item.to_number());
1113 let second: Option<f64> =
1114 items.get(1).and_then(|item: &DataValue| item.to_number());
1115 let label: String = items
1116 .first()
1117 .and_then(|item: &DataValue| item.as_text())
1118 .map(|text: &str| text.to_string())
1119 .unwrap_or_default();
1120 match (first, second) {
1126 (Some(x), Some(y)) => (label, x, y),
1127 (None, Some(y)) => (label, data_index as f64, y),
1128 (Some(y), None) => (label, data_index as f64, y),
1129 (None, None) => continue,
1130 }
1131 }
1132 DataValue::Object(_) => {
1133 let name: String = value
1134 .get(DATA_FIELD_NAME)
1135 .and_then(|item: &DataValue| item.as_text())
1136 .unwrap_or_default()
1137 .to_string();
1138 let (x_value, y_value) = match value.get(DATA_FIELD_VALUE) {
1143 Some(DataValue::List(items)) => (
1144 items.first().and_then(|item: &DataValue| item.to_number()),
1145 items.get(1).and_then(|item: &DataValue| item.to_number()),
1146 ),
1147 other => (None, other.and_then(|item: &DataValue| item.to_number())),
1148 };
1149 let Some(y_value) = y_value else {
1150 continue;
1151 };
1152 let x_value: f64 = match x_value {
1153 Some(x_value) => x_value,
1154 None => data_index as f64,
1155 };
1156 (name, x_value, y_value)
1157 }
1158 other => {
1159 let y: f64 = match other.to_number() {
1160 Some(y) => y,
1161 None => continue,
1162 };
1163 (String::new(), data_index as f64, y)
1164 }
1165 };
1166 let x: f64 = if category_axis {
1167 x_axis.scale.map(data_index as f64)
1168 } else {
1169 x_axis.scale.map(x_value)
1170 };
1171 let y: f64 = y_axis.scale.map(y_value);
1172 points.push(SeriesPoint {
1173 x,
1174 y,
1175 value: y_value,
1176 name,
1177 data_index,
1178 });
1179 }
1180 points
1181 }
1182 pub fn geo(&self, name: &str) -> Option<&GeoLayout> {
1196 self.get_geo()
1197 .iter()
1198 .find(|layout: &&GeoLayout| layout.name == name)
1199 }
1200}
1201
1202impl VisualMap {
1204 pub fn resolve(option: &VisualMapOption, values: &[f64]) -> Self {
1215 let mut min: f64 = option.min.unwrap_or(f64::INFINITY);
1216 let mut max: f64 = option.max.unwrap_or(f64::NEG_INFINITY);
1217 for value in values {
1218 min = min.min(*value);
1219 max = max.max(*value);
1220 }
1221 if !min.is_finite() {
1222 min = 0.0;
1223 }
1224 if !max.is_finite() {
1225 max = 0.0;
1226 }
1227 if max <= min {
1228 max = min + 1.0;
1231 }
1232 let colors: Vec<Color> = parse_colors(&option.colors);
1233 Self {
1234 visual_type: option.visual_type,
1235 min,
1236 max,
1237 colors,
1238 split_number: option
1239 .split_number
1240 .unwrap_or(DEFAULT_VISUAL_MAP_SPLIT)
1241 .max(1),
1242 pieces: parse_pieces(&option.pieces),
1243 out_of_range: option.out_of_range.as_deref().and_then(Color::parse),
1244 }
1245 }
1246
1247 pub fn color_of(&self, value: f64) -> Color {
1261 if !self.get_pieces().is_empty() {
1262 for piece in self.get_pieces() {
1263 if value >= piece.min && value < piece.max {
1264 return piece.color;
1265 }
1266 }
1267 if let Some(color) = self.get_out_of_range() {
1268 return color;
1269 }
1270 }
1271 if self.get_colors().is_empty() {
1272 return Color::rgb(191, 68, 76);
1273 }
1274 let span: f64 = self.get_max() - self.get_min();
1275 let position: f64 = if span.abs() < f64::EPSILON {
1276 0.0
1277 } else {
1278 ((value - self.get_min()) / span).clamp(0.0, 1.0)
1279 };
1280 match self.get_visual_type() {
1281 VisualMapType::Piecewise => {
1282 let bands: usize = self.get_split_number().max(1);
1283 let index: usize = ((position * bands as f64).floor() as usize).min(bands - 1);
1284 self.get_colors()[index % self.get_colors().len()]
1285 }
1286 VisualMapType::Continuous => {
1287 let place: f64 = position * (self.get_colors().len() - 1) as f64;
1288 let low: usize = place.floor() as usize;
1289 let high: usize = place.ceil() as usize;
1290 if low == high {
1291 return self.get_colors()[low];
1292 }
1293 self.get_colors()[low].lerp(self.get_colors()[high], place - low as f64)
1294 }
1295 }
1296 }
1297}