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<f64>,
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(
343 mut self,
344 legend_group_title: impl Into<LegendGroupTitle>,
345 ) -> Box<Self> {
346 self.legend_group_title = Some(legend_group_title.into());
347 Box::new(self)
348 }
349
350 pub fn line(mut self, line: Line) -> Box<Self> {
351 self.line = Some(line);
352 Box::new(self)
353 }
354
355 pub fn name(mut self, name: &str) -> Box<Self> {
356 self.name = Some(name.to_string());
357 Box::new(self)
358 }
359
360 pub fn n_contours(mut self, n_contours: usize) -> Box<Self> {
361 self.n_contours = Some(n_contours);
362 Box::new(self)
363 }
364
365 pub fn opacity(mut self, opacity: f64) -> Box<Self> {
366 self.opacity = Some(opacity);
367 Box::new(self)
368 }
369
370 pub fn reverse_scale(mut self, reverse_scale: bool) -> Box<Self> {
371 self.reverse_scale = Some(reverse_scale);
372 Box::new(self)
373 }
374
375 pub fn show_legend(mut self, show_legend: bool) -> Box<Self> {
376 self.show_legend = Some(show_legend);
377 Box::new(self)
378 }
379
380 pub fn show_scale(mut self, show_scale: bool) -> Box<Self> {
381 self.show_scale = Some(show_scale);
382 Box::new(self)
383 }
384
385 pub fn text<S: AsRef<str>>(mut self, text: Vec<S>) -> Box<Self> {
386 let text = private::owned_string_vector(text);
387 self.text = Some(text);
388 Box::new(self)
389 }
390
391 pub fn transpose(mut self, transpose: bool) -> Box<Self> {
392 self.transpose = Some(transpose);
393 Box::new(self)
394 }
395
396 pub fn visible(mut self, visible: Visible) -> Box<Self> {
397 self.visible = Some(visible);
398 Box::new(self)
399 }
400
401 pub fn x(mut self, x: Vec<X>) -> Box<Self> {
402 self.x = Some(x);
403 Box::new(self)
404 }
405
406 pub fn x_axis(mut self, axis: &str) -> Box<Self> {
407 self.x_axis = Some(axis.to_string());
408 Box::new(self)
409 }
410
411 pub fn x_calendar(mut self, x_calendar: Calendar) -> Box<Self> {
412 self.x_calendar = Some(x_calendar);
413 Box::new(self)
414 }
415
416 pub fn x0(mut self, x0: X) -> Box<Self> {
417 self.x0 = Some(x0);
418 Box::new(self)
419 }
420
421 pub fn y_axis(mut self, axis: &str) -> Box<Self> {
422 self.y_axis = Some(axis.to_string());
423 Box::new(self)
424 }
425
426 pub fn y(mut self, y: Vec<Y>) -> Box<Self> {
427 self.y = Some(y);
428 Box::new(self)
429 }
430
431 pub fn y_calendar(mut self, y_calendar: Calendar) -> Box<Self> {
432 self.y_calendar = Some(y_calendar);
433 Box::new(self)
434 }
435
436 pub fn y0(mut self, y0: Y) -> Box<Self> {
437 self.y0 = Some(y0);
438 Box::new(self)
439 }
440
441 pub fn zauto(mut self, zauto: bool) -> Box<Self> {
442 self.zauto = Some(zauto);
443 Box::new(self)
444 }
445
446 pub fn zhover_format(mut self, zhover_format: &str) -> Box<Self> {
447 self.zhover_format = Some(zhover_format.to_string());
448 Box::new(self)
449 }
450
451 pub fn zmax(mut self, zmax: Z) -> Box<Self> {
452 self.zmax = Some(zmax);
453 Box::new(self)
454 }
455
456 pub fn zmid(mut self, zmid: Z) -> Box<Self> {
457 self.zmid = Some(zmid);
458 Box::new(self)
459 }
460
461 pub fn zmin(mut self, zmin: Z) -> Box<Self> {
462 self.zmin = Some(zmin);
463 Box::new(self)
464 }
465}
466
467impl<X, Y, Z> Trace for Contour<X, Y, Z>
468where
469 X: Serialize + Clone,
470 Y: Serialize + Clone,
471 Z: Serialize + Clone,
472{
473 fn to_json(&self) -> String {
474 serde_json::to_string(self).unwrap()
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use serde_json::{json, to_value};
481
482 use super::*;
483 use crate::common::ColorScalePalette;
484
485 #[test]
486 #[rustfmt::skip]
487 fn serialize_contours_type() {
488 assert_eq!(to_value(ContoursType::Levels).unwrap(), json!("levels"));
489 assert_eq!(to_value(ContoursType::Constraint).unwrap(), json!("constraint"));
490 }
491
492 #[test]
493 fn serialize_coloring() {
494 assert_eq!(to_value(Coloring::Fill).unwrap(), json!("fill"));
495 assert_eq!(to_value(Coloring::HeatMap).unwrap(), json!("heatmap"));
496 assert_eq!(to_value(Coloring::Lines).unwrap(), json!("lines"));
497 assert_eq!(to_value(Coloring::None).unwrap(), json!("none"));
498 }
499
500 #[test]
501 #[rustfmt::skip]
502 fn serialize_operation() {
503 assert_eq!(to_value(Operation::Equals).unwrap(), json!("="));
504 assert_eq!(to_value(Operation::LessThan).unwrap(), json!("<"));
505 assert_eq!(to_value(Operation::LessThanOrEqual).unwrap(), json!("<="));
506 assert_eq!(to_value(Operation::GreaterThan).unwrap(), json!(">"));
507 assert_eq!(to_value(Operation::GreaterThanOrEqual).unwrap(), json!(">="));
508 assert_eq!(to_value(Operation::Inside).unwrap(), json!("[]"));
509 assert_eq!(to_value(Operation::Outside).unwrap(), json!("]["));
510 }
511
512 #[test]
513 fn serialize_default_contours() {
514 let contours = Contours::new();
515 let expected = json!({});
516
517 assert_eq!(to_value(contours).unwrap(), expected);
518 }
519 #[test]
520 fn serialize_contours() {
521 let contours = Contours::new()
522 .type_(ContoursType::Levels)
523 .start(0.0)
524 .end(10.0)
525 .size(5.0)
526 .coloring(Coloring::HeatMap)
527 .show_lines(true)
528 .show_labels(false)
529 .label_font(Font::new())
530 .label_format("fmt")
531 .operation(Operation::GreaterThan)
532 .value(6.0);
533
534 let expected = json!({
535 "type": "levels",
536 "start": 0.0,
537 "end": 10.0,
538 "size": 5.0,
539 "coloring": "heatmap",
540 "showlines": true,
541 "showlabels": false,
542 "labelformat": "fmt",
543 "labelfont": {},
544 "operation": ">",
545 "value": 6.0
546 });
547
548 assert_eq!(to_value(contours).unwrap(), expected)
549 }
550
551 #[test]
552 fn serialize_default_contour() {
553 let trace: Contour<f64, f64, f64> = Contour::default();
554 let expected = json!({"type": "contour"}).to_string();
555
556 assert_eq!(trace.to_json(), expected);
557 }
558
559 #[test]
560 fn new_z_contour() {
561 let trace = Contour::new_z(vec![1.0]);
562 let expected = json!({
563 "type": "contour",
564 "z": [1.0]
565 });
566
567 assert_eq!(to_value(trace).unwrap(), expected);
568 }
569
570 #[test]
571 fn serialize_contour() {
572 let trace = Contour::new(vec![0., 1.], vec![2., 3.], vec![4., 5.])
573 .auto_color_scale(true)
574 .auto_contour(true)
575 .color_bar(ColorBar::new())
576 .color_scale(ColorScale::Palette(ColorScalePalette::Blackbody))
577 .connect_gaps(true)
578 .contours(Contours::new())
579 .dx(1.)
580 .dy(1.)
581 .fill_color("#123456")
582 .hover_info(HoverInfo::XAndYAndZ)
583 .hover_label(Label::new())
584 .hover_on_gaps(false)
585 .hover_template("ok {1}")
586 .hover_template_array(vec!["ok {1}", "ok {2}"])
587 .hover_text(vec!["p3", "p4"])
588 .legend_group("group_1")
589 .legend_group_title("Legend Group Title")
590 .line(Line::new())
591 .n_contours(5)
592 .name("contour trace")
593 .opacity(0.6)
594 .reverse_scale(true)
595 .show_scale(true)
596 .show_legend(false)
597 .text(vec!["p1", "p2"])
598 .transpose(true)
599 .visible(Visible::True)
600 .x(vec![0.0, 1.0])
601 .x_axis("x0")
602 .x_calendar(Calendar::Ethiopian)
603 .x0(0.)
604 .y(vec![2.0, 3.0])
605 .y_axis("y0")
606 .y_calendar(Calendar::Gregorian)
607 .y0(0.)
608 .zauto(false)
609 .zhover_format("fmt")
610 .zmax(10.)
611 .zmid(5.)
612 .zmin(0.);
613
614 let expected = json!({
615 "type": "contour",
616 "x": [0.0, 1.0],
617 "y": [2.0, 3.0],
618 "z": [4.0, 5.0],
619 "x0": 0.0,
620 "dx": 1.0,
621 "y0": 0.0,
622 "dy": 1.0,
623 "name": "contour trace",
624 "visible": true,
625 "showlegend": false,
626 "legendgroup": "group_1",
627 "legendgrouptitle": {"text": "Legend Group Title"},
628 "opacity": 0.6,
629 "text": ["p1", "p2"],
630 "hovertext": ["p3", "p4"],
631 "hoverinfo": "x+y+z",
632 "hovertemplate": ["ok {1}", "ok {2}"],
633 "xaxis": "x0",
634 "yaxis": "y0",
635 "line": {},
636 "colorbar": {},
637 "autocolorscale": true,
638 "colorscale": "Blackbody",
639 "showscale": true,
640 "reversescale": true,
641 "zauto": false,
642 "zhoverformat": "fmt",
643 "zmax": 10.0,
644 "zmid": 5.0,
645 "zmin": 0.0,
646 "autocontour": true,
647 "connectgaps": true,
648 "contours": {},
649 "fillcolor": "#123456",
650 "hoverlabel": {},
651 "hoverongaps": false,
652 "ncontours": 5,
653 "transpose": true,
654 "xcalendar": "ethiopian",
655 "ycalendar": "gregorian"
656 });
657
658 assert_eq!(to_value(trace).unwrap(), expected);
659 }
660}