1use crate::color::{COLOR_HEX_GREEN_1, COLOR_HEX_GREEN_4, COLOR_HEX_GREEN_5};
2use crate::render::svg::*;
3use crate::shape::area::Area;
4use crate::shape::point::Point;
5use crate::{BandScale, Color, Error, LinearScale, PointLabelPosition, PointType, Scale, View};
6use svg::Node;
7
8const DEFAULT_LABEL_VISIBLE: bool = true;
9const DEFAULT_LABEL_POSITION: PointLabelPosition = PointLabelPosition::Top;
10
11const DEFAULT_POINT_TYPE: PointType = PointType::Circle;
12const DEFAULT_POINT_VISIBLE: bool = true;
13
14#[derive(Clone)]
16pub struct AreaView {
17 x_scale: BandScale,
18 y_scale: LinearScale,
19 area: Area,
20 fill_color: String,
21 stroke_color: String,
22 point_fill_color: String,
23 point_stroke_color: String,
24 point_type: PointType,
25 point_visible: bool,
26 point_label_visible: bool,
27 point_label_position: PointLabelPosition,
28}
29
30impl AreaView {
31 pub fn new(x_scale: BandScale, y_scale: LinearScale) -> Self {
33 Self {
34 x_scale,
35 y_scale,
36 fill_color: COLOR_HEX_GREEN_5.to_string(),
37 stroke_color: COLOR_HEX_GREEN_1.to_string(),
38 point_fill_color: COLOR_HEX_GREEN_4.to_string(),
39 point_stroke_color: COLOR_HEX_GREEN_1.to_string(),
40 area: Area::default(),
41 point_type: DEFAULT_POINT_TYPE,
42 point_visible: DEFAULT_POINT_VISIBLE,
43 point_label_visible: DEFAULT_LABEL_VISIBLE,
44 point_label_position: DEFAULT_LABEL_POSITION,
45 }
46 }
47
48 pub fn set_fill_color(mut self, fill_color: Color) -> Self {
50 self.fill_color = fill_color.to_string();
51 self
52 }
53 pub fn set_stroke_color(mut self, stroke_color: Color) -> Self {
55 self.stroke_color = stroke_color.to_string();
56 self
57 }
58
59 pub fn set_point_fill_color(mut self, point_fill_color: Color) -> Self {
61 self.point_fill_color = point_fill_color.to_string();
62 self
63 }
64
65 pub fn set_point_stroke_color(mut self, point_stroke_color: Color) -> Self {
67 self.point_stroke_color = point_stroke_color.to_string();
68 self
69 }
70
71 pub fn set_point_type(mut self, point_type: PointType) -> Self {
73 self.point_type = point_type;
74 self
75 }
76
77 pub fn set_point_visible(mut self, point_visible: bool) -> Self {
79 self.point_visible = point_visible;
80 self
81 }
82
83 pub fn set_point_label_visible(mut self, point_label_visible: bool) -> Self {
85 self.point_label_visible = point_label_visible;
86 self
87 }
88
89 pub fn set_point_label_position(mut self, point_label_position: PointLabelPosition) -> Self {
91 self.point_label_position = point_label_position;
92 self
93 }
94
95 pub fn set_data(mut self, data: &[f32]) -> Result<Self, Error> {
97 if data.is_empty() {
98 return Err(Error::DataIsEmpty);
99 }
100 if data.len() != self.x_scale.ticks().len() {
101 return Err(Error::CategoriesCountDoesntEqual);
102 }
103
104 let x_bandwidth_offset = {
106 if self.x_scale.is_range_reversed() {
107 -self.x_scale.tick_offset()
108 } else {
109 self.x_scale.tick_offset()
110 }
111 };
112 let y_bandwidth_offset = {
113 if self.y_scale.is_range_reversed() {
114 -self.y_scale.tick_offset()
115 } else {
116 self.y_scale.tick_offset()
117 }
118 };
119
120 let categories = self.x_scale.ticks();
121 let mut points = Vec::new();
122 for (idx, value) in data.iter().enumerate() {
123 let category = &categories[idx];
124 let scaled_x = &self.x_scale.scale(category);
125 let scaled_y = self.y_scale.scale(&value);
126
127 let point = Point::new(
128 scaled_x + x_bandwidth_offset,
129 scaled_y + y_bandwidth_offset,
130 self.point_type,
131 DEFAULT_POINT_SIZE,
132 &value.to_string(),
133 &self.point_fill_color.to_string(),
134 &self.point_stroke_color.to_string(),
135 )
136 .set_point_visible(self.point_visible)
137 .set_label_visible(self.point_label_visible)
138 .set_label_position(self.point_label_position);
139 points.push(point);
140 }
141
142 let y_origin = {
143 if self.y_scale.is_range_reversed() {
144 self.y_scale.range_start()
145 } else {
146 self.y_scale.range_end()
147 }
148 };
149
150 let last_point = Point::new(
151 self.x_scale
152 .scale(&categories[self.x_scale.ticks().len() - 1])
153 + x_bandwidth_offset,
154 y_origin as f32,
155 self.point_type,
156 DEFAULT_POINT_SIZE,
157 &data[0].to_string(),
158 &self.point_fill_color,
159 &self.point_stroke_color,
160 )
161 .set_point_visible(false)
162 .set_label_visible(false);
163 points.push(last_point);
164
165 let first_point = Point::new(
166 self.x_scale.scale(&categories[0]) + x_bandwidth_offset,
167 y_origin as f32,
168 self.point_type,
169 DEFAULT_POINT_SIZE,
170 &data[0].to_string(),
171 &self.point_fill_color,
172 &self.point_stroke_color,
173 )
174 .set_point_visible(false)
175 .set_label_visible(false);
176 points.push(first_point);
177
178 self.area = Area::new(points, &self.fill_color, &self.stroke_color);
179
180 Ok(self)
181 }
182}
183
184impl View for AreaView {
185 fn to_svg(&self) -> svg::node::element::Group {
187 let mut res = svg::node::element::Group::new();
188 res.append(self.area.to_svg());
189
190 res
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::Color;
198
199 #[test]
200 fn area_basic() {
201 let expected_svg_group = r##"<g>
202<g class="area">
203<g class="point" transform="translate(13.414631,99.01)">
204<g>
205<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
206<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
207</g>
208<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
20989.1
210</text>
211</g>
212<g class="point" transform="translate(37.804874,99.865555)">
213<g>
214<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
215<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
216</g>
217<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
21812.1
219</text>
220</g>
221<g class="point" transform="translate(62.195118,99.5)">
222<g>
223<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
224<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
225</g>
226<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
22745
228</text>
229</g>
230<g class="point" transform="translate(86.585365,99.76667)">
231<g>
232<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
233<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
234</g>
235<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
23621
237</text>
238</g>
239<g class="point" transform="translate(86.585365,100)"/>
240<g class="point" transform="translate(13.414631,100)"/>
241<path d="M13.414631,99.01 L37.804874,99.865555 L62.195118,99.5 L86.585365,99.76667 L86.585365,100 L13.414631,100 z" fill="#038d05" stroke="#ff006c"/>
242<g class="point" transform="translate(13.414631,99.01)">
243<g>
244<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
245<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
246</g>
247<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
24889.1
249</text>
250</g>
251<g class="point" transform="translate(37.804874,99.865555)">
252<g>
253<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
254<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
255</g>
256<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
25712.1
258</text>
259</g>
260<g class="point" transform="translate(62.195118,99.5)">
261<g>
262<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
263<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
264</g>
265<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
26645
267</text>
268</g>
269<g class="point" transform="translate(86.585365,99.76667)">
270<g>
271<line stroke="#ffffff" stroke-width="2px" x1="-5" x2="5" y1="-5" y2="5"/>
272<line stroke="#ffffff" stroke-width="2px" x1="5" x2="-5" y1="-5" y2="5"/>
273</g>
274<text dy=".35em" fill="#080808" font-family="sans-serif" font-size="14px" text-anchor="middle" x="0" y="17">
27521
276</text>
277</g>
278<g class="point" transform="translate(86.585365,100)"/>
279<g class="point" transform="translate(13.414631,100)"/>
280</g>
281</g>"##;
282
283 let x_scale = BandScale::new(
284 vec![
285 "a".to_string(),
286 "n1".to_string(),
287 "n2".to_string(),
288 "y".to_string(),
289 ],
290 0,
291 100,
292 );
293 let y_scale = LinearScale::new(0_f32, 9000_f32, 100, 0);
294 let data = vec![89.1_f32, 12.1_f32, 45_f32, 21_f32];
295 let area = AreaView::new(x_scale, y_scale)
296 .set_point_label_position(PointLabelPosition::Bottom)
297 .set_stroke_color(Color::new_from_hex("#ff006c"))
298 .set_point_fill_color(Color::new_from_hex("#ffe5f5"))
299 .set_point_type(PointType::X)
300 .set_point_stroke_color(Color::new_from_hex("#ffffff"))
301 .set_data(&data)
302 .expect("unable to set data");
303 let area_svg = area.to_svg();
304 assert_eq!(area_svg.to_string(), expected_svg_group);
305 }
306}