Skip to main content

renamite_behavior_common/
lib.rs

1//! Shared context for tool behaviors: selection, view transform, snapping.
2
3pub mod assets;
4pub mod color;
5pub mod context_menu;
6pub mod fill;
7pub mod inspect;
8pub mod layers;
9pub mod machine;
10pub mod modifiers;
11pub mod path;
12pub mod stroke;
13
14use glam::DVec2;
15use renamite_animation::Frame;
16use renamite_model::{CompId, Document, NodeId};
17use serde::{Deserialize, Serialize};
18
19#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
20pub struct Selection {
21    pub nodes: Vec<NodeId>,
22    /// Optional focus target when editing a group/precomp's contents.
23    pub comp: Option<CompId>,
24}
25
26impl Selection {
27    pub fn is_empty(&self) -> bool {
28        self.nodes.is_empty()
29    }
30
31    pub fn contains(&self, id: NodeId) -> bool {
32        self.nodes.contains(&id)
33    }
34}
35
36/// Screen ↔ world mapping. Px tolerance in world units.
37#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
38pub struct ViewTransform {
39    pub scale: f64,
40    pub offset: DVec2,
41}
42
43impl ViewTransform {
44    pub fn identity() -> Self {
45        Self {
46            scale: 1.0,
47            offset: DVec2::ZERO,
48        }
49    }
50
51    pub fn screen_to_world(&self, p: DVec2) -> DVec2 {
52        (p - self.offset) / self.scale
53    }
54
55    pub fn world_to_screen(&self, p: DVec2) -> DVec2 {
56        p * self.scale + self.offset
57    }
58
59    /// Tolerance in world units for a sub-pixel screen tolerance (0.25px).
60    pub fn world_tolerance(&self, px: f64) -> f64 {
61        px / self.scale
62    }
63
64    /// Zoom about `screen_pos` by `factor`, clamped to `[min, max]`.
65    /// Shared by canvas viewport and machine graph to stay DRY.
66    pub fn zoom_at(&mut self, screen_pos: DVec2, factor: f64, min: f64, max: f64) {
67        let world = self.screen_to_world(screen_pos);
68        self.scale = (self.scale * factor).clamp(min, max);
69        self.offset = screen_pos - world * self.scale;
70    }
71
72    pub fn pan_by(&mut self, delta: DVec2) {
73        self.offset += delta;
74    }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
78pub struct Modifiers {
79    pub shift: bool,
80    pub alt: bool,
81    pub ctrl: bool,
82}
83
84impl Modifiers {
85    pub fn none() -> Self {
86        Self {
87            shift: false,
88            alt: false,
89            ctrl: false,
90        }
91    }
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
95pub struct SnapConfig {
96    pub grid: Option<f64>,
97    pub anchor: bool,
98    pub guide: bool,
99}
100
101pub struct ToolContext<'a> {
102    pub doc: &'a Document,
103    /// Evaluated frame currently on screen - the hit-test surface.
104    pub scene: &'a renamite_model::Scene,
105    pub comp: CompId,
106    pub selection: &'a Selection,
107    pub playhead: Frame,
108    pub record: bool,
109    pub view: ViewTransform,
110    pub snap: SnapConfig,
111    pub modifiers: Modifiers,
112    /// Current paint used by the Fill tool (set from Properties or a future picker).
113    pub current_paint: &'a renamite_model::StylePaint,
114}