renamite_behavior_common/
path.rs1use renamite_animation::Frame;
4use renamite_history::EditorCommand;
5use renamite_model::{Document, NodeId, PropPath};
6
7pub fn path_edit_target(
15 doc: &Document,
16 id: NodeId,
17 playhead: Frame,
18 record: bool,
19) -> Option<(Option<Frame>, Option<EditorCommand>)> {
20 let prop = PropPath::new("shape.path");
21 let animated = doc.property_is_animated(id, &prop);
22
23 if !record && !animated {
24 return Some((None, None));
25 }
26
27 if doc.keyframe_data(id, &prop, playhead).is_some() {
28 return Some((Some(playhead), None));
29 }
30
31 let value = doc.value_at(id, &prop, playhead.0 as f64).ok()?;
32 Some((
33 Some(playhead),
34 Some(EditorCommand::AddKeyframe {
35 id,
36 prop,
37 frame: playhead,
38 value,
39 }),
40 ))
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use renamite_animation::Animated;
47 use renamite_geometry::VectorPath;
48 use renamite_model::{Node, NodeKind, Parent, ShapeKind, Value};
49
50 fn doc_with_path() -> (Document, NodeId) {
51 let mut doc = Document::empty();
52 let id = doc.create_node(Node::new(
53 "path",
54 NodeKind::Shape(ShapeKind::Path(Animated::new(VectorPath::default()))),
55 ));
56 doc.attach(id, Parent::Comp(doc.main), 0).unwrap();
57 (doc, id)
58 }
59
60 #[test]
61 fn static_no_record_targets_base() {
62 let (doc, id) = doc_with_path();
63 let (frame, seed) = path_edit_target(&doc, id, Frame(10), false).unwrap();
64 assert_eq!(frame, None);
65 assert!(seed.is_none());
66 }
67
68 #[test]
69 fn record_seeds_playhead_key() {
70 let (doc, id) = doc_with_path();
71 let (frame, seed) = path_edit_target(&doc, id, Frame(10), true).unwrap();
72 assert_eq!(frame, Some(Frame(10)));
73 assert!(matches!(
74 seed,
75 Some(EditorCommand::AddKeyframe {
76 frame: Frame(10),
77 ..
78 })
79 ));
80 }
81
82 #[test]
83 fn animated_without_key_at_playhead_seeds() {
84 let (mut doc, id) = doc_with_path();
85 let prop = PropPath::new("shape.path");
86 doc.add_keyframe(id, &prop, Frame(0), &Value::Path(VectorPath::default()))
87 .unwrap();
88
89 let (frame, seed) = path_edit_target(&doc, id, Frame(5), false).unwrap();
90 assert_eq!(frame, Some(Frame(5)));
91 assert!(seed.is_some());
92 }
93
94 #[test]
95 fn animated_with_key_at_playhead_needs_no_seed() {
96 let (mut doc, id) = doc_with_path();
97 let prop = PropPath::new("shape.path");
98 doc.add_keyframe(id, &prop, Frame(10), &Value::Path(VectorPath::default()))
99 .unwrap();
100
101 let (frame, seed) = path_edit_target(&doc, id, Frame(10), false).unwrap();
102 assert_eq!(frame, Some(Frame(10)));
103 assert!(seed.is_none());
104 }
105}