use datatype::*;
use coordinates::*;
use options::*;
use writer::*;
pub use self::LabelType::*;
pub use self::TickAxis::*;
pub use self::PlotType::*;
pub use self::DataSourceType::*;
impl PlotWriter for Vec<u8>
{
fn write_data<T: DataType>(&mut self, v: T)
{
self.write_le_f64(v.get());
}
}
pub struct PlotElement
{
pub args: Vec<u8>,
pub data: Vec<u8>
}
impl PlotElement
{
pub fn new() -> PlotElement
{
PlotElement
{
args: vec![],
data: vec![],
}
}
}
#[derive(Copy)]
pub enum LabelType
{
XLabel,
YLabel,
ZLabel,
CBLabel,
TitleLabel,
Label(Coordinate, Coordinate),
AxesTicks,
}
impl LabelType
{
fn is_label(&self) -> bool
{
match *self
{
Label(..) => true,
_ => false
}
}
}
pub fn write_out_label_options<T: PlotWriter + Writer>(label_type: LabelType, options: &[LabelOption], writer: &mut T)
{
let w = writer as &mut Writer;
match label_type
{
Label(x, y) =>
{
write!(w, " at {},{} front", x, y);
}
_ => ()
}
first_opt!{options,
TextOffset(x, y) =>
{
write!(w, " offset character {:.12e},{:.12e}", x, y);
}
}
first_opt!{options,
TextColor(s) =>
{
write!(w, r#" tc rgb "{}""#, s);
}
}
first_opt!{options,
Font(f, s) =>
{
write!(w, r#" font "{},{}""#, f, s);
}
}
first_opt!{options,
Rotate(a) =>
{
write!(w, " rotate by {:.12e}", a);
}
}
if label_type.is_label()
{
let mut have_point = false;
first_opt!{options,
MarkerSymbol(s) =>
{
write!(w, " point pt {}", char_to_symbol(s));
have_point = true;
}
}
if have_point
{
first_opt!{options,
MarkerColor(s) =>
{
write!(w, r#" lc rgb "{}""#, s);
}
}
first_opt!{options,
MarkerSize(z) =>
{
write!(w, " ps {:.12e}", z);
}
}
}
first_opt!{options,
TextAlign(a) =>
{
write!(w, "{}", match a
{
AlignLeft => " left",
AlignRight => " right",
_ => " center",
});
}
}
}
}
pub enum TickAxis
{
XTickAxis,
YTickAxis,
ZTickAxis,
CBTickAxis,
}
impl TickAxis
{
pub fn to_axis_str(&self) -> &str
{
match *self
{
XTickAxis => "x",
YTickAxis => "y",
ZTickAxis => "z",
CBTickAxis => "cb",
}
}
pub fn to_tick_str(&self) -> &str
{
match *self
{
XTickAxis => "xtics",
YTickAxis => "ytics",
ZTickAxis => "ztics",
CBTickAxis => "cbtics",
}
}
pub fn to_range_str(&self) -> &str
{
match *self
{
XTickAxis => "xrange",
YTickAxis => "yrange",
ZTickAxis => "zrange",
CBTickAxis => "cbrange",
}
}
}
pub enum PlotType
{
Lines,
Points,
LinesPoints,
XErrorLines,
YErrorLines,
FillBetween,
Boxes,
Pm3D,
Image,
}
impl PlotType
{
fn is_line(&self) -> bool
{
match *self
{
Lines |
LinesPoints |
XErrorLines |
Boxes |
YErrorLines => true,
_ => false
}
}
fn is_points(&self) -> bool
{
match *self
{
Points |
LinesPoints |
XErrorLines |
YErrorLines => true,
_ => false
}
}
fn is_fill(&self) -> bool
{
match *self
{
Boxes |
FillBetween => true,
_ => false
}
}
}
pub struct AxisData
{
pub ticks_buf: Vec<u8>,
pub log_base: Option<f64>,
pub mticks: i32,
pub axis: TickAxis,
pub min: AutoOption<f64>,
pub max: AutoOption<f64>,
}
impl AxisData
{
pub fn new(axis: TickAxis) -> AxisData
{
AxisData
{
ticks_buf: vec![],
log_base: None,
mticks: 0,
axis: axis,
min: Auto,
max: Auto,
}
}
pub fn write_out_commands(&self, w: &mut Writer)
{
let log = match self.log_base
{
Some(base) =>
{
w.write_str("set logscale ");
w.write_str(self.axis.to_axis_str());
write!(w, " {:.12e}", base);
true
},
None =>
{
w.write_str("unset logscale ");
w.write_str(self.axis.to_axis_str());
false
}
};
w.write_str("\n");
if self.mticks > 0
{
write!(w, "set m{} ", self.axis.to_tick_str());
if log
{
writeln!(w, "default");
}
else
{
writeln!(w, "{}", self.mticks as i32 + 1);
}
}
else
{
writeln!(w, "unset m{}", self.axis.to_tick_str());
}
w.write_str("\n");
w.write_str("set ");
w.write_str(self.axis.to_range_str());
w.write_str(" [");
match self.min
{
Fix(v) => write!(w, "{:.12e}", v),
Auto => w.write_str("*")
};
w.write_str(":");
match self.max
{
Fix(v) => write!(w, "{:.12e}", v),
Auto => w.write_str("*")
};
w.write_str("]\n");
w.write_all(&self.ticks_buf[]);
}
pub fn set_ticks_custom<T: DataType, TL: Iterator<Item = Tick<T>>>(&mut self, ticks: TL, tick_options: &[TickOption], label_options: &[LabelOption])
{
self.mticks = 0;
{
let c = &mut self.ticks_buf;
c.truncate(0);
c.write_str("set ");
c.write_str(self.axis.to_tick_str());
c.write_str(" (");
let mut first = true;
for tick in ticks
{
if first
{
first = false;
}
else
{
c.write_str(",");
}
let a = Auto;
let (ref pos, ref label, level) = match tick
{
Minor(ref pos) =>
{
(pos, &a, 1us)
},
Major(ref pos, ref label) =>
{
(pos, label, 0us)
}
};
match **label
{
Fix(ref label) =>
{
c.write_str("\"");
c.write_str(label.as_slice());
c.write_str("\" ");
},
Auto => ()
}
write!(&mut *c, "{:.12e} {}", pos.get(), level);
}
c.write_str(")");
}
self.set_ticks_options(tick_options, label_options);
self.ticks_buf.write_str("\n");
}
fn set_ticks_options(&mut self, tick_options: &[TickOption], label_options: &[LabelOption])
{
let c = &mut self.ticks_buf;
write_out_label_options(AxesTicks, label_options, c);
first_opt!{tick_options,
OnAxis(b) =>
{
c.write_str(match b
{
true => " axis",
false => " border",
});
}
}
first_opt!{tick_options,
Mirror(b) =>
{
c.write_str(match b
{
true => " mirror",
false => " nomirror",
});
}
}
first_opt!{tick_options,
Inward(b) =>
{
c.write_str(match b
{
true => " in",
false => " out",
});
}
}
let mut minor_scale = 0.5;
let mut major_scale = 0.5;
first_opt!{tick_options,
MinorScale(s) =>
{
minor_scale = s;
}
}
first_opt!{tick_options,
MajorScale(s) =>
{
major_scale = s;
}
}
write!(&mut *c, " scale {:.12e},{:.12e}", minor_scale, major_scale);
}
pub fn set_ticks(&mut self, tick_placement: Option<(AutoOption<f64>, u32)>, tick_options: &[TickOption], label_options: &[LabelOption])
{
self.ticks_buf.truncate(0);
self.mticks = match tick_placement
{
Some((incr, mticks)) =>
{
{
let c = &mut self.ticks_buf;
c.write_str("set ");
c.write_str(self.axis.to_tick_str());
match incr
{
Auto =>
{
c.write_str(" autofreq");
},
Fix(incr) =>
{
if incr <= 0.0
{
panic!("'incr' must be positive, but is actually {}", incr);
}
c.write_str(" ");
write!(&mut *c, " {:.12e}", incr);
}
}
}
self.set_ticks_options(tick_options, label_options);
mticks as i32
},
None =>
{
write!(&mut self.ticks_buf, "unset {0}", self.axis.to_tick_str());
0
}
};
self.ticks_buf.write_str("\n");
}
pub fn set_range(&mut self, min: AutoOption<f64>, max: AutoOption<f64>)
{
self.min = min;
self.max = max;
}
pub fn set_log(&mut self, base: Option<f64>)
{
self.log_base = base;
}
}
pub struct AxesCommonData
{
pub commands: Vec<u8>,
pub elems: Vec<PlotElement>,
pub grid_rows: u32,
pub grid_cols: u32,
pub grid_pos: Option<u32>,
pub x_axis: AxisData,
pub y_axis: AxisData,
pub cb_axis: AxisData,
}
pub fn char_to_symbol(c: char) -> i32
{
match c
{
'.' => 0,
'+' => 1,
'x' => 2,
'*' => 3,
's' => 4,
'S' => 5,
'o' => 6,
'O' => 7,
't' => 8,
'T' => 9,
'd' => 10,
'D' => 11,
'r' => 12,
'R' => 13,
a => panic!("Invalid symbol {}", a)
}
}
enum DataSourceType
{
Record,
Array,
SizedArray(f64, f64, f64, f64),
}
impl AxesCommonData
{
pub fn new() -> AxesCommonData
{
AxesCommonData
{
commands: vec![],
elems: Vec::new(),
grid_rows: 0,
grid_cols: 0,
grid_pos: None,
x_axis: AxisData::new(XTickAxis),
y_axis: AxisData::new(YTickAxis),
cb_axis: AxisData::new(CBTickAxis),
}
}
pub fn write_line_options(c: &mut Writer, options: &[PlotOption])
{
let mut found = false;
c.write_str(" lw ");
first_opt!{options,
LineWidth(w) =>
{
write!(c, "{:.12e}", w);
found = true;
}
}
if !found
{
c.write_str("1");
}
c.write_str(" lt ");
let mut found = false;
first_opt!{options,
LineStyle(d) =>
{
write!(c, "{}", d.to_int());
found = true;
}
}
if !found
{
c.write_str("1");
}
}
pub fn write_color_options<'l>(c: &mut Writer, options: &[PlotOption<'l>], default: Option<&'l str>)
{
let mut col = default;
first_opt!{options,
Color(s) =>
{
col = Some(s)
}
}
match col
{
Some(s) =>
{
write!(c, r#" lc rgb "{}""#, s);
},
None => ()
}
}
pub fn plot2<T1: DataType, X1: Iterator<Item = T1>,
T2: DataType, X2: Iterator<Item = T2>>(&mut self, plot_type: PlotType, x1: X1, x2: X2, options: &[PlotOption])
{
let l = self.elems.len();
self.elems.push(PlotElement::new());
let mut num_rows = 0;
{
let data = &mut self.elems.as_mut_slice()[l].data;
for (x1, x2) in x1.zip(x2)
{
data.write_data(x1);
data.write_data(x2);
num_rows += 1;
}
}
self.write_common_commands(l, num_rows, 2, plot_type, Record, false, options);
}
pub fn plot3<T1: DataType, X1: Iterator<Item = T1>,
T2: DataType, X2: Iterator<Item = T2>,
T3: DataType, X3: Iterator<Item = T3>>(&mut self, plot_type: PlotType, x1: X1, x2: X2, x3: X3, options: &[PlotOption])
{
let l = self.elems.len();
self.elems.push(PlotElement::new());
let mut num_rows = 0;
{
let data = &mut self.elems.as_mut_slice()[l].data;
for ((x1, x2), x3) in x1.zip(x2).zip(x3)
{
data.write_data(x1);
data.write_data(x2);
data.write_data(x3);
num_rows += 1;
}
}
self.write_common_commands(l, num_rows, 3, plot_type, Record, false, options);
}
pub fn plot_matrix<T: DataType, X: Iterator<Item = T>>(&mut self, plot_type: PlotType, is_3d: bool, mat: X, num_rows: usize, num_cols: usize,
dimensions: Option<(f64, f64, f64, f64)>, options: &[PlotOption])
{
let l = self.elems.len();
self.elems.push(PlotElement::new());
{
let mut count = 0;
let data = &mut self.elems.as_mut_slice()[l].data;
for x in mat
{
data.write_data(x);
count += 1;
}
if count < num_rows * num_cols
{
for _ in range(0, num_rows * num_cols - count)
{
use std::f64;
data.write_data(f64::NAN);
}
}
}
let source_type = match dimensions
{
Some((x1, y1, x2, y2)) => SizedArray(x1, y1, x2, y2),
None => Array
};
self.write_common_commands(l, num_rows, num_cols, plot_type, source_type, is_3d, options);
}
fn write_common_commands(&mut self, elem_idx: usize, num_rows: usize, num_cols: usize, plot_type: PlotType,
source_type: DataSourceType, is_3d: bool, options: &[PlotOption])
{
let args = &mut self.elems.as_mut_slice()[elem_idx].args as &mut Writer;
match source_type
{
Record =>
{
write!(args, r#" "-" binary endian=little record={} format="%float64" using "#, num_rows);
let mut col_idx = 1;
while col_idx < num_cols + 1
{
write!(args, "{}", col_idx);
if col_idx < num_cols
{
args.write_str(":");
}
col_idx += 1;
}
},
_ =>
{
write!(args, r#" "-" binary endian=little array=({},{}) format="%float64" "#, num_cols, num_rows);
match source_type
{
SizedArray(x1, y1, x2, y2) =>
{
let (x1, x2) = if x1 > x2
{
(x2, x1)
}
else
{
(x1, x2)
};
let (y1, y2) = if y1 > y2
{
(y2, y1)
}
else
{
(y1, y2)
};
write!(args, "origin=({:.12e},{:.12e}", x1, y1);
if is_3d
{
write!(args, ",0");
}
write!(args, ") ");
if num_cols > 1
{
write!(args, "dx={:.12e} ", (x2 - x1) / (num_cols as f64 - 1.0));
}
else
{
write!(args, "dx=1 ");
}
if num_rows > 1
{
write!(args, "dy={:.12e} ", (y2 - y1) / (num_rows as f64 - 1.0));
}
else
{
write!(args, "dy=1 ");
}
},
_ => ()
}
}
}
args.write_str(" with ");
let type_str = match plot_type
{
Lines => "lines",
Points => "points",
LinesPoints => "linespoints",
XErrorLines => "xerrorlines",
YErrorLines => "yerrorlines",
FillBetween => "filledcurves",
Boxes => "boxes",
Pm3D => "pm3d",
Image => "image",
};
args.write_str(type_str);
if plot_type.is_fill()
{
match plot_type
{
FillBetween =>
{
let mut found = false;
first_opt!{options,
FillRegion(d) =>
{
found = true;
args.write_str(match d
{
Above => " above",
Below => " below",
Between => " closed",
});
}
}
if !found
{
args.write_str(" closed");
}
},
_ => ()
}
args.write_str(" fill transparent solid ");
first_opt!{options,
FillAlpha(a) =>
{
write!(args, "{:.12e}", a);
}
}
if plot_type.is_line()
{
args.write_str(" border");
first_opt!{options,
BorderColor(s) =>
{
write!(args, r#" rgb "{}""#, s);
}
}
}
else
{
args.write_str(" noborder");
}
}
if plot_type.is_line()
{
AxesCommonData::write_line_options(args, options);
}
if plot_type.is_points()
{
first_opt!{options,
PointSymbol(s) =>
{
write!(args, " pt {}", char_to_symbol(s));
}
}
first_opt!{options,
PointSize(z) =>
{
write!(args, " ps {}", z);
}
}
}
AxesCommonData::write_color_options(args, options, None);
args.write_str(" t \"");
first_opt!{options,
Caption(s) =>
{
args.write_str(s);
}
}
args.write_str("\"");
}
pub fn write_out_commands(&self, writer: &mut Writer)
{
writer.write_all(&self.commands[]);
self.x_axis.write_out_commands(writer);
self.y_axis.write_out_commands(writer);
self.cb_axis.write_out_commands(writer);
}
pub fn write_out_elements(&self, cmd: &str, writer: &mut Writer)
{
write!(writer, "{}", cmd);
let mut first = true;
for e in self.elems.iter()
{
if !first
{
write!(writer, ",");
}
writer.write_all(&e.args[]);
first = false;
}
write!(writer, "\n");
for e in self.elems.iter()
{
writer.write_all(&e.data[]);
}
}
pub fn set_label_common(&mut self, label_type: LabelType, text: &str, options: &[LabelOption])
{
let c = &mut self.commands;
c.write_str("set ");
let label_str = match label_type
{
XLabel => "xlabel",
YLabel => "ylabel",
ZLabel => "zlabel",
CBLabel => "cblabe",
TitleLabel => "title",
Label(..) => "label",
_ => panic!("Invalid label type")
};
c.write_str(label_str);
c.write_str(" \"");
c.write_str(text);
c.write_str("\"");
write_out_label_options(label_type, options, c);
c.write_str("\n");
}
}
#[doc(hidden)]
pub trait AxesCommonPrivate
{
fn get_common_data<'l>(&'l self) -> &'l AxesCommonData;
fn get_common_data_mut<'l>(&'l mut self) -> &'l mut AxesCommonData;
}
pub trait AxesCommon : AxesCommonPrivate
{
fn set_pos_grid<'l>(&'l mut self, nrow: u32, ncol: u32, pos: u32) -> &'l mut Self
{
assert!(nrow > 0);
assert!(ncol > 0);
assert!(pos < nrow * ncol);
{
let c = self.get_common_data_mut();
c.grid_rows = nrow;
c.grid_cols = ncol;
c.grid_pos = Some(pos);
}
self
}
fn set_pos<'l>(&'l mut self, x: f64, y: f64) -> &'l mut Self
{
self.get_common_data_mut().grid_pos = None;
writeln!(&mut self.get_common_data_mut().commands, "set origin {:.12e},{:.12e}", x, y);
self
}
fn set_size<'l>(&'l mut self, w: f64, h: f64) -> &'l mut Self
{
writeln!(&mut self.get_common_data_mut().commands, "set size {:.12e},{:.12e}", w, h);
self
}
fn set_aspect_ratio<'l>(&'l mut self, ratio: AutoOption<f64>) -> &'l mut Self
{
{
let c = &mut self.get_common_data_mut().commands as &mut Writer;
match ratio
{
Fix(r) =>
{
writeln!(c, "set size ratio {:.12e}", r);
},
Auto =>
{
writeln!(c, "set size noratio");
}
}
}
self
}
fn set_x_label<'l>(&'l mut self, text: &str, options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().set_label_common(XLabel, text, options);
self
}
fn set_y_label<'l>(&'l mut self, text: &str, options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().set_label_common(YLabel, text, options);
self
}
fn set_cb_label<'l>(&'l mut self, text: &str, options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().set_label_common(CBLabel, text, options);
self
}
fn set_title<'l>(&'l mut self, text: &str, options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().set_label_common(TitleLabel, text, options);
self
}
fn label<'l>(&'l mut self, text: &str, x: Coordinate, y: Coordinate, options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().set_label_common(Label(x, y), text, options);
self
}
fn set_x_ticks<'l>(&'l mut self, tick_placement: Option<(AutoOption<f64>, u32)>, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().x_axis.set_ticks(tick_placement, tick_options, label_options);
self
}
fn set_y_ticks<'l>(&'l mut self, tick_placement: Option<(AutoOption<f64>, u32)>, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().y_axis.set_ticks(tick_placement, tick_options, label_options);
self
}
fn set_cb_ticks<'l>(&'l mut self, tick_placement: Option<(AutoOption<f64>, u32)>, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().cb_axis.set_ticks(tick_placement, tick_options, label_options);
self
}
fn set_x_ticks_custom<'l, T: DataType, TL: Iterator<Item = Tick<T>>>(&'l mut self, ticks: TL, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().x_axis.set_ticks_custom(ticks, tick_options, label_options);
self
}
fn set_y_ticks_custom<'l, T: DataType, TL: Iterator<Item = Tick<T>>>(&'l mut self, ticks: TL, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().y_axis.set_ticks_custom(ticks, tick_options, label_options);
self
}
fn set_cb_ticks_custom<'l, T: DataType, TL: Iterator<Item = Tick<T>>>(&'l mut self, ticks: TL, tick_options: &[TickOption], label_options: &[LabelOption]) -> &'l mut Self
{
self.get_common_data_mut().cb_axis.set_ticks_custom(ticks, tick_options, label_options);
self
}
fn set_x_range<'l>(&'l mut self, min: AutoOption<f64>, max: AutoOption<f64>) -> &'l mut Self
{
self.get_common_data_mut().x_axis.set_range(min, max);
self
}
fn set_y_range<'l>(&'l mut self, min: AutoOption<f64>, max: AutoOption<f64>) -> &'l mut Self
{
self.get_common_data_mut().y_axis.set_range(min, max);
self
}
fn set_cb_range<'l>(&'l mut self, min: AutoOption<f64>, max: AutoOption<f64>) -> &'l mut Self
{
self.get_common_data_mut().cb_axis.set_range(min, max);
self
}
fn set_x_log<'l>(&'l mut self, base: Option<f64>) -> &'l mut Self
{
self.get_common_data_mut().x_axis.set_log(base);
self
}
fn set_y_log<'l>(&'l mut self, base: Option<f64>) -> &'l mut Self
{
self.get_common_data_mut().y_axis.set_log(base);
self
}
fn set_cb_log<'l>(&'l mut self, base: Option<f64>) -> &'l mut Self
{
self.get_common_data_mut().cb_axis.set_log(base);
self
}
fn set_palette(&mut self, palette: PaletteType) -> &mut Self
{
{
let c = &mut self.get_common_data_mut().commands as &mut Writer;
match palette
{
Gray(gamma) =>
{
assert!(gamma > 0.0, "Gamma must be positive");
writeln!(c, "set palette gray gamma {:.12e}", gamma);
},
Formula(r, g, b) =>
{
assert!(r >= -36 && r <= 36, "Invalid r formula!");
assert!(g >= -36 && g <= 36, "Invalid g formula!");
assert!(b >= -36 && b <= 36, "Invalid b formula!");
writeln!(c, "set palette rgbformulae {},{},{}", r, g, b);
},
CubeHelix(start, rev, sat, gamma) =>
{
assert!(sat >= 0.0, "Saturation must be non-negative");
assert!(gamma > 0.0, "Gamma must be positive");
writeln!(c, "set palette cubehelix start {:.12e} cycles {:.12e} saturation {:.12e} gamma {:.12e}", start, rev, sat, gamma);
},
}
}
self
}
fn set_custom_palette<T: Iterator<Item = (f32, f32, f32, f32)>>(&mut self, palette_generator: T) -> &mut Self
{
{
let c = &mut self.get_common_data_mut().commands as &mut Writer;
write!(c, "set palette defined (");
let mut first = true;
let mut old_x = 0.0;
for (x, r, g, b) in palette_generator
{
if first
{
old_x = x;
first = false;
}
else
{
write!(c, ",");
}
assert!(x >= old_x, "The gray levels must be non-decreasing!");
old_x = x;
write!(c, "{:.12e} {:.12e} {:.12e} {:.12e}", x, r, g, b);
}
if first
{
panic!("Need at least 1 element in the generator");
}
writeln!(c, ")");
}
self
}
}