use std::error::Error;
use std::path::Path;
use crate::visualization::{VisualizationData, VisualizationMetadata, VisualizationOptions};
#[cfg(feature = "plotly_backend")]
mod plotly;
#[cfg(feature = "plotly_backend")]
mod plotly_interactive;
#[cfg(feature = "plotters_backend")]
mod plotters;
#[cfg(feature = "plotly_backend")]
pub use self::plotly::PlotlyBackend;
#[cfg(feature = "plotly_backend")]
pub use self::plotly_interactive::{PlotlyInteractiveBackend, PlotlyInteractiveBackendInterface};
#[cfg(feature = "plotters_backend")]
pub use self::plotters::PlottersBackend;
pub trait PlottingBackend {
fn save_to_file(
&self,
data: &VisualizationData,
metadata: &VisualizationMetadata,
options: &VisualizationOptions,
path: impl AsRef<Path>,
) -> Result<(), Box<dyn Error>>;
fn render_svg(
&self,
data: &VisualizationData,
metadata: &VisualizationMetadata,
options: &VisualizationOptions,
) -> Result<Vec<u8>, Box<dyn Error>>;
fn render_png(
&self,
data: &VisualizationData,
metadata: &VisualizationMetadata,
options: &VisualizationOptions,
) -> Result<Vec<u8>, Box<dyn Error>>;
}
#[allow(dead_code)]
pub fn default_backend() -> impl PlottingBackend {
#[cfg(feature = "plotly_backend")]
{
PlotlyBackend::new()
}
#[cfg(not(feature = "plotly_backend"))]
#[cfg(feature = "plotters_backend")]
{
PlottersBackend::new()
}
#[cfg(not(feature = "plotly_backend"))]
#[cfg(not(feature = "plotters_backend"))]
{
struct NoopBackend;
impl PlottingBackend for NoopBackend {
fn save_to_file(
&self,
_data: &VisualizationData,
_metadata: &VisualizationMetadata,
_options: &VisualizationOptions,
_path: impl AsRef<Path>,
) -> Result<(), Box<dyn Error>> {
Err("No visualization backend available. Enable either 'plotly_backend' or 'plotters_backend' feature.".into())
}
fn render_svg(
&self,
_data: &VisualizationData,
_metadata: &VisualizationMetadata,
_options: &VisualizationOptions,
) -> Result<Vec<u8>, Box<dyn Error>> {
Err("No visualization backend available. Enable either 'plotly_backend' or 'plotters_backend' feature.".into())
}
fn render_png(
&self,
_data: &VisualizationData,
_metadata: &VisualizationMetadata,
_options: &VisualizationOptions,
) -> Result<Vec<u8>, Box<dyn Error>> {
Err("No visualization backend available. Enable either 'plotly_backend' or 'plotters_backend' feature.".into())
}
}
NoopBackend
}
}
#[cfg(feature = "plotly_backend")]
#[allow(dead_code)]
pub fn default_interactive_backend() -> PlotlyInteractiveBackend {
PlotlyInteractiveBackend::new()
}
#[allow(dead_code)]
pub fn enhance_visualization(
data: &VisualizationData,
metadata: &VisualizationMetadata,
) -> VisualizationData {
let mut enhanced = data.clone();
match metadata.plot_type {
crate::visualization::PlotType::Line => {
if data.x.len() > 5 && data.y.len() > 5 {
let n = data.x.len() as f64;
let sum_x: f64 = data.x.iter().sum();
let sum_y: f64 = data.y.iter().sum();
let sum_xy: f64 = data.x.iter().zip(data.y.iter()).map(|(&x, &y)| x * y).sum();
let sum_xx: f64 = data.x.iter().map(|&x| x * x).sum();
let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
let intercept = (sum_y - slope * sum_x) / n;
let trend_line: Vec<f64> = data.x.iter().map(|&x| slope * x + intercept).collect();
enhanced
.auxiliary_data
.insert("trend_line".to_string(), trend_line);
enhanced
.auxiliary_metadata
.insert("trend_slope".to_string(), slope.to_string());
enhanced
.auxiliary_metadata
.insert("trend_intercept".to_string(), intercept.to_string());
}
}
crate::visualization::PlotType::Scatter => {
if data.x.len() > 0 && data.y.len() > 0 {
let center_x = data.x.iter().sum::<f64>() / data.x.len() as f64;
let center_y = data.y.iter().sum::<f64>() / data.y.len() as f64;
enhanced
.auxiliary_data
.insert("center_x".to_string(), vec![center_x]);
enhanced
.auxiliary_data
.insert("center_y".to_string(), vec![center_y]);
}
}
_ => {
}
}
enhanced
}