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