use crate::curves::Curve;
use crate::error::CurveError;
use crate::geometrics::{PlotBuilder, Plottable};
use crate::visualization::Graph;
impl Plottable for Curve {
type Error = CurveError;
fn plot(&self) -> PlotBuilder<Self>
where
Self: Sized,
{
PlotBuilder {
data: self.clone(),
options: self.graph_config(),
}
}
}
impl Plottable for Vec<Curve> {
type Error = CurveError;
fn plot(&self) -> PlotBuilder<Self>
where
Self: Sized,
{
PlotBuilder {
data: self.clone(),
options: self.graph_config(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::curves::Point2D;
use crate::geometrics::GeometricObject;
use rust_decimal_macros::dec;
#[cfg(feature = "plotly")]
use {std::fs, std::path::Path, tracing::error};
#[cfg(feature = "plotly")]
fn cleanup_image(filename: &Path) {
match fs::remove_file(filename) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
}
Err(e) => {
error!("Failed to remove {}: {}", filename.display(), e);
}
}
}
fn create_test_curves() -> (Curve, Curve, Curve) {
let p1_1 = Point2D::new(dec!(0.0), dec!(0.0));
let p1_2 = Point2D::new(dec!(1.0), dec!(1.0));
let p1_3 = Point2D::new(dec!(2.0), dec!(4.0));
let p2_1 = Point2D::new(dec!(0.0), dec!(1.0));
let p2_2 = Point2D::new(dec!(1.0), dec!(2.0));
let p2_3 = Point2D::new(dec!(2.0), dec!(5.0));
let p3_1 = Point2D::new(dec!(0.0), dec!(2.0));
let p3_2 = Point2D::new(dec!(1.0), dec!(3.0));
let p3_3 = Point2D::new(dec!(2.0), dec!(6.0));
let points1 = vec![&p1_1, &p1_2, &p1_3];
let points2 = vec![&p2_1, &p2_2, &p2_3];
let points3 = vec![&p3_1, &p3_2, &p3_3];
(
Curve::from_vector(points1),
Curve::from_vector(points2),
Curve::from_vector(points3),
)
}
#[test]
fn test_single_curve_plot_bis() {
let p1 = Point2D::new(dec!(0.0), dec!(0.0));
let p2 = Point2D::new(dec!(1.0), dec!(1.0));
let p3 = Point2D::new(dec!(2.0), dec!(4.0));
let points = vec![&p1, &p2, &p3];
let curve = Curve::from_vector(points);
curve
.plot()
.title("Test Curve")
.x_label("X Axis")
.y_label("Y Axis")
.dimensions(800, 600);
#[cfg(feature = "plotly")]
{
let file_path_html = "single_curve_test.html".as_ref();
curve.write_html(file_path_html).unwrap();
cleanup_image(file_path_html);
#[cfg(feature = "static_export")]
{
let file_path_png = "single_curve_test.png".as_ref();
if curve.write_png(file_path_png).is_ok() {
cleanup_image(file_path_png);
}
}
}
}
#[test]
fn test_multiple_curves_plot() {
let p1_1 = Point2D::new(dec!(0.0), dec!(0.0));
let p1_2 = Point2D::new(dec!(1.0), dec!(1.0));
let p1_3 = Point2D::new(dec!(2.0), dec!(4.0));
let p2_1 = Point2D::new(dec!(0.0), dec!(1.0));
let p2_2 = Point2D::new(dec!(1.0), dec!(2.0));
let p2_3 = Point2D::new(dec!(2.0), dec!(5.0));
let points1 = vec![&p1_1, &p1_2, &p1_3];
let points2 = vec![&p2_1, &p2_2, &p2_3];
let curve1 = Curve::from_vector(points1);
let curve2 = Curve::from_vector(points2);
let curve_vector = vec![curve1.clone(), curve2.clone()];
curve_vector
.plot()
.title("Multiple Curves")
.x_label("X Axis")
.y_label("Y Axis")
.dimensions(800, 600);
#[cfg(feature = "plotly")]
{
let file_path_html = "multiple_curves_test.html".as_ref();
curve_vector.write_html(file_path_html).unwrap();
cleanup_image(file_path_html);
#[cfg(feature = "static_export")]
{
let file_path_png = "multiple_curves_test.png".as_ref();
if curve_vector.write_png(file_path_png).is_ok() {
cleanup_image(file_path_png);
}
}
}
}
#[test]
fn test_single_curve_plot() {
let p1_1 = Point2D::new(dec!(0.0), dec!(0.0));
let p1_2 = Point2D::new(dec!(1.0), dec!(1.0));
let p1_3 = Point2D::new(dec!(2.0), dec!(4.0));
let points = vec![&p1_1, &p1_2, &p1_3];
let curve = Curve::from_vector(points);
curve
.plot()
.title("Test Curve")
.x_label("X Axis")
.y_label("Y Axis")
.dimensions(800, 600);
#[cfg(feature = "plotly")]
{
let file_path_html = "single_curve_test.html".as_ref();
curve.write_html(file_path_html).unwrap();
cleanup_image(file_path_html);
#[cfg(feature = "static_export")]
{
let file_path_png = "single_curve_test.png".as_ref();
if curve.write_png(file_path_png).is_ok() {
cleanup_image(file_path_png);
}
}
}
}
#[test]
fn test_multiple_curves_plot_bis() {
let (curve1, curve2, curve3) = create_test_curves();
let curve_vector = vec![curve1.clone(), curve2.clone(), curve3.clone()];
curve_vector
.plot()
.title("Multiple Curves")
.x_label("X Axis")
.y_label("Y Axis")
.dimensions(1000, 700);
#[cfg(feature = "plotly")]
{
let file_path_html = "multiple_curves_test.html".as_ref();
curve_vector.write_html(file_path_html).unwrap();
cleanup_image(file_path_html);
#[cfg(feature = "static_export")]
{
let file_path_png = "multiple_curves_test.png".as_ref();
if curve_vector.write_png(file_path_png).is_ok() {
cleanup_image(file_path_png);
}
}
}
}
#[test]
fn test_plot_with_custom_line_width() {
let (curve1, curve2, _) = create_test_curves();
let curve_vector = vec![curve1.clone(), curve2.clone()];
curve_vector
.plot()
.title("Thick Line Curves")
.dimensions(800, 600);
#[cfg(feature = "plotly")]
{
let file_path_html = "thick_line_curves_test.html".as_ref();
curve_vector.write_html(file_path_html).unwrap();
cleanup_image(file_path_html);
#[cfg(feature = "static_export")]
{
let file_path_png = "thick_line_curves_test.png".as_ref();
if curve_vector.write_png(file_path_png).is_ok() {
cleanup_image(file_path_png);
}
}
}
}
}
#[cfg(test)]
mod tests_extended {
use super::*;
struct MockChart {
pub x_desc: String,
pub y_desc: String,
}
impl MockChart {
pub fn new() -> Self {
MockChart {
x_desc: String::new(),
y_desc: String::new(),
}
}
pub fn configure_mesh(&mut self) -> &mut Self {
self
}
pub fn x_label_formatter(&mut self, _formatter: &dyn Fn(f64) -> String) -> &mut Self {
self
}
pub fn y_label_formatter(&mut self, _formatter: &dyn Fn(f64) -> String) -> &mut Self {
self
}
pub fn x_desc(&mut self, desc: &str) -> &mut Self {
self.x_desc = desc.to_string();
self
}
pub fn y_desc(&mut self, desc: &str) -> &mut Self {
self.y_desc = desc.to_string();
self
}
}
#[test]
fn test_map_err_to_std_error() {
let result: Result<(), CurveError> =
Err(std::io::Error::other("Test error")).map_err(|e| CurveError::StdError {
reason: e.to_string(),
});
assert!(result.is_err());
let error = result.unwrap_err();
match error {
CurveError::StdError { reason } => {
assert_eq!(reason, "Test error");
}
_ => panic!("Unexpected error type"),
}
}
#[test]
fn test_configure_chart_mesh() {
let mut chart = MockChart::new(); chart
.configure_mesh()
.x_label_formatter(&|v| format!("{v:.2}"))
.y_label_formatter(&|v| format!("{v:.2}"))
.x_desc("X-axis")
.y_desc("Y-axis");
assert_eq!(chart.x_desc, "X-axis");
assert_eq!(chart.y_desc, "Y-axis");
}
#[test]
fn test_draw_series_error() {
let result: Result<(), CurveError> =
Err("Draw error".to_string()).map_err(|e| CurveError::StdError { reason: e });
assert!(result.is_err());
let error = result.unwrap_err();
match error {
CurveError::StdError { reason } => {
assert_eq!(reason, "Draw error");
}
_ => panic!("Unexpected error type"),
}
}
}