1use std::collections::HashMap;
7use std::time::{Duration, Instant};
8
9#[derive(Debug, Clone)]
15pub struct VirtualScroller {
16 pub viewport_height: f64,
17 pub item_height: f64,
18 pub total_items: usize,
19 pub visible_start: usize,
20 pub visible_end: usize,
21 pub scroll_offset: f64,
22 pub overscan: usize, }
24
25impl VirtualScroller {
26 pub fn new(viewport_height: f64, item_height: f64, total_items: usize) -> Self {
28 let visible_count = (viewport_height / item_height).ceil() as usize;
29 Self {
30 viewport_height,
31 item_height,
32 total_items,
33 visible_start: 0,
34 visible_end: visible_count.min(total_items),
35 scroll_offset: 0.0,
36 overscan: 5, }
38 }
39
40 pub fn scroll_to(&mut self, offset: f64) {
42 self.scroll_offset = offset.max(0.0);
43 self.update_visible_range();
44 }
45
46 fn update_visible_range(&mut self) {
48 let visible_count = (self.viewport_height / self.item_height).ceil() as usize;
49 self.visible_start = (self.scroll_offset / self.item_height).floor() as usize;
50 self.visible_end = (self.visible_start + visible_count).min(self.total_items);
51
52 self.visible_start = self.visible_start.saturating_sub(self.overscan);
54 self.visible_end = (self.visible_end + self.overscan).min(self.total_items);
55 }
56
57 pub fn get_visible_items(&self) -> Vec<VisibleItem> {
59 (self.visible_start..self.visible_end)
60 .map(|i| VisibleItem {
61 index: i,
62 y_position: i as f64 * self.item_height - self.scroll_offset,
63 height: self.item_height,
64 })
65 .collect()
66 }
67
68 pub fn get_total_height(&self) -> f64 {
70 self.total_items as f64 * self.item_height
71 }
72
73 pub fn is_item_visible(&self, index: usize) -> bool {
75 index >= self.visible_start && index < self.visible_end
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct VisibleItem {
82 pub index: usize,
83 pub y_position: f64,
84 pub height: f64,
85}
86
87#[derive(Debug, Clone, PartialEq)]
93pub enum SamplingStrategy {
94 Uniform,
96 Adaptive,
98 Statistical,
100 LevelOfDetail(f64),
102}
103
104#[derive(Debug, Clone)]
106pub struct DataSampler {
107 strategy: SamplingStrategy,
108 cache: HashMap<String, Vec<DataPoint>>,
109}
110
111impl DataSampler {
112 pub fn new(strategy: SamplingStrategy) -> Self {
114 Self {
115 strategy,
116 cache: HashMap::new(),
117 }
118 }
119
120 pub fn sample(&self, data: &[DataPoint], target_size: usize) -> Vec<DataPoint> {
122 if data.len() <= target_size {
123 return data.to_vec();
124 }
125
126 match self.strategy {
127 SamplingStrategy::Uniform => self.uniform_sample(data, target_size),
128 SamplingStrategy::Adaptive => self.adaptive_sample(data, target_size),
129 SamplingStrategy::Statistical => self.statistical_sample(data, target_size),
130 SamplingStrategy::LevelOfDetail(zoom) => self.lod_sample(data, target_size, zoom),
131 }
132 }
133
134 fn uniform_sample(&self, data: &[DataPoint], target_size: usize) -> Vec<DataPoint> {
136 let step = data.len() as f64 / target_size as f64;
137 (0..target_size)
138 .map(|i| {
139 let index = (i as f64 * step).floor() as usize;
140 data[index.min(data.len() - 1)].clone()
141 })
142 .collect()
143 }
144
145 fn adaptive_sample(&self, data: &[DataPoint], target_size: usize) -> Vec<DataPoint> {
147 let mut sampled = Vec::new();
149 let chunk_size = data.len() / target_size;
150
151 for i in 0..target_size {
152 let start = i * chunk_size;
153 let end = ((i + 1) * chunk_size).min(data.len());
154 let chunk = &data[start..end];
155
156 if let Some(max_point) = chunk
158 .iter()
159 .max_by(|a, b| a.value.partial_cmp(&b.value).unwrap())
160 {
161 sampled.push(max_point.clone());
162 }
163 }
164
165 sampled
166 }
167
168 fn statistical_sample(&self, data: &[DataPoint], target_size: usize) -> Vec<DataPoint> {
170 let mut sampled = Vec::new();
172
173 if let (Some(min_point), Some(max_point)) = (
175 data.iter()
176 .min_by(|a, b| a.value.partial_cmp(&b.value).unwrap()),
177 data.iter()
178 .max_by(|a, b| a.value.partial_cmp(&b.value).unwrap()),
179 ) {
180 sampled.push(min_point.clone());
181 sampled.push(max_point.clone());
182 }
183
184 let remaining = target_size.saturating_sub(sampled.len());
186 if remaining > 0 {
187 let uniform_sample = self.uniform_sample(data, remaining);
188 sampled.extend(uniform_sample);
189 }
190
191 sampled
192 }
193
194 fn lod_sample(&self, data: &[DataPoint], target_size: usize, zoom: f64) -> Vec<DataPoint> {
196 let adjusted_size = (target_size as f64 * zoom).ceil() as usize;
198 self.uniform_sample(data, adjusted_size.min(target_size))
199 }
200}
201
202#[derive(Debug, Clone)]
208pub struct WebGLRenderer {
209 pub width: u32,
210 pub height: u32,
211 pub shader_cache: HashMap<String, u32>,
212 pub buffer_cache: HashMap<String, u32>,
213 pub is_initialized: bool,
214}
215
216impl WebGLRenderer {
217 pub fn new(width: u32, height: u32) -> Self {
219 Self {
220 width,
221 height,
222 shader_cache: HashMap::new(),
223 buffer_cache: HashMap::new(),
224 is_initialized: false,
225 }
226 }
227
228 pub fn initialize(&mut self) -> Result<(), String> {
230 self.is_initialized = true;
232 Ok(())
233 }
234
235 pub fn is_initialized(&self) -> bool {
237 self.is_initialized
238 }
239
240 pub fn compile_shader_program(&mut self, vertex: &str, fragment: &str) -> Option<u32> {
242 let key = format!("{}:{}", vertex, fragment);
243 if let Some(&program) = self.shader_cache.get(&key) {
244 return Some(program);
245 }
246
247 let program = self.shader_cache.len() as u32 + 1;
249 self.shader_cache.insert(key, program);
250 Some(program)
251 }
252
253 pub fn render_batch(&mut self, batch: &RenderBatch) -> Result<(), String> {
255 if !self.is_initialized {
256 return Err("WebGL renderer not initialized".to_string());
257 }
258
259 let _ = batch.points.len();
261 Ok(())
262 }
263
264 pub fn clear(&mut self, _color: Color) -> Result<(), String> {
266 if !self.is_initialized {
267 return Err("WebGL renderer not initialized".to_string());
268 }
269
270 Ok(())
272 }
273}
274
275#[derive(Debug, Clone)]
277pub struct WebGPURenderer {
278 pub width: u32,
279 pub height: u32,
280 pub buffer_pool: Vec<Buffer>,
281 pub shader_cache: HashMap<String, u32>,
282 pub is_initialized: bool,
283}
284
285impl WebGPURenderer {
286 pub fn new(width: u32, height: u32) -> Self {
288 Self {
289 width,
290 height,
291 buffer_pool: Vec::new(),
292 shader_cache: HashMap::new(),
293 is_initialized: false,
294 }
295 }
296
297 pub fn initialize(&mut self) -> Result<(), String> {
299 self.is_initialized = true;
301 Ok(())
302 }
303
304 pub fn is_initialized(&self) -> bool {
306 self.is_initialized
307 }
308
309 pub fn allocate_buffer(&mut self, size: usize) -> Option<Buffer> {
311 if let Some(index) = self.buffer_pool.iter().position(|b| b.size >= size) {
313 return Some(self.buffer_pool.remove(index));
314 }
315
316 Some(Buffer {
318 size,
319 id: self.buffer_pool.len() as u32 + 1,
320 })
321 }
322
323 pub fn deallocate_buffer(&mut self, buffer: Buffer) {
325 self.buffer_pool.push(buffer);
326 }
327
328 pub fn render_batch(&mut self, batch: &RenderBatch) -> Result<(), String> {
330 if !self.is_initialized {
331 return Err("WebGPU renderer not initialized".to_string());
332 }
333
334 let _ = batch.points.len();
336 Ok(())
337 }
338}
339
340#[derive(Debug, Clone)]
342pub struct Buffer {
343 pub size: usize,
344 pub id: u32,
345}
346
347#[derive(Debug, Clone)]
349pub struct RenderBatch {
350 pub points: Vec<(Point2D, Color)>,
351 pub lines: Vec<(Point2D, Point2D, Color)>,
352 pub triangles: Vec<(Point2D, Point2D, Point2D, Color)>,
353}
354
355impl RenderBatch {
356 pub fn new() -> Self {
358 Self {
359 points: Vec::new(),
360 lines: Vec::new(),
361 triangles: Vec::new(),
362 }
363 }
364
365 pub fn add_point(&mut self, point: Point2D, color: Color) {
367 self.points.push((point, color));
368 }
369
370 pub fn add_line(&mut self, start: Point2D, end: Point2D, color: Color) {
372 self.lines.push((start, end, color));
373 }
374
375 pub fn add_triangle(&mut self, a: Point2D, b: Point2D, c: Point2D, color: Color) {
377 self.triangles.push((a, b, c, color));
378 }
379
380 pub fn clear(&mut self) {
382 self.points.clear();
383 self.lines.clear();
384 self.triangles.clear();
385 }
386
387 pub fn primitive_count(&self) -> usize {
389 self.points.len() + self.lines.len() + self.triangles.len()
390 }
391}
392
393#[derive(Debug, Clone, Copy)]
395pub struct Point2D {
396 pub x: f64,
397 pub y: f64,
398}
399
400#[derive(Debug, Clone, Copy)]
402pub struct Color {
403 pub r: f32,
404 pub g: f32,
405 pub b: f32,
406 pub a: f32,
407}
408
409impl Color {
410 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
412 Self { r, g, b, a }
413 }
414
415 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
417 Self {
418 r: r as f32 / 255.0,
419 g: g as f32 / 255.0,
420 b: b as f32 / 255.0,
421 a: 1.0,
422 }
423 }
424
425 pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
427 Self {
428 r: r as f32 / 255.0,
429 g: g as f32 / 255.0,
430 b: b as f32 / 255.0,
431 a: a as f32 / 255.0,
432 }
433 }
434}
435
436#[derive(Debug, Clone)]
442pub struct MemoryPool {
443 pub total_size: usize,
444 pub used_size: usize,
445 pub available_size: usize,
446 pub allocations: Vec<Allocation>,
447}
448
449impl MemoryPool {
450 pub fn new(size: usize) -> Self {
452 Self {
453 total_size: size,
454 used_size: 0,
455 available_size: size,
456 allocations: Vec::new(),
457 }
458 }
459
460 pub fn allocate(&mut self, size: usize) -> Option<*mut u8> {
462 if self.available_size < size {
463 return None;
464 }
465
466 if let Some(index) = self.find_free_block(size) {
468 let allocation = &mut self.allocations[index];
469 allocation.size = size;
470 allocation.used = true;
471 self.used_size += size;
472 self.available_size -= size;
473 return Some(allocation.ptr);
474 }
475
476 let ptr = std::ptr::null_mut(); self.allocations.push(Allocation {
479 ptr,
480 size,
481 used: true,
482 });
483 self.used_size += size;
484 self.available_size -= size;
485 Some(ptr)
486 }
487
488 pub fn deallocate(&mut self, ptr: *mut u8) {
490 if let Some(allocation) = self.allocations.iter_mut().find(|a| a.ptr == ptr) {
491 self.used_size -= allocation.size;
492 self.available_size += allocation.size;
493 allocation.used = false;
494 }
495 }
496
497 fn find_free_block(&self, size: usize) -> Option<usize> {
499 self.allocations
500 .iter()
501 .position(|a| !a.used && a.size >= size)
502 }
503
504 pub fn defragment(&mut self) {
506 self.allocations.sort_by(|a, b| a.ptr.cmp(&b.ptr));
508
509 let mut i = 0;
510 while i < self.allocations.len() - 1 {
511 if !self.allocations[i].used && !self.allocations[i + 1].used {
512 self.allocations[i].size += self.allocations[i + 1].size;
514 self.allocations.remove(i + 1);
515 } else {
516 i += 1;
517 }
518 }
519 }
520}
521
522#[derive(Debug, Clone)]
524pub struct Allocation {
525 pub ptr: *mut u8,
526 pub size: usize,
527 pub used: bool,
528}
529
530#[derive(Debug, Clone)]
532pub struct GarbageCollector {
533 objects: Vec<Object>,
534 next_id: usize,
535}
536
537impl GarbageCollector {
538 pub fn new() -> Self {
540 Self {
541 objects: Vec::new(),
542 next_id: 0,
543 }
544 }
545
546 pub fn allocate_object(&mut self, name: &str) -> ObjectId {
548 let id = ObjectId(self.next_id);
549 self.next_id += 1;
550
551 self.objects.push(Object {
552 id,
553 name: name.to_string(),
554 reachable: false,
555 size: 0, });
557
558 id
559 }
560
561 pub fn mark_reachable(&mut self, id: ObjectId) {
563 if let Some(obj) = self.objects.iter_mut().find(|o| o.id == id) {
564 obj.reachable = true;
565 }
566 }
567
568 pub fn collect(&mut self) {
570 self.objects.retain(|obj| obj.reachable);
572
573 for obj in &mut self.objects {
575 obj.reachable = false;
576 }
577 }
578
579 pub fn object_count(&self) -> usize {
581 self.objects.len()
582 }
583
584 pub fn is_allocated(&self, id: ObjectId) -> bool {
586 self.objects.iter().any(|obj| obj.id == id)
587 }
588
589 pub fn get_memory_stats(&self) -> MemoryStats {
591 let total_size: usize = self.objects.iter().map(|obj| obj.size).sum();
592 MemoryStats {
593 object_count: self.objects.len(),
594 total_size,
595 reachable_count: self.objects.iter().filter(|obj| obj.reachable).count(),
596 }
597 }
598}
599
600#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
602pub struct ObjectId(usize);
603
604#[derive(Debug, Clone)]
606pub struct Object {
607 pub id: ObjectId,
608 pub name: String,
609 pub reachable: bool,
610 pub size: usize,
611}
612
613#[derive(Debug, Clone)]
615pub struct MemoryStats {
616 pub object_count: usize,
617 pub total_size: usize,
618 pub reachable_count: usize,
619}
620
621#[derive(Debug, Clone)]
627pub struct PerformanceMonitor {
628 pub metrics: HashMap<String, PerformanceMetric>,
629 pub budgets: HashMap<String, Duration>,
630 pub enabled: bool,
631}
632
633impl PerformanceMonitor {
634 pub fn new() -> Self {
636 Self {
637 metrics: HashMap::new(),
638 budgets: HashMap::new(),
639 enabled: true,
640 }
641 }
642
643 pub fn is_enabled(&self) -> bool {
645 self.enabled
646 }
647
648 pub fn set_enabled(&mut self, enabled: bool) {
650 self.enabled = enabled;
651 }
652
653 pub fn start_timer(&mut self, name: &str) {
655 if !self.enabled {
656 return;
657 }
658
659 let metric = PerformanceMetric {
660 name: name.to_string(),
661 start_time: Instant::now(),
662 duration: Duration::ZERO,
663 call_count: 1,
664 min_duration: Duration::MAX,
665 max_duration: Duration::ZERO,
666 total_duration: Duration::ZERO,
667 };
668 self.metrics.insert(name.to_string(), metric);
669 }
670
671 pub fn end_timer(&mut self, name: &str) {
673 if !self.enabled {
674 return;
675 }
676
677 if let Some(metric) = self.metrics.get_mut(name) {
678 let duration = metric.start_time.elapsed();
679 metric.duration = duration;
680 metric.total_duration += duration;
681 metric.min_duration = metric.min_duration.min(duration);
682 metric.max_duration = metric.max_duration.max(duration);
683 }
684 }
685
686 pub fn get_metrics(&self) -> &HashMap<String, PerformanceMetric> {
688 &self.metrics
689 }
690
691 pub fn set_budget(&mut self, name: &str, budget: Duration) {
693 self.budgets.insert(name.to_string(), budget);
694 }
695
696 pub fn is_over_budget(&self, name: &str) -> bool {
698 if let (Some(metric), Some(budget)) = (self.metrics.get(name), self.budgets.get(name)) {
699 metric.duration > *budget
700 } else {
701 false
702 }
703 }
704
705 pub fn get_optimization_suggestions(&self) -> Vec<String> {
707 let mut suggestions = Vec::new();
708
709 for (name, metric) in &self.metrics {
710 if metric.duration > Duration::from_millis(100) {
711 suggestions.push(format!(
712 "Consider optimizing {} (took {:?}, avg: {:?})",
713 name,
714 metric.duration,
715 metric.total_duration / metric.call_count as u32
716 ));
717 }
718
719 if metric.call_count > 1000 {
720 suggestions.push(format!(
721 "Consider batching {} (called {} times)",
722 name, metric.call_count
723 ));
724 }
725 }
726
727 suggestions
728 }
729
730 pub fn get_performance_report(&self) -> PerformanceReport {
732 let mut slow_operations = Vec::new();
733 let mut over_budget = Vec::new();
734
735 for (name, metric) in &self.metrics {
736 if metric.duration > Duration::from_millis(50) {
737 slow_operations.push((name.clone(), metric.duration));
738 }
739
740 if self.is_over_budget(name) {
741 over_budget.push(name.clone());
742 }
743 }
744
745 slow_operations.sort_by(|a, b| b.1.cmp(&a.1));
746
747 PerformanceReport {
748 total_operations: self.metrics.len(),
749 slow_operations,
750 over_budget,
751 suggestions: self.get_optimization_suggestions(),
752 }
753 }
754}
755
756#[derive(Debug, Clone)]
758pub struct PerformanceMetric {
759 pub name: String,
760 pub start_time: Instant,
761 pub duration: Duration,
762 pub call_count: u32,
763 pub min_duration: Duration,
764 pub max_duration: Duration,
765 pub total_duration: Duration,
766}
767
768#[derive(Debug, Clone)]
770pub struct PerformanceReport {
771 pub total_operations: usize,
772 pub slow_operations: Vec<(String, Duration)>,
773 pub over_budget: Vec<String>,
774 pub suggestions: Vec<String>,
775}
776
777#[derive(Debug, Clone)]
783pub struct DataPoint {
784 pub x: f64,
785 pub y: f64,
786 pub value: f64,
787}