use super::{vector_to_array, AsVector, GraphMaker};
use crate::quote_marker;
use num_traits::Num;
use std::fmt::Write;
#[derive(Clone, Debug)]
pub enum RayEndpoint {
Coords(f64, f64),
Slope(f64),
Horizontal,
Vertical,
}
pub struct Curve {
label: String, line_alpha: f64, line_color: String, line_style: String, line_width: f64, marker_color: String, marker_every: usize, marker_void: bool, marker_line_color: String, marker_line_width: f64, marker_size: f64, marker_style: String, stop_clip: bool, extra: String, buffer: String, }
impl Curve {
pub fn new() -> Self {
Curve {
label: String::new(),
line_alpha: 0.0,
line_color: String::new(),
line_style: String::new(),
line_width: 0.0,
marker_color: String::new(),
marker_every: 0,
marker_void: false,
marker_line_color: String::new(),
marker_line_width: 0.0,
marker_size: 0.0,
marker_style: String::new(),
stop_clip: false,
extra: String::new(),
buffer: String::new(),
}
}
pub fn points_begin(&mut self) -> &mut Self {
write!(&mut self.buffer, "xy=np.array([").unwrap();
self
}
pub fn points_add<T>(&mut self, x: T, y: T) -> &mut Self
where
T: std::fmt::Display + Num,
{
write!(&mut self.buffer, "[{},{}],", x, y).unwrap();
self
}
pub fn points_end(&mut self) -> &mut Self {
let opt = self.options();
write!(&mut self.buffer, "])\nplt.plot(xy[:,0],xy[:,1]{})\n", &opt).unwrap();
self
}
pub fn points_3d_begin(&mut self) -> &mut Self {
write!(&mut self.buffer, "xyz=np.array([").unwrap();
self
}
pub fn points_3d_add<T>(&mut self, x: T, y: T, z: T) -> &mut Self
where
T: std::fmt::Display + Num,
{
write!(&mut self.buffer, "[{},{},{}],", x, y, z).unwrap();
self
}
pub fn points_3d_end(&mut self) -> &mut Self {
let opt = self.options();
write!(
&mut self.buffer,
"])\nax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2]{})\n",
&opt
)
.unwrap();
self
}
pub fn draw<'a, T, U>(&mut self, x: &'a T, y: &'a T)
where
T: AsVector<'a, U>,
U: 'a + std::fmt::Display + Num,
{
vector_to_array(&mut self.buffer, "x", x);
vector_to_array(&mut self.buffer, "y", y);
let opt = self.options();
write!(&mut self.buffer, "plt.plot(x,y{})\n", &opt).unwrap();
}
pub fn draw_with_twin_x<'a, T, U>(&mut self, y: &'a T)
where
T: AsVector<'a, U>,
U: 'a + std::fmt::Display + Num,
{
vector_to_array(&mut self.buffer, "y2", y);
let opt = self.options();
write!(
&mut self.buffer,
"ax=plt.gca()\n\
ax_twinx=ax.twinx()\n\
ax_twinx.plot(x,y2{})\n\
plt.sca(ax)\n",
&opt
)
.unwrap();
}
pub fn draw_3d<'a, T, U>(&mut self, x: &'a T, y: &'a T, z: &'a T)
where
T: AsVector<'a, U>,
U: 'a + std::fmt::Display + Num,
{
vector_to_array(&mut self.buffer, "x", x);
vector_to_array(&mut self.buffer, "y", y);
vector_to_array(&mut self.buffer, "z", z);
let opt = self.options();
write!(&mut self.buffer, "ax3d().plot(x,y,z{})\n", &opt).unwrap();
}
pub fn set_label(&mut self, label: &str) -> &mut Self {
self.label = String::from(label);
self
}
pub fn set_line_alpha(&mut self, alpha: f64) -> &mut Self {
self.line_alpha = alpha;
self
}
pub fn set_line_color(&mut self, color: &str) -> &mut Self {
self.line_color = String::from(color);
self
}
pub fn draw_ray(&mut self, xa: f64, ya: f64, endpoint: RayEndpoint) {
let opt = self.options();
match endpoint {
RayEndpoint::Coords(xb, yb) => write!(
&mut self.buffer,
"plt.axline(({},{}),({},{}){})\n",
xa, ya, xb, yb, &opt
)
.unwrap(),
RayEndpoint::Slope(m) => write!(
&mut self.buffer,
"plt.axline(({},{}),None,slope={}{})\n",
xa, ya, m, &opt
)
.unwrap(),
RayEndpoint::Horizontal => write!(&mut self.buffer, "plt.axhline({}{})\n", ya, &opt).unwrap(),
RayEndpoint::Vertical => write!(&mut self.buffer, "plt.axvline({}{})\n", xa, &opt).unwrap(),
}
}
pub fn set_line_style(&mut self, style: &str) -> &mut Self {
self.line_style = String::from(style);
self
}
pub fn set_line_width(&mut self, width: f64) -> &mut Self {
self.line_width = width;
self
}
pub fn set_marker_color(&mut self, color: &str) -> &mut Self {
self.marker_color = String::from(color);
self
}
pub fn set_marker_every(&mut self, every: usize) -> &mut Self {
self.marker_every = every;
self
}
pub fn set_marker_void(&mut self, flag: bool) -> &mut Self {
self.marker_void = flag;
self
}
pub fn set_marker_line_color(&mut self, color: &str) -> &mut Self {
self.marker_line_color = String::from(color);
self
}
pub fn set_marker_line_width(&mut self, width: f64) -> &mut Self {
self.marker_line_width = width;
self
}
pub fn set_marker_size(&mut self, size: f64) -> &mut Self {
self.marker_size = size;
self
}
pub fn set_marker_style(&mut self, style: &str) -> &mut Self {
self.marker_style = String::from(style);
self
}
pub fn set_stop_clip(&mut self, flag: bool) -> &mut Self {
self.stop_clip = flag;
self
}
pub fn set_extra(&mut self, extra: &str) -> &mut Self {
self.extra = extra.to_string();
self
}
fn options(&self) -> String {
let mut opt = String::new();
if self.label != "" {
write!(&mut opt, ",label=r'{}'", self.label).unwrap();
}
if self.line_alpha > 0.0 {
write!(&mut opt, ",alpha={}", self.line_alpha).unwrap();
}
if self.line_color != "" {
write!(&mut opt, ",color='{}'", self.line_color).unwrap();
}
if self.line_style != "" {
write!(&mut opt, ",linestyle='{}'", self.line_style).unwrap();
}
if self.line_width > 0.0 {
write!(&mut opt, ",linewidth={}", self.line_width).unwrap();
}
if !self.marker_void && self.marker_color != "" {
write!(&mut opt, ",markerfacecolor='{}'", self.marker_color).unwrap();
}
if self.marker_every > 0 {
write!(&mut opt, ",markevery={}", self.marker_every).unwrap();
}
if self.marker_void {
write!(&mut opt, ",markerfacecolor='none'").unwrap();
}
if self.marker_line_color != "" {
write!(&mut opt, ",markeredgecolor='{}'", self.marker_line_color).unwrap();
}
if self.marker_line_width > 0.0 {
write!(&mut opt, ",markeredgewidth={}", self.marker_line_width).unwrap();
}
if self.marker_size > 0.0 {
write!(&mut opt, ",markersize={}", self.marker_size).unwrap();
}
if self.marker_style != "" {
write!(&mut opt, ",marker={}", quote_marker(&self.marker_style)).unwrap();
}
if self.stop_clip {
write!(&mut opt, ",clip_on=False").unwrap();
}
if self.extra != "" {
write!(&mut opt, ",{}", self.extra).unwrap();
}
opt
}
}
impl GraphMaker for Curve {
fn get_buffer<'a>(&'a self) -> &'a String {
&self.buffer
}
fn clear_buffer(&mut self) {
self.buffer.clear();
}
}
#[cfg(test)]
mod tests {
use super::{Curve, RayEndpoint};
use crate::GraphMaker;
#[test]
fn new_works() {
let curve = Curve::new();
assert_eq!(curve.label.len(), 0);
assert_eq!(curve.line_alpha, 0.0);
assert_eq!(curve.line_color.len(), 0);
assert_eq!(curve.line_style.len(), 0);
assert_eq!(curve.line_width, 0.0);
assert_eq!(curve.marker_color.len(), 0);
assert_eq!(curve.marker_every, 0);
assert_eq!(curve.marker_void, false);
assert_eq!(curve.marker_line_color.len(), 0);
assert_eq!(curve.marker_line_width, 0.0);
assert_eq!(curve.marker_size, 0.0);
assert_eq!(curve.marker_style.len(), 0);
assert_eq!(curve.buffer.len(), 0);
}
#[test]
fn options_works() {
let mut curve = Curve::new();
curve
.set_label("my-curve")
.set_line_alpha(0.7)
.set_line_color("#b33434")
.set_line_style("-")
.set_line_width(3.0)
.set_marker_color("#4c4deb")
.set_marker_every(2)
.set_marker_void(false)
.set_marker_line_color("blue")
.set_marker_line_width(1.5)
.set_marker_size(8.0)
.set_marker_style("o")
.set_stop_clip(true);
let options = curve.options();
assert_eq!(
options,
",label=r'my-curve'\
,alpha=0.7\
,color='#b33434'\
,linestyle='-'\
,linewidth=3\
,markerfacecolor='#4c4deb'\
,markevery=2\
,markeredgecolor='blue'\
,markeredgewidth=1.5\
,markersize=8\
,marker='o'\
,clip_on=False"
);
let mut curve = Curve::new();
for i in 5..12 {
curve.set_marker_style(&format!("{}", i));
let options = curve.options();
assert_eq!(options, format!(",marker={}", i));
}
}
#[test]
fn points_methods_work() {
let mut curve = Curve::new();
curve.points_begin().points_add(1, 2).points_add(3, 4).points_end();
let b: &str = "xy=np.array([[1,2],[3,4],])\n\
plt.plot(xy[:,0],xy[:,1])\n";
assert_eq!(curve.buffer, b);
}
#[test]
fn points_3d_methods_work() {
let mut curve = Curve::new();
curve
.points_3d_begin()
.points_3d_add(1, 2, 3)
.points_3d_add(4, 5, 6)
.points_3d_end();
let b: &str = "\
xyz=np.array([[1,2,3],[4,5,6],])\n\
ax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2])\n";
assert_eq!(curve.buffer, b);
}
#[test]
fn draw_works() {
let x = &[1.0, 2.0, 3.0, 4.0, 5.0];
let y = &[1.0, 4.0, 9.0, 16.0, 25.0];
let mut curve = Curve::new();
curve.set_label("the-curve");
curve.draw(x, y);
let b: &str = "x=np.array([1,2,3,4,5,])\n\
y=np.array([1,4,9,16,25,])\n\
plt.plot(x,y,label=r'the-curve')\n";
assert_eq!(curve.buffer, b);
curve.clear_buffer();
assert_eq!(curve.buffer, "");
}
#[test]
fn draw_3d_works() {
let x = &[1.0, 2.0, 3.0, 4.0, 5.0];
let y = &[1.0, 4.0, 9.0, 16.0, 25.0];
let z = &[0.0, 0.0, 0.0, 1.0, 1.0];
let mut curve = Curve::new();
curve.set_label("the-curve");
curve.draw_3d(x, y, z);
let b: &str = "x=np.array([1,2,3,4,5,])\n\
y=np.array([1,4,9,16,25,])\n\
z=np.array([0,0,0,1,1,])\n\
ax3d().plot(x,y,z,label=r'the-curve')\n";
assert_eq!(curve.buffer, b);
}
#[test]
fn derive_works() {
let endpoint = RayEndpoint::Coords(8.0, 0.5);
let cloned = endpoint.clone();
assert_eq!(format!("{:?}", endpoint), "Coords(8.0, 0.5)");
assert_eq!(format!("{:?}", cloned), "Coords(8.0, 0.5)");
}
#[test]
fn draw_ray_works() {
let mut ray = Curve::new();
ray.draw_ray(2.0, 0.0, RayEndpoint::Coords(8.0, 0.5));
ray.draw_ray(2.0, 0.0, RayEndpoint::Slope(0.2));
ray.draw_ray(2.0, 0.0, RayEndpoint::Horizontal);
ray.draw_ray(2.0, 0.0, RayEndpoint::Vertical);
let b: &str = "plt.axline((2,0),(8,0.5))\n\
plt.axline((2,0),None,slope=0.2)\n\
plt.axhline(0)\n\
plt.axvline(2)\n";
assert_eq!(ray.buffer, b);
}
}