// Copyright (c) 2013-2014 by SiegeLord
//
// All rights reserved. Distributed under LGPL 3.0. For full terms see the file LICENSE.
use crate::axes_common::*;
use crate::coordinates::*;
use crate::datatype::*;
use crate::options::*;
use crate::util::OneWayOwned;
use crate::writer::Writer;
struct BorderOptions
{
front: bool,
locations: Vec<BorderLocation2D>,
options: Vec<PlotOption<String>>,
}
impl BorderOptions
{
fn new() -> BorderOptions
{
BorderOptions {
front: true,
locations: vec![Bottom, Left, Top, Right],
options: vec![],
}
}
fn write_out(&self, writer: &mut Writer, version: GnuplotVersion)
{
writer.write_str("set border ");
let mut f: i32 = 0;
for &l in self.locations.iter()
{
f |= l as i32;
}
write!(writer, "{}", f);
writer.write_str(if self.front { " front " } else { " back " });
AxesCommonData::write_color_options(writer, &self.options, Some("black"));
AxesCommonData::write_line_options(writer, &self.options, version);
writer.write_str("\n");
}
}
/// 2D axes that is used for drawing 2D plots
pub struct Axes2D
{
common: AxesCommonData,
border_options: BorderOptions,
}
impl Axes2D
{
pub(crate) fn new() -> Axes2D
{
Axes2D {
common: AxesCommonData::new(),
border_options: BorderOptions::new(),
}
}
/// Sets the properties of the plot border
///
/// # Arguments
///
/// * `front` - Whether or not to draw the border above or below the plot contents
/// * `locations` - Which locations of the border to draw
/// * `options` - Array of PlotOption controlling the appearance of the border. Relevant options are:
/// * `Color` - Specifies the color of the border
/// * `LineStyle` - Specifies the style of the border
/// * `LineWidth` - Specifies the width of the border
pub fn set_border<'l>(&'l mut self, front: bool, locations: &[BorderLocation2D], options: &[PlotOption<&str>]) -> &'l mut Self
{
self.border_options.front = front;
self.border_options.locations = locations.into();
self.border_options.options = options.to_one_way_owned();
self
}
/// Sets the properties of x axis.
///
/// # Arguments
///
/// * `show` - Whether or not draw the axis
/// * `options` - Array of PlotOption<&str> controlling the appearance of the axis. Relevant options are:
/// * `Color` - Specifies the color of the border
/// * `LineStyle` - Specifies the style of the border
/// * `LineWidth` - Specifies the width of the border
pub fn set_x_axis<'l>(&'l mut self, show: bool, options: &[PlotOption<&str>]) -> &'l mut Self
{
self.common.x_axis.show = show;
self.common.x_axis.options = options.to_one_way_owned();
self
}
/// Like `set_x_axis` but for the y axis.
pub fn set_y_axis<'l>(&'l mut self, show: bool, options: &[PlotOption<&str>]) -> &'l mut Self
{
self.common.y_axis.show = show;
self.common.y_axis.options = options.to_one_way_owned();
self
}
/// Adds an arrow to the plot. The arrow is drawn from `(x1, y1)` to `(x2, y2)` with the arrow point towards `(x2, y2)`.
/// # Arguments
/// * `x1` - X coordinate of the arrow start
/// * `y1` - Y coordinate of the arrow start
/// * `x2` - X coordinate of the arrow end
/// * `y2` - Y coordinate of the arrow end
/// * `options` - Array of PlotOption<&str> controlling the appearance of the arrowhead and arrow shaft. Relevant options are:
/// * `ArrowType` - Specifies the style of the arrow head (or an option to omit it)
/// * `ArrowSize` - Sets the size of the arrow head (in graph units)
/// * `Color` - Specifies the color of the arrow
/// * `LineStyle` - Specifies the style of the arrow shaft
/// * `LineWidth` - Specifies the width of the arrow shaft
pub fn arrow<'l>(
&'l mut self, x1: Coordinate, y1: Coordinate, x2: Coordinate, y2: Coordinate, options: &[PlotOption<&str>],
) -> &'l mut Self
{
{
let c = &mut self.common.commands as &mut Writer;
write!(c, "set arrow from {},{} to {},{}", x1, y1, x2, y2);
first_opt!{options,
ArrowType(s) =>
{
c.write_str(match s
{
Open => "",
Closed => " empty",
Filled => " filled",
NoArrow => " nohead",
});
}
}
c.write_str(" size graph ");
first_opt_default!{options,
ArrowSize(z) =>
{
write!(c, "{:.12e}", z);
},
_ =>
{
c.write_str("0.05");
}
}
c.write_str(",12");
let options: Vec<PlotOption<String>> = options.to_one_way_owned();
AxesCommonData::write_color_options(c, &options, Some("black"));
AxesCommonData::write_line_options(c, &options, GnuplotVersion { major: 0, minor: 0 });
c.write_str("\n");
}
self
}
/// Specifies the location and other properties of the legend
/// # Arguments
/// * `x` - X coordinate of the legend
/// * `y` - Y coordinate of the legend
/// * `label_options` - Array of LegendOption options
/// * `text_options` - Array of LabelOption options specifying the appearance of the plot titles. Valid options are:
/// * `Font`
/// * `TextColor`
/// * `TextAlign(AlignLeft)`
/// * `TextAlign(AlignRight)`
pub fn set_legend<'l>(
&'l mut self, x: Coordinate, y: Coordinate, legend_options: &[LegendOption<&str>], text_options: &[LabelOption<&str>],
) -> &'l mut Self
{
{
let c = &mut self.common.commands as &mut Writer;
write!(c, "set key at {},{}", x, y);
first_opt_default!{legend_options,
Placement(h, v) =>
{
c.write_str(match h
{
AlignLeft => " left",
AlignRight => " right",
_ => " center"
});
c.write_str(match v
{
AlignTop => " top",
AlignBottom => " bottom",
_ => " center"
});
},
_ =>
{
c.write_str(" right top");
}
}
first_opt_default!{legend_options,
Horizontal =>
{
c.write_str(" horizontal");
},
_ =>
{
c.write_str(" vertical");
}
}
first_opt_default!{legend_options,
Reverse =>
{
c.write_str(" reverse");
},
_ =>
{
c.write_str(" noreverse");
}
}
first_opt_default!{legend_options,
Invert =>
{
c.write_str(" invert");
},
_ =>
{
c.write_str(" noinvert");
}
}
first_opt!{legend_options,
Title(s) =>
{
c.write_str(" title \"");
c.write_str(s);
c.write_str("\"");
}
}
first_opt!{text_options,
Font(f, s) =>
{
c.write_str(" font \"");
c.write_str(f);
c.write_str(",");
c.write_str(&s.to_string()[..]);
c.write_str("\"");
}
}
first_opt!{text_options,
TextColor(s) =>
{
c.write_str(" textcolor rgb \"");
c.write_str(s);
c.write_str("\"");
}
}
first_opt!{text_options,
TextAlign(a) =>
{
c.write_str(match a
{
AlignLeft => " Left",
AlignRight => " Right",
_ => ""
});
}
}
first_opt!{legend_options,
MaxRows(r) =>
{
write!(c, " maxrows {}", r as i32);
}
}
first_opt!{legend_options,
MaxCols(l) =>
{
write!(c, " maxcols {}", l as i32);
}
}
c.write_str("\n");
}
self
}
/// Plot a 2D scatter-plot with lines connecting each data point
/// # Arguments
/// * `x` - x values
/// * `y` - y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `LineWidth` - Sets the width of the line
/// * `LineStyle` - Sets the style of the line
/// * `Color` - Sets the color
pub fn lines<'l, Tx: DataType, X: IntoIterator<Item = Tx>, Ty: DataType, Y: IntoIterator<Item = Ty>>(
&'l mut self, x: X, y: Y, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot2(Lines, x, y, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot with a point standing in for each data point
/// # Arguments
/// * `x` - x values
/// * `y` - y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `PointSymbol` - Sets symbol for each point
/// * `PointSize` - Sets the size of each point
/// * `Color` - Sets the color
pub fn points<'l, Tx: DataType, X: IntoIterator<Item = Tx>, Ty: DataType, Y: IntoIterator<Item = Ty>>(
&'l mut self, x: X, y: Y, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot2(Points, x, y, options.to_one_way_owned()));
self
}
/// A combination of lines and points methods (drawn in that order).
/// # Arguments
/// * `x` - x values
/// * `y` - y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element
pub fn lines_points<'l, Tx: DataType, X: IntoIterator<Item = Tx>, Ty: DataType, Y: IntoIterator<Item = Ty>>(
&'l mut self, x: X, y: Y, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot2(LinesPoints, x, y, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot with a point standing in for each data point.
/// Additionally, error bars are attached to each data point in the X direction.
/// # Arguments
/// * `x` - x values
/// * `y` - y valuess
/// * `x_error` - Errors associated with the x value
/// * `options` - Array of PlotOption controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `PointSymbol` - Sets symbol for each point
/// * `PointSize` - Sets the size of each point
/// * `Color` - Sets the color
pub fn x_error_bars<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Ty: DataType,
Y: IntoIterator<Item = Ty>,
Txe: DataType,
XE: IntoIterator<Item = Txe>,
>(
&'l mut self, x: X, y: Y, x_error: XE, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(XErrorBars, x, y, x_error, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot with a point standing in for each data point.
/// Additionally, error bars are attached to each data point in the Y direction.
/// # Arguments
/// * `x` - x values
/// * `y` - y values
/// * `y_error` - Errors associated with the y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `PointSymbol` - Sets symbol for each point
/// * `PointSize` - Sets the size of each point
/// * `Color` - Sets the color
pub fn y_error_bars<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Ty: DataType,
Y: IntoIterator<Item = Ty>,
Tye: DataType,
YE: IntoIterator<Item = Tye>,
>(
&'l mut self, x: X, y: Y, y_error: YE, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(YErrorBars, x, y, y_error, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot with a point standing in for each data point and lines connecting each data point.
/// Additionally, error bars are attached to each data point in the X direction.
/// # Arguments
/// * `x` - x values
/// * `y` - y valuess
/// * `x_error` - Errors associated with the x value
/// * `options` - Array of PlotOption controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `PointSymbol` - Sets symbol for each point
/// * `PointSize` - Sets the size of each point
/// * `LineWidth` - Sets the width of the line
/// * `LineStyle` - Sets the style of the line
/// * `Color` - Sets the color
pub fn x_error_lines<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Ty: DataType,
Y: IntoIterator<Item = Ty>,
Txe: DataType,
XE: IntoIterator<Item = Txe>,
>(
&'l mut self, x: X, y: Y, x_error: XE, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(XErrorLines, x, y, x_error, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot with a point standing in for each data point and lines connecting each data point.
/// Additionally, error bars are attached to each data point in the Y direction.
/// # Arguments
/// * `x` - x values
/// * `y` - y values
/// * `y_error` - Errors associated with the y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `PointSymbol` - Sets symbol for each point
/// * `PointSize` - Sets the size of each point
/// * `LineWidth` - Sets the width of the line
/// * `LineStyle` - Sets the style of the line
/// * `Color` - Sets the color
pub fn y_error_lines<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Ty: DataType,
Y: IntoIterator<Item = Ty>,
Tye: DataType,
YE: IntoIterator<Item = Tye>,
>(
&'l mut self, x: X, y: Y, y_error: YE, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(YErrorLines, x, y, y_error, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot of two curves (bound by `y_lo` and `y_hi`) with a filled region between them.
/// `FillRegion` plot option can be used to control what happens when the curves intersect. If set to Above, then the `y_lo < y_hi` region is filled.
/// If set to Below, then the `y_lo > y_hi` region is filled. Otherwise both regions are filled.
/// # Arguments
/// * `x` - x values
/// * `y_lo` - Bottom y values
/// * `y_hi` - Top y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `FillRegion` - Specifies the region between the two curves to fill
/// * `Color` - Sets the color of the filled region
/// * `FillAlpha` - Sets the transparency of the filled region
pub fn fill_between<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Tyl: DataType,
YL: IntoIterator<Item = Tyl>,
Tyh: DataType,
YH: IntoIterator<Item = Tyh>,
>(
&'l mut self, x: X, y_lo: YL, y_hi: YH, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(FillBetween, x, y_lo, y_hi, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot using boxes of automatic width. Box widths are set so that there are no gaps between successive boxes (i.e. each box may have a different width).
/// Boxes start at the x-axis and go towards the y value of the datapoint.
/// # Arguments
/// * `x` - x values (center of the box)
/// * `y` - y values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `LineWidth` - Sets the width of the border
/// * `LineStyle` - Sets the style of the border
/// * `BorderColor` - Sets the color of the border
/// * `Color` - Sets the color of the box fill
/// * `FillAlpha` - Sets the transparency of the box fill
pub fn boxes<'l, Tx: DataType, X: IntoIterator<Item = Tx>, Ty: DataType, Y: IntoIterator<Item = Ty>>(
&'l mut self, x: X, y: Y, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot2(Boxes, x, y, options.to_one_way_owned()));
self
}
/// Plot a 2D scatter-plot using boxes of set (per box) width.
/// Boxes start at the x-axis and go towards the y value of the datapoint.
/// # Arguments
/// * `x` - x values (center of the box)
/// * `y` - y values
/// * `w` - Box width values
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `LineWidth` - Sets the width of the border
/// * `LineStyle` - Sets the style of the border
/// * `BorderColor` - Sets the color of the border
/// * `Color` - Sets the color of the box fill
/// * `FillAlpha` - Sets the transparency of the box fill
pub fn boxes_set_width<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
Ty: DataType,
Y: IntoIterator<Item = Ty>,
Tw: DataType,
W: IntoIterator<Item = Tw>,
>(
&'l mut self, x: X, y: Y, w: W, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common
.elems
.push(PlotElement::new_plot3(Boxes, x, y, w, options.to_one_way_owned()));
self
}
/// Plot a 2D box-and-whisker plot using boxes of automatic width.
///
/// # Arguments
/// * `x` - x values (center of the box)
/// * `box_min` - minimum box y value
/// * `whisker_min` - minimum whisker y value
/// * `whisker_max` - maximum whisker y value
/// * `box_max` - maximum box y value
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `LineWidth` - Sets the width of the border
/// * `LineStyle` - Sets the style of the border
/// * `BorderColor` - Sets the color of the border
/// * `Color` - Sets the color of the box fill
/// * `FillAlpha` - Sets the transparency of the box fill
/// * `WhiskerBars` - Sets the width of the whisker bars
pub fn box_and_whisker<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
TBoxMin: DataType,
BoxMin: IntoIterator<Item = TBoxMin>,
TWhiskerMin: DataType,
WhiskerMin: IntoIterator<Item = TWhiskerMin>,
TWhiskerMax: DataType,
WhiskerMax: IntoIterator<Item = TWhiskerMax>,
TBoxMax: DataType,
BoxMax: IntoIterator<Item = TBoxMax>,
>(
&'l mut self, x: X, box_min: BoxMin, whisker_min: WhiskerMin, whisker_max: WhiskerMax, box_max: BoxMax,
options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common.elems.push(PlotElement::new_plot5(
BoxAndWhisker,
x,
box_min,
whisker_min,
whisker_max,
box_max,
options.to_one_way_owned(),
));
self
}
/// Plot a 2D box-and-whisker plot using boxes of set width.
///
/// # Arguments
/// * `x` - x values (center of the box)
/// * `box_min` - minimum box y value
/// * `whisker_min` - minimum whisker y value
/// * `whisker_max` - maximum whisker y value
/// * `box_max` - maximum box y value
/// * `box_width` - width of the box (in x axis units)
/// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
/// * `LineWidth` - Sets the width of the border
/// * `LineStyle` - Sets the style of the border
/// * `BorderColor` - Sets the color of the border
/// * `Color` - Sets the color of the box fill
/// * `FillAlpha` - Sets the transparency of the box fill
/// * `WhiskerBars` - Sets the width of the whisker bars
pub fn box_and_whisker_set_width<
'l,
Tx: DataType,
X: IntoIterator<Item = Tx>,
TBoxMin: DataType,
BoxMin: IntoIterator<Item = TBoxMin>,
TWhiskerMin: DataType,
WhiskerMin: IntoIterator<Item = TWhiskerMin>,
TWhiskerMax: DataType,
WhiskerMax: IntoIterator<Item = TWhiskerMax>,
TBoxMax: DataType,
BoxMax: IntoIterator<Item = TBoxMax>,
TBoxWidth: DataType,
BoxWidth: IntoIterator<Item = TBoxWidth>,
>(
&'l mut self, x: X, box_min: BoxMin, whisker_min: WhiskerMin, whisker_max: WhiskerMax, box_max: BoxMax, box_width: BoxWidth,
options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common.elems.push(PlotElement::new_plot6(
BoxAndWhisker,
x,
box_min,
whisker_min,
whisker_max,
box_max,
box_width,
options.to_one_way_owned(),
));
self
}
/// Draws an image from a rectangular array of data by connecting the individual datapoints with polygons.
///
/// #Arguments:
/// * `mat` - Row-major 2D array signifying the value of the datapoints. The X and Y coordinates of the datapoints are determined automatically,
/// and optionally scaled using the `dimensions` argument.
/// * `num_rows` - Number of rows in the data array
/// * `num_cols` - Number of columns in the data array
/// * `dimensions` - Optional X and Y coordinates of the first and last data points (with the rest of the coordinates spaced evenly between).
/// By default this will be `(0, 0)` and `(num_rows - 1, num_cols - 1)`.
/// * `options` - Array of PlotOption<&str> controlling the appearance of the surface. Relevant options are:
/// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
pub fn image<'l, T: DataType, X: IntoIterator<Item = T>>(
&'l mut self, mat: X, num_rows: usize, num_cols: usize, dimensions: Option<(f64, f64, f64, f64)>, options: &[PlotOption<&str>],
) -> &'l mut Self
{
self.common.elems.push(PlotElement::new_plot_matrix(
Image,
false,
mat,
num_rows,
num_cols,
dimensions,
options.to_one_way_owned(),
));
self
}
}
impl AxesCommonPrivate for Axes2D
{
fn get_common_data_mut(&mut self) -> &mut AxesCommonData
{
&mut self.common
}
fn get_common_data(&self) -> &AxesCommonData
{
&self.common
}
}
impl AxesCommon for Axes2D {}
pub(crate) trait Axes2DPrivate
{
fn write_out(&self, writer: &mut Writer, version: GnuplotVersion);
}
impl Axes2DPrivate for Axes2D
{
fn write_out(&self, writer: &mut Writer, version: GnuplotVersion)
{
if self.common.elems.len() == 0
{
return;
}
self.common.write_out_commands(writer, version);
self.border_options.write_out(writer, version);
let mut grid_axes = vec![];
if self.common.x_axis.grid
{
grid_axes.push(self.common.x_axis.axis);
}
if self.common.y_axis.grid
{
grid_axes.push(self.common.y_axis.axis);
}
if self.common.cb_axis.grid
{
grid_axes.push(self.common.cb_axis.axis);
}
self.common.write_grid_options(writer, &grid_axes, version);
self.common.write_out_elements("plot", writer, version);
}
}