1use crate::core::RenderData;
6use crate::plots::Figure;
7use std::fmt::Write;
8use std::path::Path;
9
10pub struct VectorExporter {
12 settings: VectorExportSettings,
14}
15
16#[derive(Debug, Clone)]
18pub struct VectorExportSettings {
19 pub width: f32,
21 pub height: f32,
23 pub background_color: [f32; 4],
25 pub stroke_width: f32,
27 pub include_metadata: bool,
29 pub anti_aliasing: bool,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq)]
35pub enum VectorFormat {
36 Svg,
37 Pdf,
38 Eps,
39}
40
41impl Default for VectorExportSettings {
42 fn default() -> Self {
43 Self {
44 width: 800.0,
45 height: 600.0,
46 background_color: [1.0, 1.0, 1.0, 1.0], stroke_width: 1.0,
48 include_metadata: true,
49 anti_aliasing: true,
50 }
51 }
52}
53
54impl VectorExporter {
55 pub fn new() -> Self {
57 Self {
58 settings: VectorExportSettings::default(),
59 }
60 }
61
62 pub fn with_settings(settings: VectorExportSettings) -> Self {
64 Self { settings }
65 }
66
67 pub fn export_svg<P: AsRef<Path>>(&self, figure: &mut Figure, path: P) -> Result<(), String> {
69 let svg_content = self.render_to_svg(figure)?;
70 std::fs::write(path, svg_content).map_err(|e| format!("Failed to write SVG file: {e}"))?;
71 println!("DEBUG: SVG export completed successfully");
72 Ok(())
73 }
74
75 pub fn export_pdf<P: AsRef<Path>>(&self, _figure: &mut Figure, _path: P) -> Result<(), String> {
77 Err("PDF export not yet implemented".to_string())
79 }
80
81 pub fn render_to_svg(&self, _figure: &mut Figure) -> Result<String, String> {
83 println!("DEBUG: Starting SVG vector export render");
84
85 let mut svg = String::new();
86
87 writeln!(
89 &mut svg,
90 r#"<?xml version="1.0" encoding="UTF-8"?>
91<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
92 self.settings.width, self.settings.height
93 ).map_err(|e| format!("SVG write error: {e}"))?;
94
95 if self.settings.background_color[3] > 0.0 {
97 writeln!(
98 &mut svg,
99 r#" <rect width="100%" height="100%" fill="{}"/>"#,
100 self.color_to_hex(&self.settings.background_color)
101 )
102 .map_err(|e| format!("SVG write error: {e}"))?;
103 }
104
105 if self.settings.include_metadata {
107 writeln!(
108 &mut svg,
109 " <metadata>\n <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n <rdf:Description>\n <dc:creator xmlns:dc=\"http://purl.org/dc/elements/1.1/\">RunMat Plot System</dc:creator>\n </rdf:Description>\n </rdf:RDF>\n </metadata>"
110 ).map_err(|e| format!("SVG write error: {e}"))?;
111 }
112
113 writeln!(&mut svg, " <!-- Plot data will be rendered here -->")
116 .map_err(|e| format!("SVG write error: {e}"))?;
117
118 writeln!(&mut svg, "</svg>").map_err(|e| format!("SVG write error: {e}"))?;
123
124 println!(
125 "DEBUG: SVG render completed, {} characters generated",
126 svg.len()
127 );
128 Ok(svg)
129 }
130
131 #[allow(dead_code)]
134 fn add_render_data_to_svg(
135 &self,
136 svg: &mut String,
137 render_data: &RenderData,
138 ) -> Result<(), String> {
139 match render_data.pipeline_type {
140 crate::core::PipelineType::Lines => {
141 self.add_lines_to_svg(svg, render_data)?;
142 }
143 crate::core::PipelineType::Points => {
144 self.add_points_to_svg(svg, render_data)?;
145 }
146 crate::core::PipelineType::Triangles => {
147 self.add_triangles_to_svg(svg, render_data)?;
148 }
149 crate::core::PipelineType::PointCloud => {
150 self.add_points_to_svg(svg, render_data)?;
151 }
152 }
153 Ok(())
154 }
155
156 #[allow(dead_code)]
158 fn add_lines_to_svg(&self, svg: &mut String, render_data: &RenderData) -> Result<(), String> {
159 if render_data.vertices.len() < 2 {
160 return Ok(());
161 }
162
163 writeln!(svg, " <g>").map_err(|e| format!("SVG write error: {e}"))?;
165
166 for chunk in render_data.vertices.chunks(2) {
167 if chunk.len() == 2 {
168 let start = &chunk[0];
169 let end = &chunk[1];
170
171 let start_screen = self.world_to_screen(start.position);
172 let end_screen = self.world_to_screen(end.position);
173
174 writeln!(
175 svg,
176 r#" <line x1="{}" y1="{}" x2="{}" y2="{}" stroke="{}" stroke-width="{}"/>"#,
177 start_screen[0],
178 start_screen[1],
179 end_screen[0],
180 end_screen[1],
181 self.color_to_hex(&start.color),
182 self.settings.stroke_width
183 )
184 .map_err(|e| format!("SVG write error: {e}"))?;
185 }
186 }
187
188 writeln!(svg, " </g>").map_err(|e| format!("SVG write error: {e}"))?;
189 Ok(())
190 }
191
192 #[allow(dead_code)]
194 fn add_points_to_svg(&self, svg: &mut String, render_data: &RenderData) -> Result<(), String> {
195 writeln!(svg, " <g>").map_err(|e| format!("SVG write error: {e}"))?;
196
197 for vertex in &render_data.vertices {
198 let screen_pos = self.world_to_screen(vertex.position);
199 let radius = self.settings.stroke_width * 2.0;
200
201 writeln!(
202 svg,
203 r#" <circle cx="{}" cy="{}" r="{}" fill="{}"/>"#,
204 screen_pos[0],
205 screen_pos[1],
206 radius,
207 self.color_to_hex(&vertex.color)
208 )
209 .map_err(|e| format!("SVG write error: {e}"))?;
210 }
211
212 writeln!(svg, " </g>").map_err(|e| format!("SVG write error: {e}"))?;
213 Ok(())
214 }
215
216 #[allow(dead_code)]
218 fn add_triangles_to_svg(
219 &self,
220 svg: &mut String,
221 render_data: &RenderData,
222 ) -> Result<(), String> {
223 writeln!(svg, " <g>").map_err(|e| format!("SVG write error: {e}"))?;
224
225 for triangle in render_data.vertices.chunks(3) {
226 if triangle.len() == 3 {
227 let p1 = self.world_to_screen(triangle[0].position);
228 let p2 = self.world_to_screen(triangle[1].position);
229 let p3 = self.world_to_screen(triangle[2].position);
230
231 writeln!(
232 svg,
233 r#" <polygon points="{},{} {},{} {},{}" fill="{}"/>"#,
234 p1[0],
235 p1[1],
236 p2[0],
237 p2[1],
238 p3[0],
239 p3[1],
240 self.color_to_hex(&triangle[0].color)
241 )
242 .map_err(|e| format!("SVG write error: {e}"))?;
243 }
244 }
245
246 writeln!(svg, " </g>").map_err(|e| format!("SVG write error: {e}"))?;
247 Ok(())
248 }
249
250 #[allow(dead_code)]
252 fn world_to_screen(&self, world_pos: [f32; 3]) -> [f32; 2] {
253 [
256 (world_pos[0] + 1.0) * 0.5 * self.settings.width,
257 (1.0 - world_pos[1]) * 0.5 * self.settings.height,
258 ]
259 }
260
261 fn color_to_hex(&self, color: &[f32; 4]) -> String {
263 format!(
264 "#{:02x}{:02x}{:02x}",
265 (color[0] * 255.0) as u8,
266 (color[1] * 255.0) as u8,
267 (color[2] * 255.0) as u8
268 )
269 }
270
271 pub fn set_settings(&mut self, settings: VectorExportSettings) {
273 self.settings = settings;
274 }
275
276 pub fn settings(&self) -> &VectorExportSettings {
278 &self.settings
279 }
280}
281
282impl Default for VectorExporter {
283 fn default() -> Self {
284 Self::new()
285 }
286}