blob: 33d7de39c5a4503750d44e9ccfb64c45339f7e22 [file] [log] [blame]
use std::marker::PhantomData;
use super::ChartContext;
use crate::coord::cartesian::Cartesian3d;
use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter};
use crate::style::colors::{BLACK, TRANSPARENT};
use crate::style::Color;
use crate::style::{AsRelative, ShapeStyle, SizeDesc, TextStyle};
use super::Coord3D;
use crate::drawing::DrawingAreaErrorKind;
use plotters_backend::DrawingBackend;
/**
Implements 3D plot axes configurations.
The best way to use this struct is by way of the [`configure_axes()`] function.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub struct Axes3dStyle<'a, 'b, X: Ranged, Y: Ranged, Z: Ranged, DB: DrawingBackend> {
pub(super) parent_size: (u32, u32),
pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>>,
pub(super) tick_size: i32,
pub(super) light_lines_limit: [usize; 3],
pub(super) n_labels: [usize; 3],
pub(super) bold_line_style: ShapeStyle,
pub(super) light_line_style: ShapeStyle,
pub(super) axis_panel_style: ShapeStyle,
pub(super) axis_style: ShapeStyle,
pub(super) label_style: TextStyle<'b>,
pub(super) format_x: &'b dyn Fn(&X::ValueType) -> String,
pub(super) format_y: &'b dyn Fn(&Y::ValueType) -> String,
pub(super) format_z: &'b dyn Fn(&Z::ValueType) -> String,
_phantom: PhantomData<&'a (X, Y, Z)>,
}
impl<'a, 'b, X, Y, Z, XT, YT, ZT, DB> Axes3dStyle<'a, 'b, X, Y, Z, DB>
where
X: Ranged<ValueType = XT> + ValueFormatter<XT>,
Y: Ranged<ValueType = YT> + ValueFormatter<YT>,
Z: Ranged<ValueType = ZT> + ValueFormatter<ZT>,
DB: DrawingBackend,
{
/**
Set the size of the tick marks.
- `value` Desired tick mark size, in pixels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn tick_size<Size: SizeDesc>(&mut self, size: Size) -> &mut Self {
let actual_size = size.in_pixels(&self.parent_size);
self.tick_size = actual_size;
self
}
/**
Set the maximum number of divisions for the minor grid in the X axis.
- `value`: Maximum desired divisions between two consecutive X labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn x_max_light_lines(&mut self, value: usize) -> &mut Self {
self.light_lines_limit[0] = value;
self
}
/**
Set the maximum number of divisions for the minor grid in the Y axis.
- `value`: Maximum desired divisions between two consecutive Y labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn y_max_light_lines(&mut self, value: usize) -> &mut Self {
self.light_lines_limit[1] = value;
self
}
/**
Set the maximum number of divisions for the minor grid in the Z axis.
- `value`: Maximum desired divisions between two consecutive Z labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn z_max_light_lines(&mut self, value: usize) -> &mut Self {
self.light_lines_limit[2] = value;
self
}
/**
Set the maximum number of divisions for the minor grid.
- `value`: Maximum desired divisions between two consecutive labels in X, Y, and Z.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn max_light_lines(&mut self, value: usize) -> &mut Self {
self.light_lines_limit[0] = value;
self.light_lines_limit[1] = value;
self.light_lines_limit[2] = value;
self
}
/**
Set the number of labels on the X axes.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn x_labels(&mut self, n: usize) -> &mut Self {
self.n_labels[0] = n;
self
}
/**
Set the number of labels on the Y axes.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn y_labels(&mut self, n: usize) -> &mut Self {
self.n_labels[1] = n;
self
}
/**
Set the number of labels on the Z axes.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn z_labels(&mut self, n: usize) -> &mut Self {
self.n_labels[2] = n;
self
}
/**
Sets the style of the panels in the background.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
self.axis_panel_style = style.into();
self
}
/**
Sets the style of the major grid lines.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn bold_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
self.bold_line_style = style.into();
self
}
/**
Sets the style of the minor grid lines.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn light_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
self.light_line_style = style.into();
self
}
/**
Sets the text style of the axis labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn label_style<S: Into<TextStyle<'b>>>(&mut self, style: S) -> &mut Self {
self.label_style = style.into();
self
}
/**
Specifies the string format of the X axis labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn x_formatter<F: Fn(&X::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
self.format_x = f;
self
}
/**
Specifies the string format of the Y axis labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn y_formatter<F: Fn(&Y::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
self.format_y = f;
self
}
/**
Specifies the string format of the Z axis labels.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub fn z_formatter<F: Fn(&Z::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
self.format_z = f;
self
}
/**
Constructs a new configuration object and defines the defaults.
This is used internally by Plotters and should probably not be included in user code.
See [`ChartContext::configure_axes()`] for more information and examples.
*/
pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>) -> Self {
let parent_size = chart.drawing_area.dim_in_pixel();
let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area());
let tick_size = base_tick_size;
Self {
parent_size,
tick_size,
light_lines_limit: [10, 10, 10],
n_labels: [10, 10, 10],
bold_line_style: Into::<ShapeStyle>::into(&BLACK.mix(0.2)),
light_line_style: Into::<ShapeStyle>::into(&TRANSPARENT),
axis_panel_style: Into::<ShapeStyle>::into(&BLACK.mix(0.1)),
axis_style: Into::<ShapeStyle>::into(&BLACK.mix(0.8)),
label_style: ("sans-serif", (12).percent().max(12).in_pixels(&parent_size)).into(),
format_x: &X::format,
format_y: &Y::format,
format_z: &Z::format,
_phantom: PhantomData,
target: Some(chart),
}
}
pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>
where
XT: Clone,
YT: Clone,
ZT: Clone,
{
let chart = self.target.take().unwrap();
let kps_bold = chart.get_key_points(
BoldPoints(self.n_labels[0]),
BoldPoints(self.n_labels[1]),
BoldPoints(self.n_labels[2]),
);
let kps_light = chart.get_key_points(
LightPoints::new(
self.n_labels[0],
self.n_labels[0] * self.light_lines_limit[0],
),
LightPoints::new(
self.n_labels[1],
self.n_labels[1] * self.light_lines_limit[1],
),
LightPoints::new(
self.n_labels[2],
self.n_labels[2] * self.light_lines_limit[2],
),
);
let panels = chart.draw_axis_panels(
&kps_bold,
&kps_light,
self.axis_panel_style,
self.bold_line_style,
self.light_line_style,
)?;
for i in 0..3 {
let axis = chart.draw_axis(i, &panels, self.axis_style)?;
let labels: Vec<_> = match i {
0 => kps_bold
.x_points
.iter()
.map(|x| {
let x_text = (self.format_x)(x);
let mut p = axis[0].clone();
p[0] = Coord3D::X(x.clone());
(p, x_text)
})
.collect(),
1 => kps_bold
.y_points
.iter()
.map(|y| {
let y_text = (self.format_y)(y);
let mut p = axis[0].clone();
p[1] = Coord3D::Y(y.clone());
(p, y_text)
})
.collect(),
_ => kps_bold
.z_points
.iter()
.map(|z| {
let z_text = (self.format_z)(z);
let mut p = axis[0].clone();
p[2] = Coord3D::Z(z.clone());
(p, z_text)
})
.collect(),
};
chart.draw_axis_ticks(
axis,
&labels[..],
self.tick_size,
self.axis_style,
self.label_style.clone(),
)?;
}
Ok(())
}
}