1use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::{
7 color::Color,
8 common::{
9 Calendar, ColorBar, ColorScale, Dim, Font, HoverInfo, Label, LegendGroupTitle, Line,
10 PlotType, Visible,
11 },
12 private, Trace,
13};
14
15#[derive(Serialize, Debug, Clone)]
16#[serde(rename_all = "lowercase")]
17pub enum ContoursType {
18 Levels,
19 Constraint,
20}
21
22#[derive(Serialize, Debug, Clone)]
23#[serde(rename_all = "lowercase")]
24pub enum Coloring {
25 Fill,
26 HeatMap,
27 Lines,
28 None,
29}
30
31#[derive(Serialize, Debug, Clone)]
32pub enum Operation {
33 #[serde(rename = "=")]
34 Equals,
35 #[serde(rename = "<")]
36 LessThan,
37 #[serde(rename = "<=")]
38 LessThanOrEqual,
39 #[serde(rename = ">")]
40 GreaterThan,
41 #[serde(rename = ">=")]
42 GreaterThanOrEqual,
43 #[serde(rename = "[]")]
44 Inside,
45 #[serde(rename = "][")]
46 Outside,
47}
48
49#[serde_with::skip_serializing_none]
50#[derive(Serialize, Debug, Clone, FieldSetter)]
51pub struct Contours {
52 #[field_setter(skip)]
53 r#type: Option<ContoursType>,
54 start: Option<f64>,
55 end: Option<f64>,
56 size: Option<usize>,
57 coloring: Option<Coloring>,
58 #[serde(rename = "showlines")]
59 show_lines: Option<bool>,
60 #[serde(rename = "showlabels")]
61 show_labels: Option<bool>,
62 #[serde(rename = "labelfont")]
63 label_font: Option<Font>,
64 #[serde(rename = "labelformat")]
65 label_format: Option<String>,
66 operation: Option<Operation>,
67 value: Option<f64>,
68}
69
70impl Contours {
71 pub fn new() -> Self {
72 Default::default()
73 }
74
75 pub fn type_(mut self, t: ContoursType) -> Self {
76 self.r#type = Some(t);
77 self
78 }
79}
80
81#[serde_with::skip_serializing_none]
108#[derive(Serialize, Debug, Clone)]
109pub struct Contour<Z, X = f64, Y = f64>
110where
111 X: Serialize + Clone,
112 Y: Serialize + Clone,
113 Z: Serialize + Clone,
114{
115 r#type: PlotType,
116 name: Option<String>,
117 visible: Option<Visible>,
118 #[serde(rename = "showlegend")]
119 show_legend: Option<bool>,
120 #[serde(rename = "legendgroup")]
121 legend_group: Option<String>,
122 #[serde(rename = "legendgrouptitle")]
123 legend_group_title: Option<LegendGroupTitle>,
124 opacity: Option<f64>,
125 x: Option<Vec<X>>,
126 x0: Option<X>,
127 dx: Option<X>,
128 y: Option<Vec<Y>>,
129 y0: Option<Y>,
130 dy: Option<Y>,
131 z: Option<Vec<Z>>,
132 text: Option<Vec<String>>,
133 #[serde(rename = "hovertext")]
134 hover_text: Option<Vec<String>>,
135 #[serde(rename = "hoverinfo")]
136 hover_info: Option<HoverInfo>,
137 #[serde(rename = "hovertemplate")]
138 hover_template: Option<Dim<String>>,
139 #[serde(rename = "xaxis")]
140 x_axis: Option<String>,
141 #[serde(rename = "yaxis")]
142 y_axis: Option<String>,
143 line: Option<Line>,
144 #[serde(rename = "colorbar")]
145 color_bar: Option<ColorBar>,
146 #[serde(rename = "autocolorscale")]
147 auto_color_scale: Option<bool>,
148 #[serde(rename = "colorscale")]
149 color_scale: Option<ColorScale>,
150 #[serde(rename = "showscale")]
151 show_scale: Option<bool>,
152 #[serde(rename = "reversescale")]
153 reverse_scale: Option<bool>,
154 zauto: Option<bool>,
155 #[serde(rename = "zhoverformat")]
156 zhover_format: Option<String>,
157 zmax: Option<Z>,
158 zmid: Option<Z>,
159 zmin: Option<Z>,
160 #[serde(rename = "autocontour")]
161 auto_contour: Option<bool>,
162 #[serde(rename = "connectgaps")]
163 connect_gaps: Option<bool>,
164 contours: Option<Contours>,
165 #[serde(rename = "fillcolor")]
166 fill_color: Option<Box<dyn Color>>,
167 #[serde(rename = "hoverlabel")]
168 hover_label: Option<Label>,
169 #[serde(rename = "hoverongaps")]
170 hover_on_gaps: Option<bool>,
171 #[serde(rename = "ncontours")]
172 n_contours: Option<usize>,
173 transpose: Option<bool>,
174 #[serde(rename = "xcalendar")]
175 x_calendar: Option<Calendar>,
176 #[serde(rename = "ycalendar")]
177 y_calendar: Option<Calendar>,
178}
179
180impl<Z, X, Y> Default for Contour<Z, X, Y>
181where
182 X: Serialize + Clone,
183 Y: Serialize + Clone,
184 Z: Serialize + Clone,
185{
186 fn default() -> Self {
187 Self {
188 r#type: PlotType::Contour,
189 name: None,
190 visible: None,
191 show_legend: None,
192 legend_group: None,
193 legend_group_title: None,
194 opacity: None,
195 x: None,
196 x0: None,
197 dx: None,
198 y: None,
199 y0: None,
200 dy: None,
201 z: None,
202 text: None,
203 hover_text: None,
204 hover_info: None,
205 hover_template: None,
206 x_axis: None,
207 y_axis: None,
208 line: None,
209 color_bar: None,
210 auto_color_scale: None,
211 color_scale: None,
212 show_scale: None,
213 reverse_scale: None,
214 zauto: None,
215 zhover_format: None,
216 zmax: None,
217 zmid: None,
218 zmin: None,
219 auto_contour: None,
220 connect_gaps: None,
221 contours: None,
222 fill_color: None,
223 hover_label: None,
224 hover_on_gaps: None,
225 n_contours: None,
226 transpose: None,
227 x_calendar: None,
228 y_calendar: None,
229 }
230 }
231}
232
233impl<Z> Contour<Z, f64, f64>
234where
235 Z: Serialize + Clone,
236{
237 pub fn new_z(z: Vec<Z>) -> Box<Self> {
238 Box::new(Contour {
239 z: Some(z),
240 ..Default::default()
241 })
242 }
243}
244
245impl<Z, X, Y> Contour<Z, X, Y>
246where
247 X: Serialize + Clone,
248 Y: Serialize + Clone,
249 Z: Serialize + Clone,
250{
251 pub fn new(x: Vec<X>, y: Vec<Y>, z: Vec<Z>) -> Box<Self> {
252 Box::new(Contour {
253 x: Some(x),
254 y: Some(y),
255 z: Some(z),
256 ..Default::default()
257 })
258 }
259
260 pub fn auto_color_scale(mut self, auto_color_scale: bool) -> Box<Self> {
261 self.auto_color_scale = Some(auto_color_scale);
262 Box::new(self)
263 }
264
265 pub fn auto_contour(mut self, auto_contour: bool) -> Box<Self> {
266 self.auto_contour = Some(auto_contour);
267 Box::new(self)
268 }
269
270 pub fn color_bar(mut self, color_bar: ColorBar) -> Box<Self> {
271 self.color_bar = Some(color_bar);
272 Box::new(self)
273 }
274
275 pub fn color_scale(mut self, color_scale: ColorScale) -> Box<Self> {
276 self.color_scale = Some(color_scale);
277 Box::new(self)
278 }
279
280 pub fn connect_gaps(mut self, connect_gaps: bool) -> Box<Self> {
281 self.connect_gaps = Some(connect_gaps);
282 Box::new(self)
283 }
284
285 pub fn contours(mut self, contours: Contours) -> Box<Self> {
286 self.contours = Some(contours);
287 Box::new(self)
288 }
289
290 pub fn dx(mut self, dx: X) -> Box<Self> {
291 self.dx = Some(dx);
292 Box::new(self)
293 }
294
295 pub fn dy(mut self, dy: Y) -> Box<Self> {
296 self.dy = Some(dy);
297 Box::new(self)
298 }
299
300 pub fn fill_color<C: Color>(mut self, fill_color: C) -> Box<Self> {
301 self.fill_color = Some(Box::new(fill_color));
302 Box::new(self)
303 }
304
305 pub fn hover_info(mut self, hover_info: HoverInfo) -> Box<Self> {
306 self.hover_info = Some(hover_info);
307 Box::new(self)
308 }
309
310 pub fn hover_label(mut self, hover_label: Label) -> Box<Self> {
311 self.hover_label = Some(hover_label);
312 Box::new(self)
313 }
314
315 pub fn hover_on_gaps(mut self, hover_on_gaps: bool) -> Box<Self> {
316 self.hover_on_gaps = Some(hover_on_gaps);
317 Box::new(self)
318 }
319
320 pub fn hover_template(mut self, hover_template: &str) -> Box<Self> {
321 self.hover_template = Some(Dim::Scalar(hover_template.to_string()));
322 Box::new(self)
323 }
324
325 pub fn hover_template_array<S: AsRef<str>>(mut self, hover_template: Vec<S>) -> Box<Self> {
326 let hover_template = private::owned_string_vector(hover_template);
327 self.hover_template = Some(Dim::Vector(hover_template));
328 Box::new(self)
329 }
330
331 pub fn hover_text<S: AsRef<str>>(mut self, hover_text: Vec<S>) -> Box<Self> {
332 let hover_text = private::owned_string_vector(hover_text);
333 self.hover_text = Some(hover_text);
334 Box::new(self)
335 }
336
337 pub fn legend_group(mut self, legend_group: &str) -> Box<Self> {
338 self.legend_group = Some(legend_group.to_string());
339 Box::new(self)
340 }
341
342 pub fn legend_group_title(mut self, legend_group_title: LegendGroupTitle) -> Box<Self> {
343 self.legend_group_title = Some(legend_group_title);
344 Box::new(self)
345 }
346
347 pub fn line(mut self, line: Line) -> Box<Self> {
348 self.line = Some(line);
349 Box::new(self)
350 }
351
352 pub fn name(mut self, name: &str) -> Box<Self> {
353 self.name = Some(name.to_string());
354 Box::new(self)
355 }
356
357 pub fn n_contours(mut self, n_contours: usize) -> Box<Self> {
358 self.n_contours = Some(n_contours);
359 Box::new(self)
360 }
361
362 pub fn opacity(mut self, opacity: f64) -> Box<Self> {
363 self.opacity = Some(opacity);
364 Box::new(self)
365 }
366
367 pub fn reverse_scale(mut self, reverse_scale: bool) -> Box<Self> {
368 self.reverse_scale = Some(reverse_scale);
369 Box::new(self)
370 }
371
372 pub fn show_legend(mut self, show_legend: bool) -> Box<Self> {
373 self.show_legend = Some(show_legend);
374 Box::new(self)
375 }
376
377 pub fn show_scale(mut self, show_scale: bool) -> Box<Self> {
378 self.show_scale = Some(show_scale);
379 Box::new(self)
380 }
381
382 pub fn text<S: AsRef<str>>(mut self, text: Vec<S>) -> Box<Self> {
383 let text = private::owned_string_vector(text);
384 self.text = Some(text);
385 Box::new(self)
386 }
387
388 pub fn transpose(mut self, transpose: bool) -> Box<Self> {
389 self.transpose = Some(transpose);
390 Box::new(self)
391 }
392
393 pub fn visible(mut self, visible: Visible) -> Box<Self> {
394 self.visible = Some(visible);
395 Box::new(self)
396 }
397
398 pub fn x(mut self, x: Vec<X>) -> Box<Self> {
399 self.x = Some(x);
400 Box::new(self)
401 }
402
403 pub fn x_axis(mut self, axis: &str) -> Box<Self> {
404 self.x_axis = Some(axis.to_string());
405 Box::new(self)
406 }
407
408 pub fn x_calendar(mut self, x_calendar: Calendar) -> Box<Self> {
409 self.x_calendar = Some(x_calendar);
410 Box::new(self)
411 }
412
413 pub fn x0(mut self, x0: X) -> Box<Self> {
414 self.x0 = Some(x0);
415 Box::new(self)
416 }
417
418 pub fn y_axis(mut self, axis: &str) -> Box<Self> {
419 self.y_axis = Some(axis.to_string());
420 Box::new(self)
421 }
422
423 pub fn y(mut self, y: Vec<Y>) -> Box<Self> {
424 self.y = Some(y);
425 Box::new(self)
426 }
427
428 pub fn y_calendar(mut self, y_calendar: Calendar) -> Box<Self> {
429 self.y_calendar = Some(y_calendar);
430 Box::new(self)
431 }
432
433 pub fn y0(mut self, y0: Y) -> Box<Self> {
434 self.y0 = Some(y0);
435 Box::new(self)
436 }
437
438 pub fn zauto(mut self, zauto: bool) -> Box<Self> {
439 self.zauto = Some(zauto);
440 Box::new(self)
441 }
442
443 pub fn zhover_format(mut self, zhover_format: &str) -> Box<Self> {
444 self.zhover_format = Some(zhover_format.to_string());
445 Box::new(self)
446 }
447
448 pub fn zmax(mut self, zmax: Z) -> Box<Self> {
449 self.zmax = Some(zmax);
450 Box::new(self)
451 }
452
453 pub fn zmid(mut self, zmid: Z) -> Box<Self> {
454 self.zmid = Some(zmid);
455 Box::new(self)
456 }
457
458 pub fn zmin(mut self, zmin: Z) -> Box<Self> {
459 self.zmin = Some(zmin);
460 Box::new(self)
461 }
462}
463
464impl<X, Y, Z> Trace for Contour<X, Y, Z>
465where
466 X: Serialize + Clone,
467 Y: Serialize + Clone,
468 Z: Serialize + Clone,
469{
470 fn to_json(&self) -> String {
471 serde_json::to_string(self).unwrap()
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 use serde_json::{json, to_value};
478
479 use super::*;
480 use crate::common::ColorScalePalette;
481
482 #[test]
483 #[rustfmt::skip]
484 fn test_serialize_contours_type() {
485 assert_eq!(to_value(ContoursType::Levels).unwrap(), json!("levels"));
486 assert_eq!(to_value(ContoursType::Constraint).unwrap(), json!("constraint"));
487 }
488
489 #[test]
490 fn test_serialize_coloring() {
491 assert_eq!(to_value(Coloring::Fill).unwrap(), json!("fill"));
492 assert_eq!(to_value(Coloring::HeatMap).unwrap(), json!("heatmap"));
493 assert_eq!(to_value(Coloring::Lines).unwrap(), json!("lines"));
494 assert_eq!(to_value(Coloring::None).unwrap(), json!("none"));
495 }
496
497 #[test]
498 #[rustfmt::skip]
499 fn test_serialize_operation() {
500 assert_eq!(to_value(Operation::Equals).unwrap(), json!("="));
501 assert_eq!(to_value(Operation::LessThan).unwrap(), json!("<"));
502 assert_eq!(to_value(Operation::LessThanOrEqual).unwrap(), json!("<="));
503 assert_eq!(to_value(Operation::GreaterThan).unwrap(), json!(">"));
504 assert_eq!(to_value(Operation::GreaterThanOrEqual).unwrap(), json!(">="));
505 assert_eq!(to_value(Operation::Inside).unwrap(), json!("[]"));
506 assert_eq!(to_value(Operation::Outside).unwrap(), json!("]["));
507 }
508
509 #[test]
510 fn test_serialize_default_contours() {
511 let contours = Contours::new();
512 let expected = json!({});
513
514 assert_eq!(to_value(contours).unwrap(), expected);
515 }
516 #[test]
517 fn test_serialize_contours() {
518 let contours = Contours::new()
519 .type_(ContoursType::Levels)
520 .start(0.0)
521 .end(10.0)
522 .size(5)
523 .coloring(Coloring::HeatMap)
524 .show_lines(true)
525 .show_labels(false)
526 .label_font(Font::new())
527 .label_format("fmt")
528 .operation(Operation::GreaterThan)
529 .value(6.0);
530
531 let expected = json!({
532 "type": "levels",
533 "start": 0.0,
534 "end": 10.0,
535 "size": 5,
536 "coloring": "heatmap",
537 "showlines": true,
538 "showlabels": false,
539 "labelformat": "fmt",
540 "labelfont": {},
541 "operation": ">",
542 "value": 6.0
543 });
544
545 assert_eq!(to_value(contours).unwrap(), expected)
546 }
547
548 #[test]
549 fn test_serialize_default_contour() {
550 let trace: Contour<f64, f64, f64> = Contour::default();
551 let expected = json!({"type": "contour"}).to_string();
552
553 assert_eq!(trace.to_json(), expected);
554 }
555
556 #[test]
557 fn test_new_z_contour() {
558 let trace = Contour::new_z(vec![1.0]);
559 let expected = json!({
560 "type": "contour",
561 "z": [1.0]
562 });
563
564 assert_eq!(to_value(trace).unwrap(), expected);
565 }
566
567 #[test]
568 fn test_serialize_contour() {
569 let trace = Contour::new(vec![0., 1.], vec![2., 3.], vec![4., 5.])
570 .auto_color_scale(true)
571 .auto_contour(true)
572 .color_bar(ColorBar::new())
573 .color_scale(ColorScale::Palette(ColorScalePalette::Blackbody))
574 .connect_gaps(true)
575 .contours(Contours::new())
576 .dx(1.)
577 .dy(1.)
578 .fill_color("#123456")
579 .hover_info(HoverInfo::XAndYAndZ)
580 .hover_label(Label::new())
581 .hover_on_gaps(false)
582 .hover_template("ok {1}")
583 .hover_template_array(vec!["ok {1}", "ok {2}"])
584 .hover_text(vec!["p3", "p4"])
585 .legend_group("group_1")
586 .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
587 .line(Line::new())
588 .n_contours(5)
589 .name("contour trace")
590 .opacity(0.6)
591 .reverse_scale(true)
592 .show_scale(true)
593 .show_legend(false)
594 .text(vec!["p1", "p2"])
595 .transpose(true)
596 .visible(Visible::True)
597 .x(vec![0.0, 1.0])
598 .x_axis("x0")
599 .x_calendar(Calendar::Ethiopian)
600 .x0(0.)
601 .y(vec![2.0, 3.0])
602 .y_axis("y0")
603 .y_calendar(Calendar::Gregorian)
604 .y0(0.)
605 .zauto(false)
606 .zhover_format("fmt")
607 .zmax(10.)
608 .zmid(5.)
609 .zmin(0.);
610
611 let expected = json!({
612 "type": "contour",
613 "x": [0.0, 1.0],
614 "y": [2.0, 3.0],
615 "z": [4.0, 5.0],
616 "x0": 0.0,
617 "dx": 1.0,
618 "y0": 0.0,
619 "dy": 1.0,
620 "name": "contour trace",
621 "visible": true,
622 "showlegend": false,
623 "legendgroup": "group_1",
624 "legendgrouptitle": {"text": "Legend Group Title"},
625 "opacity": 0.6,
626 "text": ["p1", "p2"],
627 "hovertext": ["p3", "p4"],
628 "hoverinfo": "x+y+z",
629 "hovertemplate": ["ok {1}", "ok {2}"],
630 "xaxis": "x0",
631 "yaxis": "y0",
632 "line": {},
633 "colorbar": {},
634 "autocolorscale": true,
635 "colorscale": "Blackbody",
636 "showscale": true,
637 "reversescale": true,
638 "zauto": false,
639 "zhoverformat": "fmt",
640 "zmax": 10.0,
641 "zmid": 5.0,
642 "zmin": 0.0,
643 "autocontour": true,
644 "connectgaps": true,
645 "contours": {},
646 "fillcolor": "#123456",
647 "hoverlabel": {},
648 "hoverongaps": false,
649 "ncontours": 5,
650 "transpose": true,
651 "xcalendar": "ethiopian",
652 "ycalendar": "gregorian"
653 });
654
655 assert_eq!(to_value(trace).unwrap(), expected);
656 }
657}