1use crate::core::{
4 vertex_utils, BoundingBox, DrawCall, GpuVertexBuffer, Material, PipelineType, RenderData,
5 Vertex,
6};
7use glam::{Vec3, Vec4};
8
9#[derive(Debug, Clone)]
10pub struct ContourPlot {
11 pub base_z: f32,
12 pub label: Option<String>,
13 pub visible: bool,
14 pub line_width: f32,
15 vertices: Option<Vec<Vertex>>,
16 gpu_vertices: Option<GpuVertexBuffer>,
17 vertex_count: usize,
18 bounds: Option<BoundingBox>,
19}
20
21impl ContourPlot {
22 pub fn from_vertices(vertices: Vec<Vertex>, base_z: f32, bounds: BoundingBox) -> Self {
24 Self {
25 base_z,
26 label: None,
27 visible: true,
28 line_width: 1.0,
29 vertex_count: vertices.len(),
30 vertices: Some(vertices),
31 gpu_vertices: None,
32 bounds: Some(bounds),
33 }
34 }
35
36 pub fn from_gpu_buffer(
38 buffer: GpuVertexBuffer,
39 vertex_count: usize,
40 base_z: f32,
41 bounds: BoundingBox,
42 ) -> Self {
43 Self {
44 base_z,
45 label: None,
46 visible: true,
47 line_width: 1.0,
48 vertex_count,
49 vertices: None,
50 gpu_vertices: Some(buffer),
51 bounds: Some(bounds),
52 }
53 }
54
55 pub fn with_label<S: Into<String>>(mut self, label: S) -> Self {
56 self.label = Some(label.into());
57 self
58 }
59
60 pub fn set_visible(&mut self, visible: bool) {
61 self.visible = visible;
62 }
63
64 pub fn with_line_width(mut self, line_width: f32) -> Self {
65 self.line_width = line_width.max(0.5);
66 self
67 }
68
69 pub fn vertices(&mut self) -> &Vec<Vertex> {
70 if self.vertices.is_none() {
71 self.vertices = Some(Vec::new());
72 }
73 self.vertices.as_ref().unwrap()
74 }
75
76 pub fn bounds(&self) -> BoundingBox {
77 self.bounds.unwrap_or_default()
78 }
79
80 pub fn cpu_vertices(&self) -> Option<&[Vertex]> {
81 self.vertices.as_deref()
82 }
83
84 pub fn render_data_with_viewport(&mut self, viewport_px: Option<(u32, u32)>) -> RenderData {
85 if self.gpu_vertices.is_some() {
86 return self.render_data();
87 }
88
89 let bounds = self.bounds();
90 let (vertices, vertex_count, pipeline_type) = if self.line_width > 1.0 {
91 let viewport_px = viewport_px.unwrap_or((600, 400));
92 let data_per_px = crate::core::data_units_per_px(&bounds, viewport_px);
93 let width_data = self.line_width.max(0.1) * data_per_px;
94 let verts = self.vertices().clone();
95 let mut thick = Vec::new();
96 for segment in verts.chunks_exact(2) {
97 let x = [segment[0].position[0] as f64, segment[1].position[0] as f64];
98 let y = [segment[0].position[1] as f64, segment[1].position[1] as f64];
99 let color = Vec4::from_array(segment[0].color);
100 thick.extend(vertex_utils::create_thick_polyline(
101 &x, &y, color, width_data,
102 ));
103 }
104 let count = thick.len();
105 (thick, count, PipelineType::Triangles)
106 } else {
107 let verts = self.vertices().clone();
108 let count = verts.len();
109 (verts, count, PipelineType::Lines)
110 };
111
112 let material = Material {
113 albedo: Vec4::ONE,
114 roughness: self.line_width.max(0.0),
115 ..Default::default()
116 };
117
118 let draw_call = DrawCall {
119 vertex_offset: 0,
120 vertex_count,
121 index_offset: None,
122 index_count: None,
123 instance_count: 1,
124 };
125
126 RenderData {
127 pipeline_type,
128 vertices,
129 indices: None,
130 gpu_vertices: None,
131 bounds: Some(bounds),
132 material,
133 draw_calls: vec![draw_call],
134 image: None,
135 }
136 }
137
138 pub fn render_data(&mut self) -> RenderData {
139 let using_gpu = self.gpu_vertices.is_some();
140 let bounds = self.bounds();
141 let (vertices, vertex_count, gpu_vertices, pipeline_type) = if using_gpu {
142 (
143 Vec::new(),
144 self.vertex_count,
145 self.gpu_vertices.clone(),
146 PipelineType::Lines,
147 )
148 } else {
149 let verts = self.vertices().clone();
150 if self.line_width > 1.0 {
151 let mut thick = Vec::new();
152 for segment in verts.chunks_exact(2) {
153 let x = [segment[0].position[0] as f64, segment[1].position[0] as f64];
154 let y = [segment[0].position[1] as f64, segment[1].position[1] as f64];
155 let color = Vec4::from_array(segment[0].color);
156 thick.extend(vertex_utils::create_thick_polyline(
157 &x,
158 &y,
159 color,
160 self.line_width,
161 ));
162 }
163 let count = thick.len();
164 (thick, count, None, PipelineType::Triangles)
165 } else {
166 let count = verts.len();
167 (verts, count, None, PipelineType::Lines)
168 }
169 };
170
171 let material = Material {
172 albedo: Vec4::ONE,
173 roughness: self.line_width.max(0.0),
174 ..Default::default()
175 };
176
177 let draw_call = DrawCall {
178 vertex_offset: 0,
179 vertex_count,
180 index_offset: None,
181 index_count: None,
182 instance_count: 1,
183 };
184
185 RenderData {
186 pipeline_type,
187 vertices,
188 indices: None,
189 gpu_vertices,
190 bounds: Some(bounds),
191 material,
192 draw_calls: vec![draw_call],
193 image: None,
194 }
195 }
196
197 pub fn estimated_memory_usage(&self) -> usize {
198 self.vertices
199 .as_ref()
200 .map(|v| v.len() * std::mem::size_of::<Vertex>())
201 .unwrap_or(0)
202 }
203}
204
205pub fn contour_bounds(x_min: f32, x_max: f32, y_min: f32, y_max: f32, base_z: f32) -> BoundingBox {
206 BoundingBox::new(
207 Vec3::new(x_min, y_min, base_z),
208 Vec3::new(x_max, y_max, base_z),
209 )
210}