xdl_viz3d_threejs/
lib.rs

1//! Three.js-based volume rendering for XDL
2//!
3//! This crate provides volume visualization using Three.js and WebGL,
4//! as an alternative to the WebGPU-based xdl-viz3d implementation.
5
6use std::process::Command;
7use xdl_core::{XdlError, XdlResult};
8
9pub mod colormaps;
10pub mod shaders;
11pub mod templates;
12
13/// Launch Three.js volume visualization in Tauri viewer
14pub fn launch_visualization(
15    volume_data: Vec<f32>,
16    dims: [usize; 3],
17    colormap: &str,
18    title: Option<&str>,
19) -> XdlResult<()> {
20    let title = title.unwrap_or("3D Volume Visualization");
21
22    // Generate HTML
23    let html = templates::generate_volume_html(
24        &volume_data,
25        dims,
26        colormap,
27        title,
28        0.1, // Default threshold
29        0.8, // Default opacity
30    );
31
32    // Write to temp file
33    let temp_dir = std::env::temp_dir();
34    let temp_file = temp_dir.join(format!("xdl_viz3d_{}.html", std::process::id()));
35
36    std::fs::write(&temp_file, html)
37        .map_err(|e| XdlError::RuntimeError(format!("Failed to write volume HTML: {}", e)))?;
38
39    // Find xdl-chart-viewer
40    let exe_dir = std::env::current_exe()
41        .map_err(|e| XdlError::RuntimeError(format!("Cannot find exe: {}", e)))?
42        .parent()
43        .ok_or_else(|| XdlError::RuntimeError("Cannot find parent directory".to_string()))?
44        .to_path_buf();
45
46    // Try with .exe extension first (Windows), then without
47    let viewer_path = if cfg!(windows) {
48        let with_exe = exe_dir.join("xdl-chart-viewer.exe");
49        if with_exe.exists() {
50            with_exe
51        } else {
52            exe_dir.join("xdl-chart-viewer")
53        }
54    } else {
55        exe_dir.join("xdl-chart-viewer")
56    };
57
58    // Launch viewer
59    Command::new(viewer_path)
60        .args([
61            "--html-file",
62            temp_file.to_str().unwrap(),
63            "--title",
64            title,
65            "--width",
66            "1280",
67            "--height",
68            "720",
69        ])
70        .spawn()
71        .map_err(|e| XdlError::RuntimeError(format!("Failed to launch viewer: {}", e)))?;
72
73    println!("✓ Three.js volume visualization launched");
74    println!("  Volume: {}×{}×{}", dims[0], dims[1], dims[2]);
75    println!("  Colormap: {}", colormap);
76
77    Ok(())
78}
79
80/// Generate HTML without launching viewer (for testing)
81pub fn generate_html(
82    volume_data: Vec<f32>,
83    dims: [usize; 3],
84    colormap: &str,
85    title: &str,
86) -> String {
87    templates::generate_volume_html(&volume_data, dims, colormap, title, 0.1, 0.8)
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_html_generation() {
96        let volume_data: Vec<f32> = (0..1000).map(|i| i as f32 / 1000.0).collect();
97        let dims = [10, 10, 10];
98
99        let html = generate_html(volume_data, dims, "VIRIDIS", "Test Volume");
100
101        assert!(html.contains("<!DOCTYPE html>"));
102        assert!(html.contains("three") || html.contains("THREE")); // Three.js import
103        assert!(html.contains("Test Volume"));
104        assert!(html.contains("10")); // Dimensions
105    }
106
107    #[test]
108    fn test_colormap_names() {
109        let volume_data: Vec<f32> = vec![0.0; 1000];
110        let dims = [10, 10, 10];
111
112        for colormap in &[
113            "VIRIDIS",
114            "RAINBOW",
115            "PLASMA",
116            "INFERNO",
117            "TURBO",
118            "GRAYSCALE",
119        ] {
120            let html = generate_html(volume_data.clone(), dims, colormap, "Test");
121            assert!(html.contains(colormap));
122        }
123    }
124}