Skip to content

Commit 3615b28

Browse files
committed
Add example and 3d surface series
1 parent 17f57de commit 3615b28

File tree

8 files changed

+243
-62
lines changed

8 files changed

+243
-62
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ default = [
6161
"image",
6262
"deprecated_items", "all_series", "all_elements"
6363
]
64-
all_series = ["area_series", "line_series", "point_series"]
64+
all_series = ["area_series", "line_series", "point_series", "surface_series"]
6565
all_elements = ["errorbar", "candlestick", "boxplot", "histogram"]
6666

6767
# Tier 1 Backends
@@ -80,6 +80,7 @@ histogram = []
8080
area_series = []
8181
line_series = []
8282
point_series = []
83+
surface_series = []
8384

8485
# Font implemnetation
8586
ttf = ["font-kit", "rusttype", "lazy_static"]

examples/3d-plot.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use plotters::prelude::*;
2+
fn main() -> Result<(), Box<dyn std::error::Error>> {
3+
let area = SVGBackend::new("plotters-doc-data/3d-plot.svg", (600, 400)).into_drawing_area();
4+
area.fill(&WHITE)?;
5+
let x_axis = (-3.0..3.0).step(0.1);
6+
let z_axis = (-3.0..3.0).step(0.1);
7+
8+
let mut chart = ChartBuilder::on(&area)
9+
.caption(format!("3D Plot Test"), ("sans", 20))
10+
.build_cartesian_3d(-3.0..3.0, -3.0..3.0, -3.0..3.0)?;
11+
12+
chart.with_projection(|mut pb| {
13+
pb.yaw = 0.5;
14+
pb.scale = 0.9;
15+
pb.into_matrix()
16+
});
17+
18+
chart.configure_axes().draw()?;
19+
20+
let surface = SurfaceSeries::<f64, f64, f64>::new(
21+
x_axis.values(),
22+
z_axis.values(),
23+
|&x, &z| (x * x + z * z).cos(),
24+
&BLUE.mix(0.2),
25+
);
26+
27+
chart
28+
.draw_series(surface)?
29+
.label("Surface")
30+
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE));
31+
32+
chart
33+
.draw_series(LineSeries::new(
34+
(-100..100)
35+
.map(|y| y as f64 / 40.0)
36+
.map(|y| ((y * 10.0).sin(), y, (y * 10.0).cos())),
37+
&BLACK,
38+
))?
39+
.label("Line")
40+
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLACK));
41+
42+
chart
43+
.configure_series_labels()
44+
.border_style(&BLACK)
45+
.draw()?;
46+
Ok(())
47+
}

src/chart/axes3d.rs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,18 @@ where
4646
self.n_labels[0] = n;
4747
self
4848
}
49-
49+
5050
pub fn y_labels(&mut self, n: usize) -> &mut Self {
5151
self.n_labels[1] = n;
5252
self
5353
}
54-
54+
5555
pub fn z_labels(&mut self, n: usize) -> &mut Self {
5656
self.n_labels[2] = n;
5757
self
5858
}
5959

