1use std::f64::consts::TAU;
6
7use crate::aes::Aesthetic;
8use crate::coord::Coord;
9use crate::data::{DataFrame, Value};
10use crate::position::identity::PositionIdentity;
11use crate::position::Position;
12use crate::render::backend::{
13 DrawBackend, FontFace, LineStyle, Linetype, RectStyle, TextAnchor, TextStyle,
14};
15use crate::render::RenderError;
16use crate::scale::ScaleSet;
17use crate::stat::venn::StatVenn;
18use crate::stat::Stat;
19use crate::theme::Theme;
20
21use super::{Geom, GeomParams};
22
23pub struct GeomVenn {
25 pub fill: (u8, u8, u8),
27 pub alpha: f64,
28 pub outline_width: f64,
29 pub label_size: f64,
30 pub label_color: (u8, u8, u8),
31}
32
33impl Default for GeomVenn {
34 fn default() -> Self {
35 GeomVenn {
36 fill: (97, 156, 255),
37 alpha: 0.3,
38 outline_width: 1.5,
39 label_size: 11.0,
40 label_color: (30, 30, 30),
41 }
42 }
43}
44
45impl Geom for GeomVenn {
46 fn draw(
47 &self,
48 data: &DataFrame,
49 coord: &dyn Coord,
50 scales: &ScaleSet,
51 theme: &Theme,
52 backend: &mut dyn DrawBackend,
53 ) -> Result<(), RenderError> {
54 let col = |n: &str| {
55 data.column(n)
56 .ok_or_else(|| RenderError::MissingAesthetic(n.into()))
57 };
58 let (part, xc, yc, rc) = (col("part")?, col("x")?, col("y")?, col("r")?);
59 let (set, text, size) = (col("set")?, col("text")?, col("size")?);
60 let fill_col = data.column("fill");
61 let area = backend.plot_area();
62 let px = |x: f64, y: f64| {
63 let sx = scales.get(&Aesthetic::X).map(|s| s.map(&Value::Float(x)));
64 let sy = scales.get(&Aesthetic::Y).map(|s| s.map(&Value::Float(y)));
65 coord.transform((sx.unwrap_or(0.5), sy.unwrap_or(0.5)), &area)
66 };
67 for i in (0..data.nrows()).filter(|&i| part[i].key_str() == "circle") {
68 let (Some(x), Some(y), Some(r)) = (xc[i].as_f64(), yc[i].as_f64(), rc[i].as_f64())
69 else {
70 continue;
71 };
72 let pts: Vec<(f64, f64)> = (0..=96)
73 .map(|k| {
74 let a = TAU * k as f64 / 96.0;
75 px(x + r * a.cos(), y + r * a.sin())
76 })
77 .collect();
78 let fill = fill_col
79 .and_then(|f| scales.map_color(&Aesthetic::Fill, &f[i]))
80 .unwrap_or(self.fill);
81 let name = super::tip_value(&set[i]);
82 super::set_mark(
83 backend,
84 Some(format!("{name}: {}", super::tip_value(&size[i]))),
85 Some(name.clone()),
86 Some(name),
87 super::raw_value(&size[i]),
88 );
89 backend.draw_polygon(
90 &pts,
91 &RectStyle {
92 fill: Some(fill),
93 stroke: None,
94 stroke_width: 0.0,
95 alpha: self.alpha,
96 clip: true,
97 corner_radius: 0.0,
98 },
99 )?;
100 backend.draw_line(
101 &pts,
102 &LineStyle {
103 color: fill,
104 alpha: 1.0,
105 width: self.outline_width,
106 linetype: Linetype::Solid,
107 ..Default::default()
108 },
109 )?;
110 }
111 super::clear_mark(backend);
112 let family = super::resolve_font_family("", theme);
113 for i in (0..data.nrows()).filter(|&i| part[i].key_str() == "label") {
114 let (Some(x), Some(y)) = (xc[i].as_f64(), yc[i].as_f64()) else {
115 continue;
116 };
117 backend.draw_text(
118 &super::tip_value(&text[i]),
119 px(x, y),
120 &TextStyle {
121 color: self.label_color,
122 size: self.label_size,
123 anchor: TextAnchor::Middle,
124 angle: 0.0,
125 family: family.clone(),
126 face: FontFace::Plain,
127 bg_colour: None,
128 bg_r: 0.0,
129 },
130 )?;
131 }
132 Ok(())
133 }
134
135 fn required_aes(&self) -> Vec<Aesthetic> {
136 vec![Aesthetic::Sets]
137 }
138 fn default_stat(&self) -> Box<dyn Stat> {
139 Box::new(StatVenn)
140 }
141 fn default_position(&self) -> Box<dyn Position> {
142 Box::new(PositionIdentity)
143 }
144 fn default_params(&self) -> GeomParams {
145 GeomParams::default()
146 }
147 fn name(&self) -> &str {
148 "venn"
149 }
150 fn apply_element_geom(&mut self, el: &crate::theme::elements::ElementGeom) {
151 if let Some(ink) = el.ink {
152 self.label_color = ink;
153 }
154 }
155}