use runmat_time::unix_timestamp_us;
use std::collections::HashMap;
use std::path::Path;
pub struct WebExporter {
settings: WebExportSettings,
widgets: HashMap<String, WidgetInstance>,
}
#[derive(Debug, Clone)]
pub struct WebExportSettings {
pub width: u32,
pub height: u32,
pub enable_webassembly: bool,
pub enable_webgl: bool,
pub update_fps: u32,
pub enable_controls: bool,
pub include_styles: bool,
}
#[derive(Debug, Clone)]
pub struct WidgetInstance {
pub id: String,
pub render_data: Vec<SerializedRenderData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializedRenderData {
pub vertices: Vec<f32>,
pub indices: Vec<u32>,
pub pipeline_type: String,
}
impl Default for WebExportSettings {
fn default() -> Self {
Self {
width: 800,
height: 600,
enable_webassembly: true,
enable_webgl: true,
update_fps: 60,
enable_controls: true,
include_styles: true,
}
}
}
use serde::{Deserialize, Serialize};
impl WebExporter {
pub fn new() -> Self {
Self {
settings: WebExportSettings::default(),
widgets: HashMap::new(),
}
}
pub fn with_settings(settings: WebExportSettings) -> Self {
Self {
settings,
widgets: HashMap::new(),
}
}
pub fn export_html<P: AsRef<Path>>(
&mut self,
_figure: &mut crate::plots::Figure,
path: P,
) -> Result<(), String> {
let html_content = self.render_to_html()?;
std::fs::write(path, html_content)
.map_err(|e| format!("Failed to write HTML file: {e}"))?;
log::debug!(target: "runmat_plot", "html widget export completed");
Ok(())
}
pub fn render_to_html(&mut self) -> Result<String, String> {
log::debug!(target: "runmat_plot", "html widget export start");
let widget_id = self.generate_widget_id();
let html = format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RunMat Interactive Plot</title>
</head>
<body>
<div id="runmat-container-{}" style="width: {}px; height: {}px; border: 1px solid #ddd;">
<canvas id="runmat-canvas-{}" width="{}" height="{}"></canvas>
<div style="position: absolute; top: 10px; right: 10px;">
<button onclick="alert('RunMat Interactive Plot')">Info</button>
</div>
</div>
<script>
console.log('RunMat interactive widget placeholder initialized');
const canvas = document.getElementById('runmat-canvas-{}');
const ctx = canvas.getContext('2d');
// Draw placeholder content
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#333';
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.fillText('RunMat Interactive Plot', canvas.width/2, canvas.height/2 - 10);
ctx.fillText('WebGL Widget Ready', canvas.width/2, canvas.height/2 + 20);
</script>
</body>
</html>"#,
widget_id,
self.settings.width,
self.settings.height,
widget_id,
self.settings.width,
self.settings.height,
widget_id
);
log::debug!(target: "runmat_plot", "html widget size chars={}", html.len());
Ok(html)
}
fn generate_widget_id(&self) -> String {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let timestamp = unix_timestamp_us();
let inc = COUNTER.fetch_add(1, Ordering::Relaxed);
format!("runmat_{timestamp}_{inc}")
}
pub fn set_settings(&mut self, settings: WebExportSettings) {
self.settings = settings;
}
pub fn settings(&self) -> &WebExportSettings {
&self.settings
}
pub fn widgets(&self) -> &HashMap<String, WidgetInstance> {
&self.widgets
}
}
impl Default for WebExporter {
fn default() -> Self {
Self::new()
}
}