1use plotly_derive::FieldSetter;
2use serde::Serialize;
3
4use crate::color::Color;
5use crate::layout::{Annotation, AspectMode, Axis};
6
7#[serde_with::skip_serializing_none]
8#[derive(Serialize, Debug, Clone, FieldSetter)]
9pub struct AspectRatio {
13 x: Option<f64>,
14 y: Option<f64>,
15 z: Option<f64>,
16}
17
18impl AspectRatio {
19 pub fn new() -> Self {
20 AspectRatio {
21 x: Some(1.0),
22 y: Some(1.0),
23 z: Some(1.0),
24 }
25 }
26}
27
28impl From<(f64, f64, f64)> for AspectRatio {
29 fn from((x, y, z): (f64, f64, f64)) -> Self {
30 AspectRatio {
31 x: Some(x),
32 y: Some(y),
33 z: Some(z),
34 }
35 }
36}
37
38#[serde_with::skip_serializing_none]
39#[derive(Serialize, Debug, Clone, FieldSetter)]
40pub struct Camera {
43 center: Option<CameraCenter>,
44 eye: Option<Eye>,
45 up: Option<Up>,
46 projection: Option<Projection>,
47}
48
49impl Camera {
50 pub fn new() -> Self {
51 Default::default()
52 }
53}
54
55#[serde_with::skip_serializing_none]
56#[derive(Serialize, Debug, Clone, FieldSetter)]
57pub struct CameraCenter {
62 x: Option<f64>,
63 y: Option<f64>,
64 z: Option<f64>,
65}
66
67impl CameraCenter {
68 pub fn new() -> Self {
69 CameraCenter {
70 x: Some(0.0),
71 y: Some(0.0),
72 z: Some(0.0),
73 }
74 }
75}
76
77impl From<(f64, f64, f64)> for CameraCenter {
78 fn from((x, y, z): (f64, f64, f64)) -> Self {
79 CameraCenter {
80 x: Some(x),
81 y: Some(y),
82 z: Some(z),
83 }
84 }
85}
86
87#[serde_with::skip_serializing_none]
88#[derive(Serialize, Debug, Clone, FieldSetter)]
89pub struct Eye {
93 x: Option<f64>,
94 y: Option<f64>,
95 z: Option<f64>,
96}
97
98impl Eye {
99 pub fn new() -> Self {
100 Eye {
101 x: Some(1.25),
102 y: Some(1.25),
103 z: Some(1.25),
104 }
105 }
106}
107
108impl From<(f64, f64, f64)> for Eye {
109 fn from((x, y, z): (f64, f64, f64)) -> Self {
110 Eye {
111 x: Some(x),
112 y: Some(y),
113 z: Some(z),
114 }
115 }
116}
117
118#[serde_with::skip_serializing_none]
119#[derive(Serialize, Debug, Clone, FieldSetter)]
120pub struct Up {
124 x: Option<f64>,
125 y: Option<f64>,
126 z: Option<f64>,
127}
128
129impl Up {
130 pub fn new() -> Self {
131 Up {
132 x: Some(0.0),
133 y: Some(0.0),
134 z: Some(1.0),
135 }
136 }
137}
138
139impl From<(f64, f64, f64)> for Up {
140 fn from((x, y, z): (f64, f64, f64)) -> Self {
141 Up {
142 x: Some(x),
143 y: Some(y),
144 z: Some(z),
145 }
146 }
147}
148
149#[derive(Default, Serialize, Debug, Clone)]
153pub enum ProjectionType {
154 #[default]
155 #[serde(rename = "perspective")]
156 Perspective,
157 #[serde(rename = "orthographic")]
158 Orthographic,
159 #[serde(rename = "airy")]
160 Airy,
161 #[serde(rename = "aitoff")]
162 Aitoff,
163 #[serde(rename = "albers")]
164 Albers,
165 #[serde(rename = "albers usa")]
166 AlbersUsa,
167 #[serde(rename = "august")]
168 August,
169 #[serde(rename = "azimuthal equal area")]
170 AzimuthalEqualArea,
171 #[serde(rename = "azimuthal equidistant")]
172 AzimuthalEquidistant,
173 #[serde(rename = "baker")]
174 Baker,
175 #[serde(rename = "bertin1953")]
176 Bertin1953,
177 #[serde(rename = "boggs")]
178 Boggs,
179 #[serde(rename = "bonne")]
180 Bonne,
181 #[serde(rename = "bottomley")]
182 Bottomley,
183 #[serde(rename = "bromley")]
184 Bromley,
185 #[serde(rename = "collignon")]
186 Collignon,
187 #[serde(rename = "conic conformal")]
188 ConicConformal,
189 #[serde(rename = "conic equal area")]
190 ConicEqualArea,
191 #[serde(rename = "conic equidistant")]
192 ConicEquidistant,
193 #[serde(rename = "craig")]
194 Craig,
195 #[serde(rename = "craster")]
196 Craster,
197 #[serde(rename = "cylindrical equal area")]
198 CylindricalEqualArea,
199 #[serde(rename = "cylindrical stereographic")]
200 CylindricalStereographic,
201 #[serde(rename = "eckert1")]
202 Eckert1,
203 #[serde(rename = "eckert2")]
204 Eckert2,
205 #[serde(rename = "eckert3")]
206 Eckert3,
207 #[serde(rename = "eckert4")]
208 Eckert4,
209 #[serde(rename = "eckert5")]
210 Eckert5,
211 #[serde(rename = "eckert6")]
212 Eckert6,
213 #[serde(rename = "eisenlohr")]
214 Eisenlohr,
215 #[serde(rename = "equal earth")]
216 EqualEarth,
217 #[serde(rename = "equirectangular")]
218 Equirectangular,
219 #[serde(rename = "fahey")]
220 Fahey,
221 #[serde(rename = "foucaut")]
222 Foucaut,
223 #[serde(rename = "foucaut sinusoidal")]
224 FoucautSinusoidal,
225 #[serde(rename = "ginzburg4")]
226 Ginzburg4,
227 #[serde(rename = "ginzburg5")]
228 Ginzburg5,
229 #[serde(rename = "ginzburg6")]
230 Ginzburg6,
231 #[serde(rename = "ginzburg8")]
232 Ginzburg8,
233 #[serde(rename = "ginzburg9")]
234 Ginzburg9,
235 #[serde(rename = "gnomonic")]
236 Gnomonic,
237 #[serde(rename = "gringorten")]
238 Gringorten,
239 #[serde(rename = "gringorten quincuncial")]
240 GringortenQuincuncial,
241 #[serde(rename = "guyou")]
242 Guyou,
243 #[serde(rename = "hammer")]
244 Hammer,
245 #[serde(rename = "hill")]
246 Hill,
247 #[serde(rename = "homolosine")]
248 Homolosine,
249 #[serde(rename = "hufnagel")]
250 Hufnagel,
251 #[serde(rename = "hyperelliptical")]
252 Hyperelliptical,
253 #[serde(rename = "kavrayskiy7")]
254 Kavrayskiy7,
255 #[serde(rename = "lagrange")]
256 Lagrange,
257 #[serde(rename = "larrivee")]
258 Larrivee,
259 #[serde(rename = "laskowski")]
260 Laskowski,
261 #[serde(rename = "loximuthal")]
262 Loximuthal,
263 #[serde(rename = "mercator")]
264 Mercator,
265 #[serde(rename = "miller")]
266 Miller,
267 #[serde(rename = "mollweide")]
268 Mollweide,
269 #[serde(rename = "mt flat polar parabolic")]
270 MtFlatPolarParabolic,
271 #[serde(rename = "mt flat polar quartic")]
272 MtFlatPolarQuartic,
273 #[serde(rename = "mt flat polar sinusoidal")]
274 MtFlatPolarSinusoidal,
275 #[serde(rename = "natural earth")]
276 NaturalEarth,
277 #[serde(rename = "natural earth1")]
278 NaturalEarth1,
279 #[serde(rename = "natural earth2")]
280 NaturalEarth2,
281 #[serde(rename = "nell hammer")]
282 NellHammer,
283 #[serde(rename = "nicolosi")]
284 Nicolosi,
285 #[serde(rename = "patterson")]
286 Patterson,
287 #[serde(rename = "peirce quincuncial")]
288 PeirceQuincuncial,
289 #[serde(rename = "polyconic")]
290 Polyconic,
291 #[serde(rename = "rectangular polyconic")]
292 RectangularPolyconic,
293 #[serde(rename = "robinson")]
294 Robinson,
295 #[serde(rename = "satellite")]
296 Satellite,
297 #[serde(rename = "sinu mollweide")]
298 SinuMollweide,
299 #[serde(rename = "sinusoidal")]
300 Sinusoidal,
301 #[serde(rename = "stereographic")]
302 Stereographic,
303 #[serde(rename = "times")]
304 Times,
305 #[serde(rename = "transverse mercator")]
306 TransverseMercator,
307 #[serde(rename = "van der grinten")]
308 VanDerGrinten,
309 #[serde(rename = "van der grinten2")]
310 VanDerGrinten2,
311 #[serde(rename = "van der grinten3")]
312 VanDerGrinten3,
313 #[serde(rename = "van der grinten4")]
314 VanDerGrinten4,
315 #[serde(rename = "wagner4")]
316 Wagner4,
317 #[serde(rename = "wagner6")]
318 Wagner6,
319 #[serde(rename = "wiechel")]
320 Wiechel,
321 #[serde(rename = "winkel tripel")]
322 WinkelTripel,
323 #[serde(rename = "winkel3")]
324 Winkel3,
325}
326
327impl From<ProjectionType> for Projection {
328 fn from(projection_type: ProjectionType) -> Self {
329 Projection {
330 projection_type: Some(projection_type),
331 rotation: None,
332 }
333 }
334}
335
336#[derive(Serialize, Clone, Debug, FieldSetter)]
338pub struct Rotation {
339 lat: Option<f64>,
341 lon: Option<f64>,
343 roll: Option<f64>,
346}
347
348impl Rotation {
349 pub fn new() -> Self {
350 Default::default()
351 }
352}
353
354#[serde_with::skip_serializing_none]
355#[derive(Serialize, Debug, Clone, FieldSetter)]
356pub struct Projection {
358 #[serde(rename = "type")]
359 projection_type: Option<ProjectionType>,
360 #[serde(rename = "rotation")]
362 rotation: Option<Rotation>,
363}
364
365impl Projection {
366 pub fn new() -> Self {
367 Default::default()
368 }
369}
370
371#[derive(Debug, Clone)]
372pub enum DragMode {
373 Zoom,
374 Pan,
375 Select,
376 Lasso,
377 DrawClosedPath,
378 DrawOpenPath,
379 DrawLine,
380 DrawRect,
381 DrawCircle,
382 Orbit,
383 Turntable,
384 False,
385}
386
387impl Serialize for DragMode {
388 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
389 where
390 S: serde::Serializer,
391 {
392 match *self {
393 Self::Zoom => serializer.serialize_str("zoom"),
394 Self::Pan => serializer.serialize_str("pan"),
395 Self::Select => serializer.serialize_str("select"),
396 Self::Lasso => serializer.serialize_str("lasso"),
397 Self::DrawClosedPath => serializer.serialize_str("drawclosedpath"),
398 Self::DrawOpenPath => serializer.serialize_str("drawopenpath"),
399 Self::DrawLine => serializer.serialize_str("drawline"),
400 Self::DrawRect => serializer.serialize_str("drawrect"),
401 Self::DrawCircle => serializer.serialize_str("drawcircle"),
402 Self::Orbit => serializer.serialize_str("orbit"),
403 Self::Turntable => serializer.serialize_str("turntable"),
404 Self::False => serializer.serialize_bool(false),
405 }
406 }
407}
408
409#[derive(Debug, Clone)]
410pub enum DragMode3D {
412 Zoom,
413 Pan,
414 Turntable,
415 Orbit,
416 False,
417}
418
419impl Serialize for DragMode3D {
420 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
421 where
422 S: serde::Serializer,
423 {
424 match *self {
425 Self::Zoom => serializer.serialize_str("zoom"),
426 Self::Pan => serializer.serialize_str("pan"),
427 Self::Turntable => serializer.serialize_str("turntable"),
428 Self::Orbit => serializer.serialize_str("orbit"),
429 Self::False => serializer.serialize_bool(false),
430 }
431 }
432}
433
434#[derive(Debug, Clone)]
435pub enum HoverMode {
436 X,
437 Y,
438 Closest,
439 False,
440 XUnified,
441 YUnified,
442}
443
444impl Serialize for HoverMode {
445 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
446 where
447 S: serde::Serializer,
448 {
449 match *self {
450 Self::X => serializer.serialize_str("x"),
451 Self::Y => serializer.serialize_str("y"),
452 Self::Closest => serializer.serialize_str("closest"),
453 Self::False => serializer.serialize_bool(false),
454 Self::XUnified => serializer.serialize_str("x unified"),
455 Self::YUnified => serializer.serialize_str("y unified"),
456 }
457 }
458}
459
460#[serde_with::skip_serializing_none]
461#[derive(Serialize, Debug, Clone, FieldSetter)]
462pub struct LayoutScene {
464 #[serde(rename = "bgcolor")]
465 background_color: Option<Box<dyn Color>>,
466 camera: Option<Camera>,
467 #[serde(rename = "aspectmode")]
468 aspect_mode: Option<AspectMode>,
469 #[serde(rename = "aspectratio")]
470 aspect_ratio: Option<AspectRatio>,
471 #[serde(rename = "xaxis")]
472 x_axis: Option<Axis>,
473 #[serde(rename = "yaxis")]
474 y_axis: Option<Axis>,
475 #[serde(rename = "zaxis")]
476 z_axis: Option<Axis>,
477 #[serde(rename = "dragmode")]
478 drag_mode: Option<DragMode3D>,
479 #[serde(rename = "hovermode")]
480 hover_mode: Option<HoverMode>,
481 annotations: Option<Vec<Annotation>>,
482 }
485
486impl LayoutScene {
487 pub fn new() -> Self {
488 Default::default()
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use serde_json::{json, to_value};
495
496 use super::*;
497 use crate::layout::Layout;
498
499 #[test]
500 fn serialize_hover_mode() {
501 assert_eq!(to_value(HoverMode::X).unwrap(), json!("x"));
502 assert_eq!(to_value(HoverMode::Y).unwrap(), json!("y"));
503 assert_eq!(to_value(HoverMode::Closest).unwrap(), json!("closest"));
504 assert_eq!(to_value(HoverMode::False).unwrap(), json!(false));
505 assert_eq!(to_value(HoverMode::XUnified).unwrap(), json!("x unified"));
506 assert_eq!(to_value(HoverMode::YUnified).unwrap(), json!("y unified"));
507 }
508
509 #[test]
510 #[rustfmt::skip]
511 fn serialize_drag_mode() {
512 assert_eq!(to_value(DragMode::Zoom).unwrap(), json!("zoom"));
513 assert_eq!(to_value(DragMode::Pan).unwrap(), json!("pan"));
514 assert_eq!(to_value(DragMode::Select).unwrap(), json!("select"));
515 assert_eq!(to_value(DragMode::Lasso).unwrap(), json!("lasso"));
516 assert_eq!(to_value(DragMode::DrawClosedPath).unwrap(), json!("drawclosedpath"));
517 assert_eq!(to_value(DragMode::DrawOpenPath).unwrap(), json!("drawopenpath"));
518 assert_eq!(to_value(DragMode::DrawLine).unwrap(), json!("drawline"));
519 assert_eq!(to_value(DragMode::DrawRect).unwrap(), json!("drawrect"));
520 assert_eq!(to_value(DragMode::DrawCircle).unwrap(), json!("drawcircle"));
521 assert_eq!(to_value(DragMode::Orbit).unwrap(), json!("orbit"));
522 assert_eq!(to_value(DragMode::Turntable).unwrap(), json!("turntable"));
523 assert_eq!(to_value(DragMode::False).unwrap(), json!(false));
524 }
525
526 #[test]
527 fn serialize_aspect_ratio() {
528 let aspect_ratio = AspectRatio::new();
529
530 let expected = json!({
531 "x": 1.0,
532 "y": 1.0,
533 "z": 1.0,
534 });
535
536 assert_eq!(to_value(aspect_ratio).unwrap(), expected);
537
538 let aspect_ratio = AspectRatio::new().x(1f64).y(2f64).z(3f64);
539
540 let expected = json!({
541 "x": 1.0,
542 "y": 2.0,
543 "z": 3.0,
544 });
545
546 assert_eq!(to_value(aspect_ratio).unwrap(), expected);
547
548 let aspect_ratio: AspectRatio = (1f64, 2f64, 3f64).into();
549
550 assert_eq!(to_value(aspect_ratio).unwrap(), expected);
551 }
552
553 #[test]
554 fn serialize_layout_scene() {
555 let layout = Layout::new().scene(
556 LayoutScene::new()
557 .x_axis(Axis::new())
558 .y_axis(Axis::new())
559 .z_axis(Axis::new())
560 .camera(Camera::new())
561 .aspect_mode(AspectMode::Auto)
562 .hover_mode(HoverMode::Closest)
563 .drag_mode(DragMode3D::Turntable)
564 .background_color("#FFFFFF")
565 .annotations(vec![Annotation::new()]),
566 );
567
568 let expected = json!({
569 "scene": {
570 "xaxis": {},
571 "yaxis": {},
572 "zaxis": {},
573 "camera": {},
574 "aspectmode": "auto",
575 "hovermode": "closest",
576 "dragmode": "turntable",
577 "bgcolor": "#FFFFFF",
578 "annotations": [{}],
579 }
580 });
581
582 assert_eq!(to_value(layout).unwrap(), expected);
583 }
584
585 #[test]
586 fn serialize_eye() {
587 let eye = Eye::new();
588
589 assert_eq!(
590 to_value(eye).unwrap(),
591 json!({
592 "x": 1.25,
593 "y": 1.25,
594 "z": 1.25,
595 })
596 );
597
598 let eye = Eye::new().x(1f64).y(2f64).z(3f64);
599
600 let expected = json!({
601 "x": 1.0,
602 "y": 2.0,
603 "z": 3.0,
604 });
605
606 assert_eq!(to_value(eye).unwrap(), expected);
607
608 let eye: Eye = (1f64, 2f64, 3f64).into();
609
610 assert_eq!(to_value(eye).unwrap(), expected);
611 }
612
613 #[test]
614 fn serialize_projection() {
615 let projection = Projection::new().projection_type(ProjectionType::default());
616 assert_eq!(
617 to_value(projection).unwrap(),
618 json!({"type": "perspective"})
619 );
620
621 let projection: Projection = ProjectionType::Orthographic.into();
622 assert_eq!(
623 to_value(projection).unwrap(),
624 json!({ "type": "orthographic" })
625 );
626
627 let projections = vec![
628 (ProjectionType::Orthographic, "orthographic"),
629 (ProjectionType::Perspective, "perspective"),
630 (ProjectionType::Airy, "airy"),
631 (ProjectionType::Aitoff, "aitoff"),
632 (ProjectionType::Albers, "albers"),
633 (ProjectionType::AlbersUsa, "albers usa"),
634 (ProjectionType::August, "august"),
635 (ProjectionType::AzimuthalEqualArea, "azimuthal equal area"),
636 (
637 ProjectionType::AzimuthalEquidistant,
638 "azimuthal equidistant",
639 ),
640 (ProjectionType::Baker, "baker"),
641 (ProjectionType::Bertin1953, "bertin1953"),
642 (ProjectionType::Boggs, "boggs"),
643 (ProjectionType::Bonne, "bonne"),
644 (ProjectionType::Bottomley, "bottomley"),
645 (ProjectionType::Bromley, "bromley"),
646 (ProjectionType::Collignon, "collignon"),
647 (ProjectionType::ConicConformal, "conic conformal"),
648 (ProjectionType::ConicEqualArea, "conic equal area"),
649 (ProjectionType::ConicEquidistant, "conic equidistant"),
650 (ProjectionType::Craig, "craig"),
651 (ProjectionType::Craster, "craster"),
652 (
653 ProjectionType::CylindricalEqualArea,
654 "cylindrical equal area",
655 ),
656 (
657 ProjectionType::CylindricalStereographic,
658 "cylindrical stereographic",
659 ),
660 (ProjectionType::Eckert1, "eckert1"),
661 (ProjectionType::Eckert2, "eckert2"),
662 (ProjectionType::Eckert3, "eckert3"),
663 (ProjectionType::Eckert4, "eckert4"),
664 (ProjectionType::Eckert5, "eckert5"),
665 (ProjectionType::Eckert6, "eckert6"),
666 (ProjectionType::Eisenlohr, "eisenlohr"),
667 (ProjectionType::EqualEarth, "equal earth"),
668 (ProjectionType::Equirectangular, "equirectangular"),
669 (ProjectionType::Fahey, "fahey"),
670 (ProjectionType::Foucaut, "foucaut"),
671 (ProjectionType::FoucautSinusoidal, "foucaut sinusoidal"),
672 (ProjectionType::Ginzburg4, "ginzburg4"),
673 (ProjectionType::Ginzburg5, "ginzburg5"),
674 (ProjectionType::Ginzburg6, "ginzburg6"),
675 (ProjectionType::Ginzburg8, "ginzburg8"),
676 (ProjectionType::Ginzburg9, "ginzburg9"),
677 (ProjectionType::Gnomonic, "gnomonic"),
678 (ProjectionType::Gringorten, "gringorten"),
679 (
680 ProjectionType::GringortenQuincuncial,
681 "gringorten quincuncial",
682 ),
683 (ProjectionType::Guyou, "guyou"),
684 (ProjectionType::Hammer, "hammer"),
685 (ProjectionType::Hill, "hill"),
686 (ProjectionType::Homolosine, "homolosine"),
687 (ProjectionType::Hufnagel, "hufnagel"),
688 (ProjectionType::Hyperelliptical, "hyperelliptical"),
689 (ProjectionType::Kavrayskiy7, "kavrayskiy7"),
690 (ProjectionType::Lagrange, "lagrange"),
691 (ProjectionType::Larrivee, "larrivee"),
692 (ProjectionType::Laskowski, "laskowski"),
693 (ProjectionType::Loximuthal, "loximuthal"),
694 (ProjectionType::Mercator, "mercator"),
695 (ProjectionType::Miller, "miller"),
696 (ProjectionType::Mollweide, "mollweide"),
697 (
698 ProjectionType::MtFlatPolarParabolic,
699 "mt flat polar parabolic",
700 ),
701 (ProjectionType::MtFlatPolarQuartic, "mt flat polar quartic"),
702 (
703 ProjectionType::MtFlatPolarSinusoidal,
704 "mt flat polar sinusoidal",
705 ),
706 (ProjectionType::NaturalEarth, "natural earth"),
707 (ProjectionType::NaturalEarth1, "natural earth1"),
708 (ProjectionType::NaturalEarth2, "natural earth2"),
709 (ProjectionType::NellHammer, "nell hammer"),
710 (ProjectionType::Nicolosi, "nicolosi"),
711 (ProjectionType::Patterson, "patterson"),
712 (ProjectionType::PeirceQuincuncial, "peirce quincuncial"),
713 (ProjectionType::Polyconic, "polyconic"),
714 (
715 ProjectionType::RectangularPolyconic,
716 "rectangular polyconic",
717 ),
718 (ProjectionType::Robinson, "robinson"),
719 (ProjectionType::Satellite, "satellite"),
720 (ProjectionType::SinuMollweide, "sinu mollweide"),
721 (ProjectionType::Sinusoidal, "sinusoidal"),
722 (ProjectionType::Stereographic, "stereographic"),
723 (ProjectionType::Times, "times"),
724 (ProjectionType::TransverseMercator, "transverse mercator"),
725 (ProjectionType::VanDerGrinten, "van der grinten"),
726 (ProjectionType::VanDerGrinten2, "van der grinten2"),
727 (ProjectionType::VanDerGrinten3, "van der grinten3"),
728 (ProjectionType::VanDerGrinten4, "van der grinten4"),
729 (ProjectionType::Wagner4, "wagner4"),
730 (ProjectionType::Wagner6, "wagner6"),
731 (ProjectionType::Wiechel, "wiechel"),
732 (ProjectionType::WinkelTripel, "winkel tripel"),
733 (ProjectionType::Winkel3, "winkel3"),
734 ];
735 for (variant, name) in projections {
736 let projection = Projection::new().projection_type(variant.clone());
737 assert_eq!(to_value(projection).unwrap(), json!({"type": name}));
738 }
739 }
740
741 #[test]
742 fn serialize_camera_center() {
743 let camera_center = CameraCenter::new();
744
745 let expected = json!({
746 "x": 0.0,
747 "y": 0.0,
748 "z": 0.0,
749 });
750
751 assert_eq!(to_value(camera_center).unwrap(), expected);
752
753 let camera_center = CameraCenter::new().x(1f64).y(2f64).z(3f64);
754
755 let expected = json!({
756 "x": 1.0,
757 "y": 2.0,
758 "z": 3.0,
759 });
760
761 assert_eq!(to_value(camera_center).unwrap(), expected);
762
763 let camera_center: CameraCenter = (1f64, 2f64, 3f64).into();
764
765 assert_eq!(to_value(camera_center).unwrap(), expected);
766 }
767
768 #[test]
769 fn serialize_up() {
770 let up = Up::new();
771
772 let expected = json!({
773 "x": 0.0,
774 "y": 0.0,
775 "z": 1.0,
776 });
777
778 assert_eq!(to_value(up).unwrap(), expected);
779
780 let up = Up::new().x(1f64).y(2f64).z(3f64);
781
782 let expected = json!({
783 "x": 1.0,
784 "y": 2.0,
785 "z": 3.0,
786 });
787
788 assert_eq!(to_value(up).unwrap(), expected);
789
790 let up: Up = (1f64, 2f64, 3f64).into();
791
792 assert_eq!(to_value(up).unwrap(), expected);
793 }
794
795 #[test]
796 fn serialize_projection_with_rotation() {
797 let projection = Projection {
798 projection_type: Some(ProjectionType::Mercator),
799 rotation: Some(Rotation {
800 lat: Some(10.0),
801 lon: Some(20.0),
802 roll: Some(30.0),
803 }),
804 };
805 let expected = json!({
806 "type": "mercator",
807 "rotation": {"lat": 10.0, "lon": 20.0, "roll": 30.0}
808 });
809 assert_eq!(to_value(projection).unwrap(), expected);
810 }
811}