60-
pub fn axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self{
60+
pub fn axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
6161
self.axis_panel_style = style.into();
6262
self
6363
}
@@ -66,12 +66,12 @@ where
6666
self.bold_line_style = style.into();
6767
self
6868
}
69-
69+
7070
pub fn light_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
7171
self.light_line_style = style.into();
7272
self
7373
}
74-
74+
7575
pub fn label_style<S: Into<TextStyle<'b>>>(&mut self, style: S) -> &mut Self {
7676
self.label_style = style.into();
7777
self
@@ -81,12 +81,12 @@ where
8181
self.format_x = f;
8282
self
8383
}
84-
84+
8585
pub fn y_formatter<F: Fn(&Y::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
8686
self.format_y = f;
8787
self
8888
}
89-
89+
9090
pub fn z_formatter<F: Fn(&Z::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
9191
self.format_z = f;
9292
self
@@ -99,7 +99,7 @@ where
9999
Self {
100100
parent_size,
101101
tick_size,
102-
n_labels: [10, 10, 10],
102+
n_labels: [10, 10, 10],
103103
bold_line_style: Into::<ShapeStyle>::into(&BLACK.mix(0.2)),
104104
light_line_style: Into::<ShapeStyle>::into(&TRANSPARENT),
105105
axis_panel_style: Into::<ShapeStyle>::into(&BLACK.mix(0.1)),
@@ -141,26 +141,44 @@ where
141141
for i in 0..3 {
142142
let axis = chart.draw_axis(i, &panels, self.axis_style.clone())?;
143143
let labels: Vec<_> = match i {
144-
0 => kps_bold.x_points.iter().map(|x| {
145-
let x_text = (self.format_x)(x);
146-
let mut p = axis[0].clone();
147-
p[0] = Coord3D::X(x.clone());
148-
(p, x_text)
149-
}).collect(),
150-
1 => kps_bold.y_points.iter().map(|y| {
151-
let y_text = (self.format_y)(y);
152-
let mut p = axis[0].clone();
153-
p[1] = Coord3D::Y(y.clone());
154-
(p, y_text)
155-
}).collect(),
156-
_ => kps_bold.z_points.iter().map(|z| {
157-
let z_text = (self.format_z)(z);
158-
let mut p = axis[0].clone();
159-
p[2] = Coord3D::Z(z.clone());
160-
(p, z_text)
161-
}).collect(),
144+
0 => kps_bold
145+
.x_points
146+
.iter()
147+
.map(|x| {
148+
let x_text = (self.format_x)(x);
149+
let mut p = axis[0].clone();
150+
p[0] = Coord3D::X(x.clone());
151+
(p, x_text)
152+
})
153+
.collect(),
154+
1 => kps_bold
155+
.y_points
156+
.iter()
157+
.map(|y| {
158+
let y_text = (self.format_y)(y);
159+
let mut p = axis[0].clone();
160+
p[1] = Coord3D::Y(y.clone());
161+
(p, y_text)
162+
})
163+
.collect(),
164+
_ => kps_bold
165+
.z_points
166+
.iter()
167+
.map(|z| {
168+
let z_text = (self.format_z)(z);
169+
let mut p = axis[0].clone();
170+
p[2] = Coord3D::Z(z.clone());
171+
(p, z_text)
172+
})
173+
.collect(),
162174
};
163-
chart.draw_axis_ticks(axis, &labels[..], self.tick_size, self.axis_style.clone(), self.label_style.clone())?;
175+
chart.draw_axis_ticks(
176+
axis,
177+
&labels[..],
178+
self.tick_size,
179+
self.axis_style.clone(),
180+
self.label_style.clone(),
181+
)?;
164182
}
165183

166184
Ok(())

src/chart/context.rs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ use super::axes3d::Axes3dStyle;
55
use super::{DualCoordChartContext, MeshStyle, SeriesAnno, SeriesLabelStyle};
66

77
use crate::coord::cartesian::{Cartesian2d, Cartesian3d, MeshLine};
8-
use crate::coord::ranged3d::{ProjectionMatrix, ProjectionMatrixBuilder};
98
use crate::coord::ranged1d::{AsRangedCoord, KeyPointHint, Ranged, ValueFormatter};
9+
use crate::coord::ranged3d::{ProjectionMatrix, ProjectionMatrixBuilder};
1010
use crate::coord::{CoordTranslate, ReverseCoordTranslate, Shift};
1111

1212
use crate::drawing::{DrawingArea, DrawingAreaErrorKind};
13-
use crate::element::{Drawable, PathElement, PointCollection, Polygon, Text, EmptyElement};
13+
use crate::element::{Drawable, EmptyElement, PathElement, PointCollection, Polygon, Text};
1414
use crate::style::text_anchor::{HPos, Pos, VPos};
1515
use crate::style::{ShapeStyle, TextStyle};
1616

@@ -586,12 +586,18 @@ where
586586
}
587587
impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
588588
where
589-
DB: DrawingBackend, {
590-
pub fn with_projection<P: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(&mut self, pf: P) -> &mut Self {
591-
let (actual_x, actual_y) = self.drawing_area.get_pixel_range();
592-
self.drawing_area.as_coord_spec_mut().set_projection(actual_x, actual_y, pf);
593-
self
594-
}
589+
DB: DrawingBackend,
590+
{
591+
pub fn with_projection<P: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
592+
&mut self,
593+
pf: P,
594+
) -> &mut Self {
595+
let (actual_x, actual_y) = self.drawing_area.get_pixel_range();
596+
self.drawing_area
597+
.as_coord_spec_mut()
598+
.set_projection(actual_x, actual_y, pf);
599+
self
600+
}
595601
}
596602

597603
impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
@@ -619,15 +625,26 @@ where
619625
}
620626
pub(super) fn draw_axis_ticks(
621627
&mut self,
622-
axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>;3];2],
623-
labels: &[([Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3], String)],
628+
axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2],
629+
labels: &[(
630+
[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3],
631+
String,
632+
)],
624633
tick_size: i32,
625634
style: ShapeStyle,
626-
font: TextStyle
635+
font: TextStyle,
627636
) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> {
628637
let coord = self.plotting_area().as_coord_spec();
629-
let begin = coord.translate(&Coord3D::build_coord([&axis[0][0], &axis[0][1], &axis[0][2]]));
630-
let end = coord.translate(&Coord3D::build_coord([&axis[1][0], &axis[1][1], &axis[1][2]]));
638+
let begin = coord.translate(&Coord3D::build_coord([
639+
&axis[0][0],
640+
&axis[0][1],
641+
&axis[0][2],
642+
]));
643+
let end = coord.translate(&Coord3D::build_coord([
644+
&axis[1][0],
645+
&axis[1][1],
646+
&axis[1][2],
647+
]));
631648
let axis_dir = (end.0 - begin.0, end.1 - begin.1);
632649
let (x_range, y_range) = self.plotting_area().get_pixel_range();
633650
let x_mid = (x_range.start + x_range.end) / 2;
@@ -648,11 +665,7 @@ where
648665
let x_score = (x_dir.0 * axis_dir.0 + x_dir.1 * axis_dir.1).abs();
649666
let y_score = (y_dir.0 * axis_dir.0 + y_dir.1 * axis_dir.1).abs();
650667

651-
let dir = if x_score < y_score {
652-
x_dir
653-
} else {
654-
y_dir
655-
};
668+
let dir = if x_score < y_score { x_dir } else { y_dir };
656669

657670
for (pos, text) in labels {
658671
let logic_pos = Coord3D::build_coord([&pos[0], &pos[1], &pos[2]]);
@@ -668,8 +681,8 @@ where
668681
font.pos = Pos::new(HPos::Center, VPos::Top);
669682
};
670683
let element = EmptyElement::at(logic_pos)
671-
+ PathElement::new(vec![(0,0), dir], style.clone())
672-
+ Text::new(text.to_string(), (dir.0 * 2 , dir.1 * 2), font.clone());
684+
+ PathElement::new(vec![(0, 0), dir], style.clone())
685+
+ Text::new(text.to_string(), (dir.0 * 2, dir.1 * 2), font.clone());
673686
self.plotting_area().draw(&element)?;
674687
}
675688
Ok(())
@@ -679,7 +692,10 @@ where
679692
idx: usize,
680693
panels: &[[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3],
681694
style: ShapeStyle,
682-
) -> Result<[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>;3];2], DrawingAreaErrorKind<DB::ErrorType>> {
695+
) -> Result<
696+
[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2],
697+
DrawingAreaErrorKind<DB::ErrorType>,
698+
> {
683699
let coord = self.plotting_area().as_coord_spec();
684700
let x_range = coord.logic_x.range();
685701
let y_range = coord.logic_y.range();
@@ -808,20 +824,21 @@ where
808824
n[(idx + 2) % 3] = b[(idx + 2) % 3];
809825

810826
(
811-
vec![
812-
Coord3D::build_coord(a),
813-
Coord3D::build_coord(m),
814-
Coord3D::build_coord(b),
815-
Coord3D::build_coord(n),
816-
],
827+
vec![
828+
Coord3D::build_coord(a),
829+
Coord3D::build_coord(m),
830+
Coord3D::build_coord(b),
831+
Coord3D::build_coord(n),
832+
],
817833
a,
818834
b,
819835
)
820836
};
821-
self.plotting_area().draw(&Polygon::new(panel.clone(), panel_style.clone()))?;
837+
self.plotting_area()
838+
.draw(&Polygon::new(panel.clone(), panel_style.clone()))?;
822839
panel.push(panel[0].clone());
823-
self.plotting_area().draw(&PathElement::new(panel, bold_grid_style.clone()))?;
824-
840+
self.plotting_area()
841+
.draw(&PathElement::new(panel, bold_grid_style.clone()))?;
825842

