pub mod core;
pub mod data;
pub mod plots;
pub mod export;
#[cfg(feature = "gui")]
pub mod gui;
#[cfg(feature = "jupyter")]
pub mod jupyter;
pub mod styling;
pub use plots::*;
#[cfg(feature = "gui")]
pub use gui::{PlotWindow, WindowConfig};
#[cfg(feature = "gui")]
pub use gui::{is_window_available, show_plot_sequential};
#[cfg(feature = "gui")]
pub use gui::{
get_gui_manager, health_check_global, initialize_gui_manager, is_main_thread,
register_main_thread, show_plot_global, GuiErrorCode, GuiOperationResult, GuiThreadManager,
};
pub use export::*;
#[derive(Debug, Clone)]
pub struct PlotOptions {
pub width: u32,
pub height: u32,
pub dpi: f32,
pub background_color: [f32; 4],
}
impl Default for PlotOptions {
fn default() -> Self {
Self {
width: 800,
height: 600,
dpi: 96.0,
background_color: [0.0, 0.0, 0.0, 1.0], }
}
}
pub fn show_plot_unified(
figure: plots::Figure,
output_path: Option<&str>,
) -> Result<String, String> {
match output_path {
Some(path) => {
render_figure_to_file(figure, path)
}
None => {
#[cfg(feature = "gui")]
{
show_plot_sequential(figure)
}
#[cfg(not(feature = "gui"))]
{
Err(
"GUI feature not enabled. Build with --features gui for interactive plotting."
.to_string(),
)
}
}
}
}
fn render_figure_to_file(figure: plots::Figure, path: &str) -> Result<String, String> {
#[cfg(feature = "gui")]
{
show_plot_sequential(figure)?;
Ok(format!("Plot displayed interactively. Static export to {path} not yet implemented - please screenshot the window."))
}
#[cfg(not(feature = "gui"))]
{
Err("GUI feature not enabled. Cannot render plots without GUI.".to_string())
}
}
pub fn plot_line(xs: &[f64], ys: &[f64], path: &str, _options: PlotOptions) -> Result<(), String> {
if xs.len() != ys.len() {
return Err("input length mismatch".into());
}
let line_plot = plots::LinePlot::new(xs.to_vec(), ys.to_vec())
.map_err(|e| format!("Failed to create line plot: {e}"))?
.with_label("Data")
.with_style(
glam::Vec4::new(0.0, 0.4, 0.8, 1.0), 2.0,
plots::LineStyle::Solid,
);
let mut figure = plots::Figure::new()
.with_title("Line Plot")
.with_labels("X", "Y")
.with_grid(true);
figure.add_line_plot(line_plot);
show_plot_unified(figure, Some(path))?;
Ok(())
}
pub fn plot_scatter(
xs: &[f64],
ys: &[f64],
path: &str,
_options: PlotOptions,
) -> Result<(), String> {
if xs.len() != ys.len() {
return Err("input length mismatch".into());
}
let scatter_plot = plots::ScatterPlot::new(xs.to_vec(), ys.to_vec())
.map_err(|e| format!("Failed to create scatter plot: {e}"))?
.with_label("Data")
.with_style(
glam::Vec4::new(0.8, 0.2, 0.2, 1.0), 5.0,
plots::MarkerStyle::Circle,
);
let mut figure = plots::Figure::new()
.with_title("Scatter Plot")
.with_labels("X", "Y")
.with_grid(true);
figure.add_scatter_plot(scatter_plot);
show_plot_unified(figure, Some(path))?;
Ok(())
}
pub fn plot_bar(
labels: &[String],
values: &[f64],
path: &str,
_options: PlotOptions,
) -> Result<(), String> {
if labels.len() != values.len() {
return Err("labels and values length mismatch".into());
}
let bar_chart = plots::BarChart::new(labels.to_vec(), values.to_vec())
.map_err(|e| format!("Failed to create bar chart: {e}"))?
.with_label("Values")
.with_style(glam::Vec4::new(0.2, 0.6, 0.3, 1.0), 0.8);
let mut figure = plots::Figure::new()
.with_title("Bar Chart")
.with_labels("Categories", "Values")
.with_grid(true);
figure.add_bar_chart(bar_chart);
show_plot_unified(figure, Some(path))?;
Ok(())
}
pub fn plot_histogram(
data: &[f64],
bins: usize,
path: &str,
_options: PlotOptions,
) -> Result<(), String> {
let histogram = plots::Histogram::new(data.to_vec(), bins)
.map_err(|e| format!("Failed to create histogram: {e}"))?
.with_label("Frequency")
.with_style(glam::Vec4::new(0.6, 0.3, 0.7, 1.0), false);
let mut figure = plots::Figure::new()
.with_title("Histogram")
.with_labels("Values", "Frequency")
.with_grid(true);
figure.add_histogram(histogram);
show_plot_unified(figure, Some(path))?;
Ok(())
}
pub fn show_interactive_platform_optimal(figure: plots::Figure) -> Result<String, String> {
show_plot_unified(figure, None)
}