renamite_behavior_common/
lib.rs1pub mod align;
4pub mod assets;
5pub mod color;
6pub mod context_menu;
7pub mod fill;
8pub mod inspect;
9pub mod layers;
10pub mod machine;
11pub mod modifiers;
12pub mod path;
13pub mod stroke;
14
15use glam::DVec2;
16use renamite_animation::Frame;
17use renamite_model::{CompId, Document, NodeId};
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
21pub struct Selection {
22 pub nodes: Vec<NodeId>,
23 pub comp: Option<CompId>,
25}
26
27impl Selection {
28 pub fn is_empty(&self) -> bool {
29 self.nodes.is_empty()
30 }
31
32 pub fn contains(&self, id: NodeId) -> bool {
33 self.nodes.contains(&id)
34 }
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
39pub struct ViewTransform {
40 pub scale: f64,
41 pub offset: DVec2,
42}
43
44impl ViewTransform {
45 pub fn identity() -> Self {
46 Self {
47 scale: 1.0,
48 offset: DVec2::ZERO,
49 }
50 }
51
52 pub fn screen_to_world(&self, p: DVec2) -> DVec2 {
53 (p - self.offset) / self.scale
54 }
55
56 pub fn world_to_screen(&self, p: DVec2) -> DVec2 {
57 p * self.scale + self.offset
58 }
59
60 pub fn world_tolerance(&self, px: f64) -> f64 {
62 px / self.scale
63 }
64
65 pub fn zoom_at(&mut self, screen_pos: DVec2, factor: f64, min: f64, max: f64) {
68 let world = self.screen_to_world(screen_pos);
69 self.scale = (self.scale * factor).clamp(min, max);
70 self.offset = screen_pos - world * self.scale;
71 }
72
73 pub fn pan_by(&mut self, delta: DVec2) {
74 self.offset += delta;
75 }
76
77 pub fn fit(&mut self, surface: DVec2, artboard: DVec2) {
81 fit_view(self, surface, artboard);
82 }
83}
84
85pub fn fit_view(view: &mut ViewTransform, surface: DVec2, artboard: DVec2) {
89 if surface.x <= 1.0 || surface.y <= 1.0 || artboard.x <= 0.0 || artboard.y <= 0.0 {
90 return;
91 }
92 let margin = 56.0;
93 let available = (surface - DVec2::splat(margin * 2.0)).max(DVec2::splat(1.0));
94 let scale = (available.x / artboard.x)
95 .min(available.y / artboard.y)
96 .clamp(0.05, 32.0);
97 view.scale = scale;
98 view.offset = (surface - artboard * scale) * 0.5;
99}
100
101pub fn fit_exact_view(view: &mut ViewTransform, surface: DVec2, artboard: DVec2) {
105 if surface.x <= 1.0 || surface.y <= 1.0 || artboard.x <= 0.0 || artboard.y <= 0.0 {
106 return;
107 }
108 let scale = (surface.x / artboard.x)
109 .min(surface.y / artboard.y)
110 .clamp(0.05, 64.0);
111 view.scale = scale;
112 view.offset = (surface - artboard * scale) * 0.5;
113}
114
115#[derive(Clone, Copy, Debug, Default, PartialEq)]
119pub struct FitState {
120 surface: DVec2,
121 artboard: DVec2,
122 pub pending: bool,
123}
124
125impl FitState {
126 pub fn new() -> Self {
127 Self {
128 surface: DVec2::ZERO,
129 artboard: DVec2::ZERO,
130 pending: true,
131 }
132 }
133
134 pub fn with_surface(surface: DVec2) -> Self {
137 Self {
138 surface,
139 artboard: DVec2::ZERO,
140 pending: false,
141 }
142 }
143
144 pub fn ensure(
153 &mut self,
154 view: &mut ViewTransform,
155 surface: DVec2,
156 artboard: DVec2,
157 refit_on_resize: bool,
158 ) -> bool {
159 let resized = (surface - self.surface).abs().max_element() > 0.5;
160 let art_changed = (artboard - self.artboard).abs().max_element() > 0.5;
161 let first_layout = self.surface == DVec2::ZERO && surface != DVec2::ZERO;
162 self.surface = surface;
163 if self.pending || first_layout || art_changed || (refit_on_resize && resized) {
164 view.fit(surface, artboard);
165 self.artboard = artboard;
166 self.pending = false;
167 true
168 } else {
169 false
170 }
171 }
172
173 pub fn request(&mut self) {
176 self.pending = true;
177 }
178
179 pub fn surface_size(&self) -> DVec2 {
180 self.surface
181 }
182
183 pub fn zoom_centered(&self, view: &mut ViewTransform, factor: f64) {
185 if self.surface == DVec2::ZERO {
186 return;
187 }
188 self.zoom_at(view, self.surface * 0.5, factor);
189 }
190
191 pub fn zoom_at(&self, view: &mut ViewTransform, screen_pos: DVec2, factor: f64) {
193 if self.surface == DVec2::ZERO {
194 return;
195 }
196 view.zoom_at(screen_pos, factor, 0.05, 64.0);
197 }
198}
199
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
201pub struct Modifiers {
202 pub shift: bool,
203 pub alt: bool,
204 pub ctrl: bool,
205}
206
207impl Modifiers {
208 pub fn none() -> Self {
209 Self {
210 shift: false,
211 alt: false,
212 ctrl: false,
213 }
214 }
215}
216
217#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
218pub struct SnapConfig {
219 pub grid: Option<f64>,
220 pub anchor: bool,
221 pub guide: bool,
222}
223
224pub struct ToolContext<'a> {
225 pub doc: &'a Document,
226 pub scene: &'a renamite_model::Scene,
228 pub comp: CompId,
229 pub selection: &'a Selection,
230 pub playhead: Frame,
231 pub record: bool,
232 pub view: ViewTransform,
233 pub snap: SnapConfig,
234 pub modifiers: Modifiers,
235 pub current_paint: &'a renamite_model::StylePaint,
237}