ringkernel_procint/gui/canvas/
mod.rs

1//! Canvas rendering for process visualization.
2//!
3//! Provides force-directed DFG and timeline views.
4
5mod dfg_canvas;
6mod timeline_canvas;
7mod token_animation;
8
9pub use dfg_canvas::*;
10pub use timeline_canvas::*;
11pub use token_animation::*;
12
13use eframe::egui::{Pos2, Vec2};
14
15/// Camera for canvas panning and zooming.
16#[derive(Debug, Clone)]
17pub struct Camera {
18    /// Pan offset.
19    pub offset: Vec2,
20    /// Zoom level.
21    pub zoom: f32,
22    /// Target zoom (for smooth animation).
23    target_zoom: f32,
24    /// Target offset.
25    target_offset: Vec2,
26}
27
28impl Default for Camera {
29    fn default() -> Self {
30        Self {
31            offset: Vec2::ZERO,
32            zoom: 1.0,
33            target_zoom: 1.0,
34            target_offset: Vec2::ZERO,
35        }
36    }
37}
38
39impl Camera {
40    /// Transform world position to screen position.
41    pub fn world_to_screen(&self, pos: Pos2, canvas_center: Pos2) -> Pos2 {
42        let transformed = (pos.to_vec2() + self.offset) * self.zoom;
43        canvas_center + transformed
44    }
45
46    /// Transform screen position to world position.
47    pub fn screen_to_world(&self, pos: Pos2, canvas_center: Pos2) -> Pos2 {
48        let relative = pos - canvas_center;
49        Pos2::ZERO + (relative / self.zoom) - self.offset
50    }
51
52    /// Handle zoom input.
53    pub fn zoom_by(&mut self, delta: f32, around: Pos2, canvas_center: Pos2) {
54        let world_pos = self.screen_to_world(around, canvas_center);
55        self.target_zoom = (self.target_zoom * (1.0 + delta * 0.1)).clamp(0.1, 5.0);
56
57        // Adjust offset to zoom around cursor
58        let new_screen = self.world_to_screen(world_pos, canvas_center);
59        self.target_offset += (around - new_screen) / self.target_zoom;
60    }
61
62    /// Handle pan input.
63    pub fn pan_by(&mut self, delta: Vec2) {
64        self.target_offset += delta / self.zoom;
65    }
66
67    /// Reset camera.
68    pub fn reset(&mut self) {
69        self.target_zoom = 1.0;
70        self.target_offset = Vec2::ZERO;
71    }
72
73    /// Update animation (call each frame).
74    pub fn update(&mut self, dt: f32) {
75        let lerp_speed = 8.0 * dt;
76        self.zoom += (self.target_zoom - self.zoom) * lerp_speed;
77        self.offset += (self.target_offset - self.offset) * lerp_speed;
78    }
79}
80
81/// Position in the force-directed layout.
82#[derive(Debug, Clone, Copy)]
83pub struct NodePosition {
84    pub x: f32,
85    pub y: f32,
86    pub vx: f32,
87    pub vy: f32,
88}
89
90impl Default for NodePosition {
91    fn default() -> Self {
92        Self {
93            x: 0.0,
94            y: 0.0,
95            vx: 0.0,
96            vy: 0.0,
97        }
98    }
99}
100
101impl NodePosition {
102    pub fn new(x: f32, y: f32) -> Self {
103        Self {
104            x,
105            y,
106            vx: 0.0,
107            vy: 0.0,
108        }
109    }
110
111    pub fn pos(&self) -> Pos2 {
112        Pos2::new(self.x, self.y)
113    }
114}