runmat_plot/export/
web.rs1use std::collections::HashMap;
6use std::path::Path;
7
8pub struct WebExporter {
10 settings: WebExportSettings,
12 widgets: HashMap<String, WidgetInstance>,
14}
15
16#[derive(Debug, Clone)]
18pub struct WebExportSettings {
19 pub width: u32,
21 pub height: u32,
23 pub enable_webassembly: bool,
25 pub enable_webgl: bool,
27 pub update_fps: u32,
29 pub enable_controls: bool,
31 pub include_styles: bool,
33}
34
35#[derive(Debug, Clone)]
37pub struct WidgetInstance {
38 pub id: String,
40 pub render_data: Vec<SerializedRenderData>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct SerializedRenderData {
47 pub vertices: Vec<f32>,
49 pub indices: Vec<u32>,
51 pub pipeline_type: String,
53}
54
55impl Default for WebExportSettings {
56 fn default() -> Self {
57 Self {
58 width: 800,
59 height: 600,
60 enable_webassembly: true,
61 enable_webgl: true,
62 update_fps: 60,
63 enable_controls: true,
64 include_styles: true,
65 }
66 }
67}
68
69use serde::{Deserialize, Serialize};
71
72impl WebExporter {
73 pub fn new() -> Self {
75 Self {
76 settings: WebExportSettings::default(),
77 widgets: HashMap::new(),
78 }
79 }
80
81 pub fn with_settings(settings: WebExportSettings) -> Self {
83 Self {
84 settings,
85 widgets: HashMap::new(),
86 }
87 }
88
89 pub fn export_html<P: AsRef<Path>>(
91 &mut self,
92 _figure: &mut crate::plots::Figure,
93 path: P,
94 ) -> Result<(), String> {
95 let html_content = self.render_to_html()?;
96 std::fs::write(path, html_content)
97 .map_err(|e| format!("Failed to write HTML file: {e}"))?;
98 println!("DEBUG: HTML widget export completed successfully");
99 Ok(())
100 }
101
102 pub fn render_to_html(&mut self) -> Result<String, String> {
104 println!("DEBUG: Starting HTML widget export");
105
106 let widget_id = self.generate_widget_id();
107
108 let html = format!(
109 r#"<!DOCTYPE html>
110<html lang="en">
111<head>
112 <meta charset="UTF-8">
113 <meta name="viewport" content="width=device-width, initial-scale=1.0">
114 <title>RunMat Interactive Plot</title>
115</head>
116<body>
117 <div id="runmat-container-{}" style="width: {}px; height: {}px; border: 1px solid #ddd;">
118 <canvas id="runmat-canvas-{}" width="{}" height="{}"></canvas>
119 <div style="position: absolute; top: 10px; right: 10px;">
120 <button onclick="alert('RunMat Interactive Plot')">Info</button>
121 </div>
122 </div>
123
124 <script>
125 console.log('RunMat interactive widget placeholder initialized');
126 const canvas = document.getElementById('runmat-canvas-{}');
127 const ctx = canvas.getContext('2d');
128
129 // Draw placeholder content
130 ctx.fillStyle = '#f0f0f0';
131 ctx.fillRect(0, 0, canvas.width, canvas.height);
132
133 ctx.fillStyle = '#333';
134 ctx.font = '20px Arial';
135 ctx.textAlign = 'center';
136 ctx.fillText('RunMat Interactive Plot', canvas.width/2, canvas.height/2 - 10);
137 ctx.fillText('WebGL Widget Ready', canvas.width/2, canvas.height/2 + 20);
138 </script>
139</body>
140</html>"#,
141 widget_id,
142 self.settings.width,
143 self.settings.height,
144 widget_id,
145 self.settings.width,
146 self.settings.height,
147 widget_id
148 );
149
150 println!(
151 "DEBUG: HTML widget render completed, {} characters generated",
152 html.len()
153 );
154 Ok(html)
155 }
156
157 fn generate_widget_id(&self) -> String {
159 use std::sync::atomic::{AtomicU64, Ordering};
160 use std::time::{SystemTime, UNIX_EPOCH};
161
162 static COUNTER: AtomicU64 = AtomicU64::new(0);
163
164 let timestamp = SystemTime::now()
165 .duration_since(UNIX_EPOCH)
166 .unwrap_or_default()
167 .as_micros();
168 let inc = COUNTER.fetch_add(1, Ordering::Relaxed);
169
170 format!("runmat_{timestamp}_{inc}")
172 }
173
174 pub fn set_settings(&mut self, settings: WebExportSettings) {
176 self.settings = settings;
177 }
178
179 pub fn settings(&self) -> &WebExportSettings {
181 &self.settings
182 }
183
184 pub fn widgets(&self) -> &HashMap<String, WidgetInstance> {
186 &self.widgets
187 }
188}
189
190impl Default for WebExporter {
191 fn default() -> Self {
192 Self::new()
193 }
194}