mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #924 from MarekSpodBiedry/issue-912
Resume an open mask path when the Pen clicks its last vertex (#912)
This commit is contained in:
commit
ce3e8f1a99
2 files changed
+42
-5
No files matched your search
@@ -1296,7 +1296,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
let mods = ui.input(|i| i.modifiers);
|
||||
let space_pan = super::space_hand(&ctx);
|
||||
// Pen tool: each press places a vertex (dragging pulls Bezier tangents); pressing on the
|
||||
// first vertex closes the path.
|
||||
// first vertex closes the path, pressing an open path's last vertex resumes it.
|
||||
if app.ui.tool == Tool::Pen
|
||||
&& !space_pan
|
||||
&& resp.hovered()
|
||||
@@ -2059,10 +2059,10 @@ fn draw_rigs(
|
||||
}
|
||||
|
||||
/// Pen tool press at `pos`: continue the path in progress (pressing its first vertex closes it),
|
||||
/// add a vertex on a visible path's segment, or start a new path: a mask on the selected
|
||||
/// footage/solid layer (or shape layer, with Tool Creates Mask), a shape path (Shape n group with
|
||||
/// the Tools bar's Fill and Stroke) on the selected shape layer, or a new shape layer when
|
||||
/// nothing is selected. The placed point snaps.
|
||||
/// resume an open path whose last vertex is pressed, add a vertex on a visible path's segment, or
|
||||
/// start a new path: a mask on the selected footage/solid layer (or shape layer, with Tool Creates
|
||||
/// Mask), a shape path (Shape n group with the Tools bar's Fill and Stroke) on the selected shape
|
||||
/// layer, or a new shape layer when nothing is selected. The placed point snaps.
|
||||
/// Add a Position keyframe at a motion path's frame, keeping the path where it is.
|
||||
fn add_motion_key(app: &mut EffectcraftApp, f: ov::PathFrame) {
|
||||
if let Err(e) = app.session.execute("prop.addKey", json!({"layer": f.layer.0, "prop": f.prop, "time": f.time.seconds()})) {
|
||||
@@ -2148,6 +2148,11 @@ fn pen_press(
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Clicking the last vertex of an open path (e.g. after Esc ended it) resumes drawing it.
|
||||
if let Some(cur) = ov::open_end_at(paths, map, pos, 8.0) {
|
||||
ui.data_mut(|d| d.insert_temp(pen_id(), cur));
|
||||
return;
|
||||
}
|
||||
// On a visible path's segment the Pen adds a vertex there; on a motion path, a keyframe.
|
||||
if let Some(f) = motion_frame {
|
||||
add_motion_key(app, f);
|
||||
|
||||
@@ -87,6 +87,15 @@ pub(crate) fn segment_at(paths: &[PathInfo], map: &ViewerMap, pos: Pos2, tol: f3
|
||||
best.map(|b| b.1)
|
||||
}
|
||||
|
||||
/// The open path whose last vertex lies within `tol` points of `pos`: the Pen resumes it there.
|
||||
pub(crate) fn open_end_at(paths: &[PathInfo], map: &ViewerMap, pos: Pos2, tol: f32) -> Option<(LayerId, u64)> {
|
||||
paths.iter().find_map(|p| {
|
||||
let v = p.sp.vertices.last().filter(|_| !p.sp.closed)?;
|
||||
let q = p.m.apply(gv2(v[0], v[1]));
|
||||
(map.to_screen([q.x, q.y]).distance(pos) < tol).then_some((p.layer, p.uid))
|
||||
})
|
||||
}
|
||||
|
||||
/// A mask or shape path of a layer by group uid, evaluated at the context time.
|
||||
pub(crate) fn path_of(ectx: &EvalCtx, layer: LayerId, uid: u64) -> Option<PathInfo> {
|
||||
let l = ectx.comp.layer(layer)?;
|
||||
@@ -640,3 +649,26 @@ pub(crate) fn update(
|
||||
Drag::Roi { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn info(vertices: Vec<[f64; 2]>, closed: bool) -> PathInfo {
|
||||
let n = vertices.len();
|
||||
let sp = ShapePath { vertices, in_tangents: vec![[0.0; 2]; n], out_tangents: vec![[0.0; 2]; n], closed, feather: Vec::new() };
|
||||
PathInfo { layer: LayerId(3), uid: 7, m: Mat3::IDENTITY, sp, is_mask: true, feather: Vec::new() }
|
||||
}
|
||||
|
||||
/// #912: clicking the last vertex of an open path with the Pen resumes that path.
|
||||
#[test]
|
||||
fn pen_resumes_an_open_path_at_its_last_vertex() {
|
||||
let map = ViewerMap { origin: Pos2::ZERO, zoom: 1.0, comp: [100.0, 100.0], area: Rect::EVERYTHING };
|
||||
let open = info(vec![[10.0, 10.0], [50.0, 10.0], [50.0, 50.0]], false);
|
||||
assert_eq!(open_end_at(std::slice::from_ref(&open), &map, egui::pos2(52.0, 49.0), 8.0), Some((LayerId(3), 7)));
|
||||
assert_eq!(open_end_at(std::slice::from_ref(&open), &map, egui::pos2(10.0, 10.0), 8.0), None, "first vertex");
|
||||
assert_eq!(open_end_at(std::slice::from_ref(&open), &map, egui::pos2(80.0, 80.0), 8.0), None, "far away");
|
||||
let closed = info(vec![[10.0, 10.0], [50.0, 10.0], [50.0, 50.0]], true);
|
||||
assert_eq!(open_end_at(&[closed], &map, egui::pos2(50.0, 50.0), 8.0), None, "closed path");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user