826843
for (kps, style) in vec![
827844
(light_points, light_grid_style),

src/coord/ranged3d/cartesian3d.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
1717
fn compute_default_size(actual_x: Range<i32>, actual_y: Range<i32>) -> i32 {
1818
(actual_x.end - actual_x.start).min(actual_y.end - actual_y.start) * 4 / 5
1919
}
20-
fn create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(actual_x: Range<i32>, actual_y: Range<i32>, f: F) -> ProjectionMatrix {
20+
fn create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
21+
actual_x: Range<i32>,
22+
actual_y: Range<i32>,
23+
f: F,
24+
) -> ProjectionMatrix {
2125
let default_size = Self::compute_default_size(actual_x.clone(), actual_y.clone());
2226
let center_3d = (default_size / 2, default_size / 2, default_size / 2);
2327
let center_2d = (
@@ -28,7 +32,12 @@ impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
2832
pb.set_pivot(center_3d, center_2d);
2933
f(pb)
3034
}
31-
pub fn with_projection<SX: Into<X>, SY: Into<Y>, SZ: Into<Z>, F: FnOnce(ProjectionMatrixBuilder)-> ProjectionMatrix>(
35+
pub fn with_projection<
36+
SX: Into<X>,
37+
SY: Into<Y>,
38+
SZ: Into<Z>,
39+
F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix,
40+
>(
3241
logic_x: SX,
3342
logic_y: SY,
3443
logic_z: SZ,
@@ -44,7 +53,12 @@ impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
4453
projection: Self::create_projection(actual_x, actual_y, build_projection_matrix),
4554
}
4655
}
47-
pub fn set_projection<F:FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(&mut self, actual_x: Range<i32>, actual_y: Range<i32>, f: F) -> &mut Self {
56+
pub fn set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
57+
&mut self,
58+
actual_x: Range<i32>,
59+
actual_y: Range<i32>,
60+
f: F,
61+
) -> &mut Self {
4862
self.projection = Self::create_projection(actual_x, actual_y, f);
4963
self
5064
}
@@ -55,7 +69,9 @@ impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
5569
logic_z: SZ,
5670
(actual_x, actual_y): (Range<i32>, Range<i32>),
5771
) -> Self {
58-
Self::with_projection(logic_x, logic_y, logic_z, (actual_x, actual_y), |pb| pb.into_matrix())
72+
Self::with_projection(logic_x, logic_y, logic_z, (actual_x, actual_y), |pb| {
73+
pb.into_matrix()
74+
})
5975
}
6076
pub fn projection(&self) -> &ProjectionMatrix {
6177
&self.projection

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,8 @@ pub mod prelude {
738738
pub use crate::series::LineSeries;
739739
#[cfg(feature = "point_series")]
740740
pub use crate::series::PointSeries;
741+
#[cfg(feature = "surface_series")]
742+
pub use crate::series::SurfaceSeries;
741743

742744
// Styles
743745
pub use crate::style::{

0 commit comments

Comments
 (0)