use std::f64;
use svg;
use svg::node;
use svg::Node;
use crate::axis;
use crate::repr::ContinuousRepresentation;
use crate::style::*;
use crate::svg_render;
#[derive(Debug, Clone)]
pub struct Plot {
pub data: Vec<(f64, f64)>,
pub line_style: Option<LineStyle>,
pub point_style: Option<PointStyle>,
pub legend: Option<String>,
}
impl Plot {
pub fn new(data: Vec<(f64, f64)>) -> Self {
Plot {
data,
line_style: None,
point_style: None,
legend: None,
}
}
pub fn from_function<F: Fn(f64) -> f64>(f: F, lower: f64, upper: f64) -> Self {
let sampling = (upper - lower) / 200.;
let samples = (0..)
.map(|x| lower + (f64::from(x) * sampling))
.take_while(|&x| x <= upper);
let values = samples.map(|s| (s, f(s))).collect();
Plot {
data: values,
line_style: None,
point_style: None,
legend: None,
}
}
pub fn line_style(mut self, other: LineStyle) -> Self {
if let Some(ref mut self_style) = self.line_style {
self_style.overlay(&other);
} else {
self.line_style = Some(other);
}
self
}
pub fn point_style(mut self, other: PointStyle) -> Self {
if let Some(ref mut self_style) = self.point_style {
self_style.overlay(&other);
} else {
self.point_style = Some(other);
}
self
}
pub fn legend(mut self, legend: String) -> Self {
self.legend = Some(legend);
self
}
fn x_range(&self) -> (f64, f64) {
let mut min = f64::INFINITY;
let mut max = f64::NEG_INFINITY;
for &(x, _) in &self.data {
min = min.min(x);
max = max.max(x);
}
(min, max)
}
fn y_range(&self) -> (f64, f64) {
let mut min = f64::INFINITY;
let mut max = f64::NEG_INFINITY;
for &(_, y) in &self.data {
min = min.min(y);
max = max.max(y);
}
(min, max)
}
}
impl ContinuousRepresentation for Plot {
fn range(&self, dim: u32) -> (f64, f64) {
match dim {
0 => self.x_range(),
1 => self.y_range(),
_ => panic!("Axis out of range"),
}
}
fn to_svg(
&self,
x_axis: &axis::ContinuousAxis,
y_axis: &axis::ContinuousAxis,
face_width: f64,
face_height: f64,
) -> svg::node::element::Group {
let mut group = node::element::Group::new();
if let Some(ref line_style) = self.line_style {
group.append(
svg_render::draw_face_line(
&self.data,
x_axis,
y_axis,
face_width,
face_height,
line_style,
))
}
if let Some(ref point_style) = self.point_style {
group.append(
svg_render::draw_face_points(
&self.data,
x_axis,
y_axis,
face_width,
face_height,
point_style,
))
}
group
}
fn legend_svg(&self) -> Option<svg::node::element::Group> {
self.legend.as_ref().map(|legend| {
let legend = legend.clone();
let mut group = node::element::Group::new();
const FONT_SIZE: f32 = 12.0;
let legend_text = node::element::Text::new()
.set("x", 0)
.set("y", 0)
.set("text-anchor", "start")
.set("font-size", FONT_SIZE)
.add(node::Text::new(legend));
group.append(legend_text);
if let Some(ref style) = self.line_style {
let line = node::element::Line::new()
.set("x1", -10)
.set("y1", -FONT_SIZE/2. +2.)
.set("x2", -3)
.set("y2", -FONT_SIZE/2. +2.)
.set("stroke-width", style.get_width())
.set("stroke", style.get_colour());
group.append(line);
}
group
})
}
fn to_text(
&self,
_x_axis: &axis::ContinuousAxis,
_y_axis: &axis::ContinuousAxis,
_face_width: u32,
_face_height: u32,
) -> String {
"".into()
}
}