runmat_plot/plots/
contour_fill.rs1use 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}