use crate::render::svg::*;
use crate::shape::axis::{Axis, AxisPosition};
use crate::view::View;
use crate::{BandScale, Error, LinearScale};
use std::path::Path;
use svg::Node;
const DEFAULT_MARGIN_TOP: i32 = 90;
const DEFAULT_MARGIN_BOTTOM: i32 = 50;
const DEFAULT_MARGIN_LEFT: i32 = 60;
const DEFAULT_MARGIN_RIGHT: i32 = 40;
const DEFAULT_WIDTH: i32 = 800;
const DEFAULT_HEIGHT: i32 = 600;
const DEFAULT_TITLE_FONT_SIZE: &str = "24px";
const DEFAULT_TITLE_Y_TRANSFORM: i32 = 25;
pub struct Chart<'a> {
margin_top: i32,
margin_bottom: i32,
margin_left: i32,
margin_right: i32,
width: i32,
height: i32,
x_axis_top: Option<Axis>,
x_axis_bottom: Option<Axis>,
y_axis_left: Option<Axis>,
y_axis_right: Option<Axis>,
views: Vec<&'a dyn View>,
title: String,
}
impl<'a> Chart<'a> {
pub fn new() -> Self {
Chart {
margin_top: DEFAULT_MARGIN_TOP,
margin_bottom: DEFAULT_MARGIN_BOTTOM,
margin_left: DEFAULT_MARGIN_LEFT,
margin_right: DEFAULT_MARGIN_RIGHT,
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
x_axis_top: None,
x_axis_bottom: None,
y_axis_left: None,
y_axis_right: None,
views: Vec::new(),
title: String::new(),
}
}
pub fn view_width(&self) -> i32 {
self.width - self.margin_left - self.margin_right
}
pub fn view_height(&self) -> i32 {
self.height - self.margin_top - self.margin_bottom
}
pub fn set_margin_top(mut self, margin_top: i32) -> Self {
self.margin_top = margin_top;
self
}
pub fn set_margin_bottom(mut self, margin_bottom: i32) -> Self {
self.margin_bottom = margin_bottom;
self
}
pub fn set_margin_left(mut self, margin_left: i32) -> Self {
self.margin_left = margin_left;
self
}
pub fn set_margin_right(mut self, margin_right: i32) -> Self {
self.margin_right = margin_right;
self
}
pub fn set_width(mut self, width: i32) -> Self {
self.width = width;
self
}
pub fn set_height(mut self, height: i32) -> Self {
self.height = height;
self
}
pub fn set_axis_top_band(mut self, scale: BandScale) -> Self {
self.x_axis_top = Some(Axis::new(
&scale,
AxisPosition::Top,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_top_linear(mut self, scale: LinearScale) -> Self {
self.x_axis_top = Some(Axis::new(
&scale,
AxisPosition::Top,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_bottom_band(mut self, scale: BandScale) -> Self {
self.x_axis_bottom = Some(Axis::new(
&scale,
AxisPosition::Bottom,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_bottom_linear(mut self, scale: LinearScale) -> Self {
self.x_axis_bottom = Some(Axis::new(
&scale,
AxisPosition::Bottom,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_left_band(mut self, scale: BandScale) -> Self {
self.y_axis_left = Some(Axis::new(
&scale,
AxisPosition::Left,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_left_linear(mut self, scale: LinearScale) -> Self {
self.y_axis_left = Some(Axis::new(
&scale,
AxisPosition::Left,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_right_band(mut self, scale: BandScale) -> Self {
self.y_axis_right = Some(Axis::new(
&scale,
AxisPosition::Right,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_right_linear(mut self, scale: LinearScale) -> Self {
self.y_axis_right = Some(Axis::new(
&scale,
AxisPosition::Right,
self.view_width(),
self.view_height(),
));
self
}
pub fn set_axis_top_label(mut self, label: &str) -> Self {
if let Some(ref mut axis) = self.x_axis_top {
axis.set_label(label);
}
self
}
pub fn set_axis_bottom_label(mut self, label: &str) -> Self {
if let Some(ref mut axis) = self.x_axis_bottom {
axis.set_label(label);
}
self
}
pub fn set_axis_left_label(mut self, label: &str) -> Self {
if let Some(ref mut axis) = self.y_axis_left {
axis.set_label(label);
}
self
}
pub fn set_axis_right_label(mut self, label: &str) -> Self {
if let Some(ref mut axis) = self.y_axis_right {
axis.set_label(label);
}
self
}
pub fn set_title(mut self, title: &str) -> Self {
self.title = title.to_string();
self
}
pub fn add_view(mut self, view: &'a dyn View) -> Self {
self.views.push(view);
self
}
pub fn set_views(mut self, views: Vec<&'a dyn View>) -> Self {
self.views = views;
self
}
pub fn to_svg(&self) -> svg::Document {
let mut res = svg::node::element::Group::new().set(CLASS_ATTR, CLASS_CHART);
if let Some(ref axis) = self.x_axis_top {
let mut axis_group = axis.to_svg();
axis_group.assign(
TRANSFORM_ATTR,
translate_x_y(self.margin_left, self.margin_top),
);
res.append(axis_group);
};
if let Some(ref axis) = self.x_axis_bottom {
let mut axis_group = axis.to_svg();
axis_group.assign(
TRANSFORM_ATTR,
translate_x_y(self.margin_left, self.height - self.margin_bottom),
);
res.append(axis_group);
};
if let Some(ref axis) = self.y_axis_left {
let mut axis_group = axis.to_svg();
axis_group.assign(
TRANSFORM_ATTR,
translate_x_y(self.margin_left, self.margin_top),
);
res.append(axis_group);
};
if let Some(ref axis) = self.y_axis_right {
let mut axis_group = axis.to_svg();
axis_group.assign(
TRANSFORM_ATTR,
translate_x_y(self.width - self.margin_right, self.margin_top),
);
res.append(axis_group);
};
let mut views_group = svg::node::element::Group::new()
.set(CLASS_ATTR, CLASS_VIEWS)
.set(
TRANSFORM_ATTR,
translate_x_y(self.margin_left, self.margin_top),
);
for view in self.views.iter() {
views_group.append(view.to_svg());
}
res.append(views_group);
if !self.title.is_empty() {
let title_group = svg::node::element::Group::new()
.set(CLASS_ATTR, CLASS_TITLE)
.set(
TRANSFORM_ATTR,
translate_x_y(self.width / 2, DEFAULT_TITLE_Y_TRANSFORM),
)
.add(
svg::node::element::Text::new()
.set(X_ATTR, START)
.set(Y_ATTR, START)
.set(DY_ATTR, DEFAULT_DY)
.set(FILL_ATTR, DEFAULT_FONT_COLOR)
.set(TEXT_ANCHOR_ATTR, TEXT_ANCHOR_MIDDLE)
.set(FONT_SIZE_ATTR, DEFAULT_TITLE_FONT_SIZE)
.set(FONT_FAMILY_ATTR, DEFAULT_FONT_FAMILY)
.add(svg::node::Text::new(&self.title)),
);
res.append(title_group);
}
svg::Document::new()
.set(WIDTH_ATTR, self.width)
.set(HEIGHT_ATTR, self.height)
.set(VIEW_BOX_ATTR, (START, START, self.width, self.height))
.add(res)
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
svg::save(path, &self.to_svg())?;
Ok(())
}
}
impl<'a> Default for Chart<'a> {
fn default() -> Self {
Self::new()
}
}