Skip to main content

runmat_plot/plots/
contour_fill.rs

1//! Filled contour plot implementation (triangles on the base plane).
2
3use crate::context::shared_wgpu_context;
4use crate::core::{
5    BoundingBox, DrawCall, GpuVertexBuffer, Material, PipelineType, RenderData, Vertex,
6};
7use crate::gpu::util::copy_readback_bytes;
8use bytemuck::cast_slice;
9use glam::Vec4;
10
11#[derive(Debug, Clone)]
12pub struct ContourFillPlot {
13    pub label: Option<String>,
14    pub visible: bool,
15    vertices: Option<Vec<Vertex>>,
16    gpu_vertices: Option<GpuVertexBuffer>,
17    vertex_count: usize,
18    bounds: BoundingBox,
19}
20
21impl ContourFillPlot {
22    pub async fn export_scene_vertices(&self) -> Result<Vec<Vertex>, String> {
23        if let Some(vertices) = &self.vertices {
24            return Ok(vertices.clone());
25        }
26
27        if let Some(gpu_vertices) = &self.gpu_vertices {
28            let context = shared_wgpu_context().ok_or_else(|| {
29                "filled contour plot has GPU vertices but no shared WGPU context is installed"
30                    .to_string()
31            })?;
32            let generated_count = if let Some(indirect) = &gpu_vertices.indirect {
33                let bytes = copy_readback_bytes(
34                    &context.device,
35                    &context.queue,
36                    &indirect.args,
37                    std::mem::size_of::<u32>(),
38                )
39                .await?;
40                u32::from_le_bytes(
41                    bytes
42                        .get(0..4)
43                        .ok_or_else(|| {
44                            "filled contour indirect readback returned too few bytes".to_string()
45                        })?
46                        .try_into()
47                        .map_err(|_| "filled contour indirect readback failed".to_string())?,
48                ) as usize
49            } else {
50                self.vertex_count
51            };
52            let vertex_count = generated_count.min(gpu_vertices.vertex_count);
53            let byte_len = vertex_count * std::mem::size_of::<Vertex>();
54            let bytes = copy_readback_bytes(
55                &context.device,
56                &context.queue,
57                &gpu_vertices.buffer,
58                byte_len,
59            )
60            .await?;
61            let vertices: &[Vertex] = cast_slice(&bytes);
62            return Ok(vertices.to_vec());
63        }
64
65        Ok(Vec::new())
66    }
67
68    pub fn from_vertices(vertices: Vec<Vertex>, bounds: BoundingBox) -> Self {
69        let vertex_count = vertices.len();
70        Self {
71            label: None,
72            visible: true,
73            vertices: Some(vertices),
74            gpu_vertices: None,
75            vertex_count,
76            bounds,
77        }
78    }
79
80    pub fn from_gpu_buffer(
81        buffer: GpuVertexBuffer,
82        vertex_count: usize,
83        bounds: BoundingBox,
84    ) -> Self {
85        Self {
86            label: None,
87            visible: true,
88            vertices: None,
89            gpu_vertices: Some(buffer),
90            vertex_count,
91            bounds,
92        }
93    }
94
95    pub fn with_label<S: Into<String>>(mut self, label: S) -> Self {
96        self.label = Some(label.into());
97        self
98    }
99
100    pub fn set_visible(&mut self, visible: bool) {
101        self.visible = visible;
102    }
103
104    pub fn bounds(&self) -> BoundingBox {
105        self.bounds
106    }
107
108    pub fn cpu_vertices(&self) -> Option<&[Vertex]> {
109        self.vertices.as_deref()
110    }
111
112    pub fn render_data(&mut self) -> RenderData {
113        let bounds = self.bounds();
114        let material = Material {
115            albedo: Vec4::ONE,
116            ..Default::default()
117        };
118
119        let (vertices, gpu_vertices) = if let Some(buffer) = &self.gpu_vertices {
120            (Vec::new(), Some(buffer.clone()))
121        } else {
122            (self.vertices.clone().unwrap_or_default(), None)
123        };
124
125        let draw_call = DrawCall {
126            vertex_offset: 0,
127            vertex_count: self.vertex_count,
128            index_offset: None,
129            index_count: None,
130            instance_count: 1,
131        };
132
133        RenderData {
134            pipeline_type: PipelineType::Triangles,
135            vertices,
136            indices: None,
137            gpu_vertices,
138            bounds: Some(bounds),
139            material,
140            draw_calls: vec![draw_call],
141            image: None,
142        }
143    }
144
145    pub fn estimated_memory_usage(&self) -> usize {
146        self.vertices
147            .as_ref()
148            .map(|v| v.len() * std::mem::size_of::<Vertex>())
149            .unwrap_or(0)
150    }
151}