1use crate::error::Result;
4use crate::style::ChartStyle;
5use crate::theme::{ChartTheme, Color};
6use crate::viewport::{Rect, Viewport};
7use std::sync::Arc;
8
9use super::vertex::{Vertex, VertexBuffer};
10
11#[cfg(feature = "text-rendering")]
12use crate::text::{TextAnchor, TextBaseline, TextItem};
13
14pub struct RenderContext {
16 pub device: Arc<wgpu::Device>,
18 pub queue: Arc<wgpu::Queue>,
20 vertex_buffers: Vec<VertexBuffer>,
22 current_vertices: Vec<Vertex>,
24 max_vertices_per_buffer: u32,
26 pending_draws: Vec<usize>,
28 next_buffer_index: usize,
30 viewport: Viewport,
32 theme: ChartTheme,
34 style: ChartStyle,
36 #[cfg(feature = "text-rendering")]
38 staged_text: std::collections::BTreeMap<i32, Vec<TextItem>>,
39 #[cfg(feature = "text-rendering")]
41 current_stage_priority: i32,
42}
43
44impl RenderContext {
45 pub fn new(
47 device: Arc<wgpu::Device>,
48 queue: Arc<wgpu::Queue>,
49 viewport: Viewport,
50 theme: ChartTheme,
51 style: ChartStyle,
52 ) -> Self {
53 Self {
54 device,
55 queue,
56 vertex_buffers: Vec::new(),
57 current_vertices: Vec::new(),
58 max_vertices_per_buffer: 100_000, pending_draws: Vec::new(),
60 next_buffer_index: 0,
61 viewport,
62 theme,
63 style,
64 #[cfg(feature = "text-rendering")]
65 staged_text: std::collections::BTreeMap::new(),
66 #[cfg(feature = "text-rendering")]
67 current_stage_priority: 0,
68 }
69 }
70
71 #[cfg(feature = "text-rendering")]
73 pub fn set_stage_priority(&mut self, priority: i32) {
74 self.current_stage_priority = priority;
75 }
76
77 pub fn viewport(&self) -> &Viewport {
79 &self.viewport
80 }
81
82 pub fn theme(&self) -> &ChartTheme {
84 &self.theme
85 }
86
87 pub fn style(&self) -> &ChartStyle {
89 &self.style
90 }
91
92 pub fn update(&mut self, viewport: Viewport, theme: ChartTheme, style: ChartStyle) {
94 self.viewport = viewport;
95 self.theme = theme;
96 self.style = style;
97 }
98
99 pub fn add_vertices(&mut self, vertices: &[Vertex]) {
101 self.current_vertices.extend_from_slice(vertices);
102 }
103
104 pub fn add_vertex(&mut self, vertex: Vertex) {
106 self.current_vertices.push(vertex);
107 }
108
109 pub fn draw_line(&mut self, start: [f32; 2], end: [f32; 2], color: Color, thickness: f32) {
111 let half_thickness = thickness * 0.5;
112
113 let dx = end[0] - start[0];
115 let dy = end[1] - start[1];
116 let len = (dx * dx + dy * dy).sqrt();
117
118 if len > 0.0 {
119 let nx = -dy / len * half_thickness;
120 let ny = dx / len * half_thickness;
121
122 let vertices = [
124 Vertex::new([start[0] + nx, start[1] + ny], color),
125 Vertex::new([start[0] - nx, start[1] - ny], color),
126 Vertex::new([end[0] + nx, end[1] + ny], color),
127 Vertex::new([start[0] - nx, start[1] - ny], color),
128 Vertex::new([end[0] - nx, end[1] - ny], color),
129 Vertex::new([end[0] + nx, end[1] + ny], color),
130 ];
131
132 self.add_vertices(&vertices);
133 }
134 }
135
136 pub fn draw_dashed_line(
138 &mut self,
139 start: [f32; 2],
140 end: [f32; 2],
141 color: Color,
142 thickness: f32,
143 dash: f32,
144 gap: f32,
145 ) {
146 let dx = end[0] - start[0];
148 let dy = end[1] - start[1];
149 let len = (dx * dx + dy * dy).sqrt();
150 if len == 0.0 {
151 return;
152 }
153 let dir_x = dx / len;
155 let dir_y = dy / len;
156 let mut distance = 0.0;
157 let mut current_start = start;
158 while distance < len {
159 let seg_len = (dash).min(len - distance);
160 let current_end = [
161 current_start[0] + dir_x * seg_len,
162 current_start[1] + dir_y * seg_len,
163 ];
164 self.draw_line(current_start, current_end, color, thickness);
165 distance += dash + gap;
167 current_start = [start[0] + dir_x * distance, start[1] + dir_y * distance];
168 }
169 }
170
171 pub fn draw_rect(&mut self, rect: Rect, color: Color) {
173 let x1 = rect.x;
174 let y1 = rect.y;
175 let x2 = rect.x + rect.width;
176 let y2 = rect.y + rect.height;
177
178 let vertices = [
179 Vertex::new([x1, y1], color),
180 Vertex::new([x2, y1], color),
181 Vertex::new([x1, y2], color),
182 Vertex::new([x2, y1], color),
183 Vertex::new([x2, y2], color),
184 Vertex::new([x1, y2], color),
185 ];
186
187 self.add_vertices(&vertices);
188 }
189
190 pub fn draw_triangle(&mut self, p1: [f32; 2], p2: [f32; 2], p3: [f32; 2], color: Color) {
192 let vertices = [
193 Vertex::new(p1, color),
194 Vertex::new(p2, color),
195 Vertex::new(p3, color),
196 ];
197
198 self.add_vertices(&vertices);
199 }
200
201 pub fn draw_circle(&mut self, center: [f32; 2], radius: f32, color: Color) {
203 const SEGMENTS: u32 = 16;
204 let angle_step = std::f32::consts::TAU / SEGMENTS as f32;
205
206 for i in 0..SEGMENTS {
207 let angle1 = i as f32 * angle_step;
208 let angle2 = (i + 1) as f32 * angle_step;
209
210 let p1 = [
211 center[0] + radius * angle1.cos(),
212 center[1] + radius * angle1.sin(),
213 ];
214 let p2 = [
215 center[0] + radius * angle2.cos(),
216 center[1] + radius * angle2.sin(),
217 ];
218
219 self.draw_triangle(center, p1, p2, color);
220 }
221 }
222
223 pub fn draw_rounded_rect(&mut self, rect: Rect, radius: f32, color: Color) {
225 let r = radius.min(rect.width / 2.0).min(rect.height / 2.0);
226
227 self.draw_rect(
229 Rect::new(rect.x, rect.y + r, rect.width, rect.height - 2.0 * r),
230 color,
231 );
232
233 self.draw_rect(
235 Rect::new(rect.x + r, rect.y, rect.width - 2.0 * r, r),
236 color,
237 );
238
239 self.draw_rect(
241 Rect::new(
242 rect.x + r,
243 rect.y + rect.height - r,
244 rect.width - 2.0 * r,
245 r,
246 ),
247 color,
248 );
249
250 const SEGMENTS: u32 = 8;
252 let angle_step = std::f32::consts::FRAC_PI_2 / SEGMENTS as f32;
253
254 let tl_center = [rect.x + r, rect.y + r];
256 for i in 0..SEGMENTS {
257 let a1 = std::f32::consts::PI + i as f32 * angle_step;
258 let a2 = std::f32::consts::PI + (i + 1) as f32 * angle_step;
259 self.draw_triangle(
260 tl_center,
261 [tl_center[0] + r * a1.cos(), tl_center[1] + r * a1.sin()],
262 [tl_center[0] + r * a2.cos(), tl_center[1] + r * a2.sin()],
263 color,
264 );
265 }
266
267 let tr_center = [rect.x + rect.width - r, rect.y + r];
269 for i in 0..SEGMENTS {
270 let a1 = -std::f32::consts::FRAC_PI_2 + i as f32 * angle_step;
271 let a2 = -std::f32::consts::FRAC_PI_2 + (i + 1) as f32 * angle_step;
272 self.draw_triangle(
273 tr_center,
274 [tr_center[0] + r * a1.cos(), tr_center[1] + r * a1.sin()],
275 [tr_center[0] + r * a2.cos(), tr_center[1] + r * a2.sin()],
276 color,
277 );
278 }
279
280 let br_center = [rect.x + rect.width - r, rect.y + rect.height - r];
282 for i in 0..SEGMENTS {
283 let a1 = i as f32 * angle_step;
284 let a2 = (i + 1) as f32 * angle_step;
285 self.draw_triangle(
286 br_center,
287 [br_center[0] + r * a1.cos(), br_center[1] + r * a1.sin()],
288 [br_center[0] + r * a2.cos(), br_center[1] + r * a2.sin()],
289 color,
290 );
291 }
292
293 let bl_center = [rect.x + r, rect.y + rect.height - r];
295 for i in 0..SEGMENTS {
296 let a1 = std::f32::consts::FRAC_PI_2 + i as f32 * angle_step;
297 let a2 = std::f32::consts::FRAC_PI_2 + (i + 1) as f32 * angle_step;
298 self.draw_triangle(
299 bl_center,
300 [bl_center[0] + r * a1.cos(), bl_center[1] + r * a1.sin()],
301 [bl_center[0] + r * a2.cos(), bl_center[1] + r * a2.sin()],
302 color,
303 );
304 }
305 }
306
307 pub fn draw_rect_with_border(
309 &mut self,
310 rect: Rect,
311 fill_color: Color,
312 border_color: Color,
313 border_width: f32,
314 ) {
315 self.draw_rect(rect, fill_color);
317
318 let half_border = border_width * 0.5;
320
321 self.draw_rect(
323 Rect::new(
324 rect.x - half_border,
325 rect.y - half_border,
326 rect.width + border_width,
327 border_width,
328 ),
329 border_color,
330 );
331
332 self.draw_rect(
334 Rect::new(
335 rect.x - half_border,
336 rect.y + rect.height - half_border,
337 rect.width + border_width,
338 border_width,
339 ),
340 border_color,
341 );
342
343 self.draw_rect(
345 Rect::new(
346 rect.x - half_border,
347 rect.y - half_border,
348 border_width,
349 rect.height + border_width,
350 ),
351 border_color,
352 );
353
354 self.draw_rect(
356 Rect::new(
357 rect.x + rect.width - half_border,
358 rect.y - half_border,
359 border_width,
360 rect.height + border_width,
361 ),
362 border_color,
363 );
364 }
365
366 pub fn flush(&mut self) -> Result<()> {
368 if self.current_vertices.is_empty() {
369 return Ok(());
370 }
371
372 let required_capacity = self.current_vertices.len() as u32;
375
376 let buffer_index = if self.next_buffer_index < self.vertex_buffers.len() {
378 if self.vertex_buffers[self.next_buffer_index].capacity >= required_capacity {
380 self.next_buffer_index
381 } else {
382 let capacity = required_capacity.max(self.max_vertices_per_buffer);
384 let buffer = VertexBuffer::new(&self.device, capacity);
385 self.vertex_buffers.push(buffer);
386 self.vertex_buffers.len() - 1
387 }
388 } else {
389 let capacity = required_capacity.max(self.max_vertices_per_buffer);
391 let buffer = VertexBuffer::new(&self.device, capacity);
392 self.vertex_buffers.push(buffer);
393 self.vertex_buffers.len() - 1
394 };
395
396 self.vertex_buffers[buffer_index].update(&self.queue, &self.current_vertices)?;
398 self.pending_draws.push(buffer_index);
399 self.next_buffer_index = buffer_index + 1;
400
401 self.current_vertices.clear();
402 Ok(())
403 }
404
405 pub fn commit(&mut self, render_pass: &mut wgpu::RenderPass) -> Result<()> {
407 if !self.current_vertices.is_empty() {
408 self.flush()?;
409 }
410
411 for &buffer_index in &self.pending_draws {
412 let buffer = &mut self.vertex_buffers[buffer_index];
413 if buffer.vertex_count > 0 {
414 render_pass.set_vertex_buffer(0, buffer.slice());
415 render_pass.draw(0..buffer.vertex_count, 0..1);
416 buffer.vertex_count = 0;
417 }
418 }
419
420 self.pending_draws.clear();
421 Ok(())
422 }
423
424 pub fn vertex_buffers(&self) -> &[VertexBuffer] {
426 &self.vertex_buffers
427 }
428
429 pub fn clear(&mut self) {
431 self.current_vertices.clear();
432 self.pending_draws.clear();
433 self.next_buffer_index = 0;
436 for buffer in &mut self.vertex_buffers {
437 buffer.vertex_count = 0;
438 }
439 #[cfg(feature = "text-rendering")]
440 {
441 self.staged_text.clear();
442 self.current_stage_priority = 0;
443 }
444 }
445
446 #[cfg(feature = "text-rendering")]
448 pub fn text_items(&self) -> Vec<TextItem> {
449 self.staged_text
450 .values()
451 .flat_map(|items| items.iter().cloned())
452 .collect()
453 }
454
455 #[cfg(feature = "text-rendering")]
457 pub fn text_items_for_stage(&self, stage_priority: i32) -> &[TextItem] {
458 self.staged_text
459 .get(&stage_priority)
460 .map(|v| v.as_slice())
461 .unwrap_or(&[])
462 }
463
464 #[cfg(feature = "text-rendering")]
466 pub fn text_stage_priorities(&self) -> Vec<i32> {
467 self.staged_text.keys().copied().collect()
468 }
469
470 #[cfg(feature = "text-rendering")]
472 pub fn take_text_for_stage(&mut self, stage_priority: i32) -> Vec<TextItem> {
473 self.staged_text.remove(&stage_priority).unwrap_or_default()
474 }
475
476 #[cfg(feature = "text-rendering")]
478 pub fn draw_text(&mut self, text: &str, x: f32, y: f32, color: Color, font_size: Option<f32>) {
479 self.staged_text
480 .entry(self.current_stage_priority)
481 .or_default()
482 .push(TextItem {
483 text: text.to_string(),
484 x,
485 y,
486 font_size: font_size.unwrap_or(12.0),
487 color,
488 anchor: TextAnchor::Start,
489 baseline: TextBaseline::Top,
490 });
491 }
492
493 #[cfg(feature = "text-rendering")]
495 pub fn draw_text_anchored(
496 &mut self,
497 text: &str,
498 x: f32,
499 y: f32,
500 color: Color,
501 font_size: Option<f32>,
502 anchor: TextAnchor,
503 baseline: TextBaseline,
504 ) {
505 self.staged_text
506 .entry(self.current_stage_priority)
507 .or_default()
508 .push(TextItem {
509 text: text.to_string(),
510 x,
511 y,
512 font_size: font_size.unwrap_or(12.0),
513 color,
514 anchor,
515 baseline,
516 });
517 }
518}