use super::frame::Frame;
use crate::mark::Mark;
use crate::render::Surface;
use crate::scale::Scale;
#[derive(Debug, Clone, Default)]
pub struct Plot<'a> {
layers: Vec<Mark<'a>>,
title: Option<String>,
x: Scale,
y: Scale,
x_label: Option<String>,
y_label: Option<String>,
}
impl<'a> Plot<'a> {
pub fn new() -> Plot<'a> {
Plot {
layers: Vec::new(),
title: None,
x: Scale::Linear,
y: Scale::Linear,
x_label: None,
y_label: None,
}
}
#[must_use]
pub fn x_scale(mut self, scale: Scale) -> Plot<'a> {
self.x = scale;
self
}
#[must_use]
pub fn y_scale(mut self, scale: Scale) -> Plot<'a> {
assert!(
!matches!(scale, Scale::Bands(_)),
"categorical y axes are not supported yet"
);
self.y = scale;
self
}
#[must_use]
pub fn time_x(self) -> Plot<'a> {
self.x_scale(Scale::Time)
}
#[must_use]
pub fn log_x(self) -> Plot<'a> {
self.x_scale(Scale::Log)
}
#[must_use]
pub fn log_y(self) -> Plot<'a> {
self.y_scale(Scale::Log)
}
#[must_use]
pub fn x_label(mut self, label: impl Into<String>) -> Plot<'a> {
self.x_label = Some(label.into());
self
}
#[must_use]
pub fn y_label(mut self, label: impl Into<String>) -> Plot<'a> {
self.y_label = Some(label.into());
self
}
#[must_use]
pub fn layer(mut self, mark: impl Into<Mark<'a>>) -> Plot<'a> {
self.layers.push(mark.into());
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Plot<'a> {
self.title = Some(title.into());
self
}
pub fn into_owned(self) -> Plot<'static> {
Plot {
layers: self.layers.into_iter().map(Mark::into_owned).collect(),
title: self.title,
x: self.x,
y: self.y,
x_label: self.x_label,
y_label: self.y_label,
}
}
pub fn render(&self, frame: &Frame) -> String {
self.rasterize(frame).encode(frame.color)
}
fn rasterize(&self, frame: &Frame) -> Surface {
let mut surface = Surface::new(frame.width, frame.height, frame.charset);
if frame.width == 0 || frame.height == 0 {
return surface;
}
let (px, _) = frame.charset.pixels_per_cell();
let layers = super::resolve::resolve(&self.layers, frame.width * px, &frame.theme.palette);
let layout = super::layout::Layout::compute(
frame,
&layers,
self.title.is_some(),
(&self.x, &self.y),
(self.x_label.as_deref(), self.y_label.as_deref()),
);
super::chrome::draw(
&mut surface,
&layout,
self.title.as_deref(),
(self.x_label.as_deref(), self.y_label.as_deref()),
&layers,
);
super::draw::layers(&mut surface, &layout, &layers);
surface
}
}
impl std::fmt::Display for Plot<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.render(&Frame::detect()))
}
}
#[cfg(test)]
#[path = "tests/plot_tests.rs"]
mod tests;