1use crate::core::{
4 BoundingBox, DrawCall, GpuVertexBuffer, Material, PipelineType, RenderData, Vertex,
5};
6use glam::{Vec3, Vec4};
7
8#[derive(Debug, Clone)]
9pub struct AreaPlot {
10 pub x: Vec<f64>,
11 pub y: Vec<f64>,
12 pub baseline: f64,
13 pub lower_y: Option<Vec<f64>>,
14 pub color: Vec4,
15 pub label: Option<String>,
16 pub visible: bool,
17 vertices: Option<Vec<Vertex>>,
18 indices: Option<Vec<u32>>,
19 bounds: Option<BoundingBox>,
20 dirty: bool,
21 gpu_vertices: Option<GpuVertexBuffer>,
22 gpu_vertex_count: Option<usize>,
23 gpu_bounds: Option<BoundingBox>,
24}
25
26impl AreaPlot {
27 pub fn new(x: Vec<f64>, y: Vec<f64>) -> Result<Self, String> {
28 if x.len() != y.len() || x.is_empty() {
29 return Err("area: X and Y must be same non-zero length".to_string());
30 }
31 Ok(Self {
32 x,
33 y,
34 baseline: 0.0,
35 lower_y: None,
36 color: Vec4::new(0.0, 0.5, 1.0, 0.4),
37 label: None,
38 visible: true,
39 vertices: None,
40 indices: None,
41 bounds: None,
42 dirty: true,
43 gpu_vertices: None,
44 gpu_vertex_count: None,
45 gpu_bounds: None,
46 })
47 }
48 pub fn from_gpu_buffer(
49 color: Vec4,
50 baseline: f64,
51 lower_y: Option<Vec<f64>>,
52 buffer: GpuVertexBuffer,
53 vertex_count: usize,
54 bounds: BoundingBox,
55 ) -> Self {
56 Self {
57 x: Vec::new(),
58 y: Vec::new(),
59 baseline,
60 lower_y,
61 color,
62 label: None,
63 visible: true,
64 vertices: None,
65 indices: None,
66 bounds: Some(bounds),
67 dirty: false,
68 gpu_vertices: Some(buffer),
69 gpu_vertex_count: Some(vertex_count),
70 gpu_bounds: Some(bounds),
71 }
72 }
73 pub fn with_style(mut self, color: Vec4, baseline: f64) -> Self {
74 self.color = color;
75 self.baseline = baseline;
76 self.dirty = true;
77 self
78 }
79 pub fn with_lower_curve(mut self, lower_y: Vec<f64>) -> Self {
80 self.lower_y = Some(lower_y);
81 self.dirty = true;
82 self
83 }
84 pub fn with_label<S: Into<String>>(mut self, label: S) -> Self {
85 self.label = Some(label.into());
86 self
87 }
88 pub fn set_visible(&mut self, v: bool) {
89 self.visible = v;
90 }
91 pub fn generate_vertices(&mut self) -> (&Vec<Vertex>, &Vec<u32>) {
92 if self.dirty || self.vertices.is_none() {
93 let mut verts = Vec::new();
94 let mut inds = Vec::new();
95 for i in 0..self.x.len() {
97 let xi = self.x[i] as f32;
98 let yi = self.y[i] as f32;
99 let b = self
100 .lower_y
101 .as_ref()
102 .and_then(|vals| vals.get(i).copied())
103 .unwrap_or(self.baseline) as f32;
104 if !xi.is_finite() || !yi.is_finite() {
105 continue;
106 }
107 verts.push(Vertex::new(Vec3::new(xi, b, 0.0), self.color));
108 verts.push(Vertex::new(Vec3::new(xi, yi, 0.0), self.color));
109 }
110 for i in 0..(verts.len() / 2 - 1) {
112 let base = (i * 2) as u32;
113 inds.extend_from_slice(&[base, base + 1, base + 3, base, base + 3, base + 2]);
114 }
115 self.vertices = Some(verts);
116 self.indices = Some(inds);
117 self.dirty = false;
118 }
119 (
120 self.vertices.as_ref().unwrap(),
121 self.indices.as_ref().unwrap(),
122 )
123 }
124 pub fn bounds(&mut self) -> BoundingBox {
125 if let Some(bounds) = self.gpu_bounds {
126 return bounds;
127 }
128 if self.dirty || self.bounds.is_none() {
129 let mut min = Vec3::new(f32::INFINITY, f32::INFINITY, 0.0);
130 let mut max = Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, 0.0);
131 for (idx, (&x, &y)) in self.x.iter().zip(self.y.iter()).enumerate() {
132 let (x, y) = (x as f32, y as f32);
133 let lower = self
134 .lower_y
135 .as_ref()
136 .and_then(|vals| vals.get(idx).copied())
137 .unwrap_or(self.baseline) as f32;
138 if !x.is_finite() || !y.is_finite() {
139 continue;
140 }
141 min.x = min.x.min(x);
142 max.x = max.x.max(x);
143 min.y = min.y.min(y.min(lower));
144 max.y = max.y.max(y.max(lower));
145 }
146 if !min.x.is_finite() {
147 min = Vec3::ZERO;
148 max = Vec3::ZERO;
149 }
150 self.bounds = Some(BoundingBox::new(min, max));
151 }
152 self.bounds.unwrap()
153 }
154 pub fn render_data(&mut self) -> RenderData {
155 let using_gpu = self.gpu_vertices.is_some();
156 let bounds = self.bounds();
157 let (vertices, indices) = if using_gpu {
158 (Vec::new(), Vec::new())
159 } else {
160 let (v, i) = self.generate_vertices();
161 (v.clone(), i.clone())
162 };
163 let material = Material {
164 albedo: self.color,
165 ..Default::default()
166 };
167 let draw_call = DrawCall {
168 vertex_offset: 0,
169 vertex_count: self.gpu_vertex_count.unwrap_or(vertices.len()),
170 index_offset: if using_gpu { None } else { Some(0) },
171 index_count: if using_gpu { None } else { Some(indices.len()) },
172 instance_count: 1,
173 };
174 RenderData {
175 pipeline_type: PipelineType::Triangles,
176 vertices,
177 indices: if using_gpu { None } else { Some(indices) },
178 material,
179 draw_calls: vec![draw_call],
180 gpu_vertices: self.gpu_vertices.clone(),
181 bounds: Some(bounds),
182 image: None,
183 }
184 }
185 pub fn estimated_memory_usage(&self) -> usize {
186 self.vertices
187 .as_ref()
188 .map_or(0, |v| v.len() * std::mem::size_of::<Vertex>())
189 + self
190 .indices
191 .as_ref()
192 .map_or(0, |i| i.len() * std::mem::size_of::<u32>())
193 }
194}