1 | use super::{BackendCoordAndZ, Drawable, PointCollection}; |
2 | use crate::style::ShapeStyle; |
3 | use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; |
4 | |
5 | /** |
6 | Represents a cuboid, a six-faced solid. |
7 | |
8 | # Examples |
9 | |
10 | ``` |
11 | use plotters::prelude::*; |
12 | let drawing_area = SVGBackend::new("cuboid.svg" , (300, 200)).into_drawing_area(); |
13 | drawing_area.fill(&WHITE).unwrap(); |
14 | let mut chart_builder = ChartBuilder::on(&drawing_area); |
15 | let mut chart_context = chart_builder.margin(20).build_cartesian_3d(0.0..3.5, 0.0..2.5, 0.0..1.5).unwrap(); |
16 | chart_context.configure_axes().x_labels(4).y_labels(3).z_labels(2).draw().unwrap(); |
17 | let cubiod = Cubiod::new([(0.,0.,0.), (3.,2.,1.)], BLUE.mix(0.2), BLUE); |
18 | chart_context.draw_series(std::iter::once(cubiod)).unwrap(); |
19 | ``` |
20 | |
21 | The result is a semi-transparent cuboid with blue edges: |
22 | |
23 | ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@b6703f7/apidoc/cuboid.svg) |
24 | */ |
25 | pub struct Cubiod<X, Y, Z> { |
26 | face_style: ShapeStyle, |
27 | edge_style: ShapeStyle, |
28 | vert: [(X, Y, Z); 8], |
29 | } |
30 | |
31 | impl<X: Clone, Y: Clone, Z: Clone> Cubiod<X, Y, Z> { |
32 | /** |
33 | Creates a cuboid. |
34 | |
35 | See [`Cubiod`] for more information and examples. |
36 | */ |
37 | #[allow (clippy::redundant_clone)] |
38 | pub fn new<FS: Into<ShapeStyle>, ES: Into<ShapeStyle>>( |
39 | [(x0, y0, z0), (x1, y1, z1)]: [(X, Y, Z); 2], |
40 | face_style: FS, |
41 | edge_style: ES, |
42 | ) -> Self { |
43 | Self { |
44 | face_style: face_style.into(), |
45 | edge_style: edge_style.into(), |
46 | vert: [ |
47 | (x0.clone(), y0.clone(), z0.clone()), |
48 | (x0.clone(), y0.clone(), z1.clone()), |
49 | (x0.clone(), y1.clone(), z0.clone()), |
50 | (x0.clone(), y1.clone(), z1.clone()), |
51 | (x1.clone(), y0.clone(), z0.clone()), |
52 | (x1.clone(), y0.clone(), z1.clone()), |
53 | (x1.clone(), y1.clone(), z0.clone()), |
54 | (x1.clone(), y1.clone(), z1.clone()), |
55 | ], |
56 | } |
57 | } |
58 | } |
59 | |
60 | impl<'a, X: 'a, Y: 'a, Z: 'a> PointCollection<'a, (X, Y, Z), BackendCoordAndZ> |
61 | for &'a Cubiod<X, Y, Z> |
62 | { |
63 | type Point = &'a (X, Y, Z); |
64 | type IntoIter = &'a [(X, Y, Z)]; |
65 | fn point_iter(self) -> Self::IntoIter { |
66 | &self.vert |
67 | } |
68 | } |
69 | |
70 | impl<X, Y, Z, DB: DrawingBackend> Drawable<DB, BackendCoordAndZ> for Cubiod<X, Y, Z> { |
71 | fn draw<I: Iterator<Item = (BackendCoord, i32)>>( |
72 | &self, |
73 | points: I, |
74 | backend: &mut DB, |
75 | _: (u32, u32), |
76 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
77 | let vert: Vec<_> = points.collect(); |
78 | let mut polygon = vec![]; |
79 | for mask in [1, 2, 4].iter().cloned() { |
80 | let mask_a = if mask == 4 { 1 } else { mask * 2 }; |
81 | let mask_b = if mask == 1 { 4 } else { mask / 2 }; |
82 | let a = 0; |
83 | let b = a | mask_a; |
84 | let c = a | mask_a | mask_b; |
85 | let d = a | mask_b; |
86 | polygon.push([vert[a], vert[b], vert[c], vert[d]]); |
87 | polygon.push([ |
88 | vert[a | mask], |
89 | vert[b | mask], |
90 | vert[c | mask], |
91 | vert[d | mask], |
92 | ]); |
93 | } |
94 | polygon.sort_by_cached_key(|t| std::cmp::Reverse(t[0].1 + t[1].1 + t[2].1 + t[3].1)); |
95 | |
96 | for p in polygon { |
97 | backend.fill_polygon(p.iter().map(|(coord, _)| *coord), &self.face_style)?; |
98 | backend.draw_path( |
99 | p.iter() |
100 | .map(|(coord, _)| *coord) |
101 | .chain(std::iter::once(p[0].0)), |
102 | &self.edge_style, |
103 | )?; |
104 | } |
105 | |
106 | Ok(()) |
107 | } |
108 | } |
109 | |