Merge pull request #1409 from vangel76/feat-gap-slot1
#1408: Shift constrains mask vertex and whole-mask drags to 45°
This commit is contained in:
commit
4ac34587b0
|
|
@ -302,7 +302,10 @@ pub fn monitor_overlay(app: &mut FilmcraftApp, ui: &mut egui::Ui, pic: Rect, fra
|
|||
ui.data_mut(|d| d.insert_temp(gesture_id.with("body"), ok));
|
||||
}
|
||||
if body.dragged_by(LEFT) && ui.data(|d| d.get_temp::<bool>(gesture_id.with("body"))).unwrap_or(false) && body.drag_delta() != egui::Vec2::ZERO {
|
||||
actions.push(("masks.translate".into(), with(json!({"delta": clip_delta(body.drag_delta()), "merge": format!("body-{gesture}")}))));
|
||||
let d = drag_step(ui, egui::Id::new(("mask-body-drag", gesture)), body.drag_delta());
|
||||
if d != egui::Vec2::ZERO {
|
||||
actions.push(("masks.translate".into(), with(json!({"delta": clip_delta(d), "merge": format!("body-{gesture}")}))));
|
||||
}
|
||||
}
|
||||
if body.clicked() && !ptr_in(body.interact_pointer_pos()) {
|
||||
actions.push(("masks.select".into(), json!({"none": true})));
|
||||
|
|
@ -392,7 +395,10 @@ pub fn monitor_overlay(app: &mut FilmcraftApp, ui: &mut egui::Ui, pic: Rect, fra
|
|||
ui.data_mut(|d| d.insert_temp(sel_v_id, i));
|
||||
}
|
||||
if r.dragged_by(LEFT) && r.drag_delta() != egui::Vec2::ZERO {
|
||||
actions.push(("masks.moveVertex".into(), with(json!({"vertex": i, "delta": clip_delta(r.drag_delta()), "merge": format!("vertex-{gesture}")}))));
|
||||
let d = drag_step(ui, egui::Id::new(("mask-vertex-drag", gesture, i)), r.drag_delta());
|
||||
if d != egui::Vec2::ZERO {
|
||||
actions.push(("masks.moveVertex".into(), with(json!({"vertex": i, "delta": clip_delta(d), "merge": format!("vertex-{gesture}")}))));
|
||||
}
|
||||
}
|
||||
if r.clicked() {
|
||||
if alt {
|
||||
|
|
@ -678,6 +684,27 @@ fn open_polyline(path: &MaskPath, to_screen: &Affine) -> Vec<Pos2> {
|
|||
out
|
||||
}
|
||||
|
||||
/// This frame's drag delta for a whole-mask or vertex drag. With Shift held the movement since the
|
||||
/// press is constrained to 45° steps (horizontal, vertical, diagonal); the state (raw and applied
|
||||
/// totals) lives under `id`, which includes the pointer-press counter so each drag starts fresh.
|
||||
fn drag_step(ui: &egui::Ui, id: egui::Id, delta: egui::Vec2) -> egui::Vec2 {
|
||||
let shift = ui.input(|i| i.modifiers.shift);
|
||||
let (raw, applied) = ui.data(|d| d.get_temp::<(egui::Vec2, egui::Vec2)>(id)).unwrap_or_default();
|
||||
let (raw, applied, step) = shift_drag(raw, applied, delta, shift);
|
||||
ui.data_mut(|d| d.insert_temp(id, (raw, applied)));
|
||||
step
|
||||
}
|
||||
|
||||
/// Pure core of [`drag_step`]: `(raw total, applied total, step to apply now)`.
|
||||
fn shift_drag(raw: egui::Vec2, applied: egui::Vec2, delta: egui::Vec2, shift: bool) -> (egui::Vec2, egui::Vec2, egui::Vec2) {
|
||||
if !(delta.x.is_finite() && delta.y.is_finite()) {
|
||||
return (raw, applied, egui::Vec2::ZERO);
|
||||
}
|
||||
let raw = raw + delta;
|
||||
let target = if shift { constrain_45(Pos2::ZERO, raw.to_pos2()).to_vec2() } else { applied + delta };
|
||||
(raw, target, target - applied)
|
||||
}
|
||||
|
||||
/// `to` moved onto the nearest 45° ray from `from` (horizontal, vertical or diagonal on screen),
|
||||
/// keeping its distance along that ray; `to` itself when it coincides with `from` or is not finite.
|
||||
fn constrain_45(from: Pos2, to: Pos2) -> Pos2 {
|
||||
|
|
@ -754,6 +781,26 @@ pub fn path_value(app: &mut FilmcraftApp, ui: &mut egui::Ui, clip: ClipId, effec
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn shift_drag_constrains_total_movement_to_45_degree_steps() {
|
||||
use egui::vec2;
|
||||
let (mut raw, mut applied) = (egui::Vec2::ZERO, egui::Vec2::ZERO);
|
||||
let mut sum = egui::Vec2::ZERO;
|
||||
// a mostly horizontal drag stays on the x axis
|
||||
for d in [vec2(10.0, 2.0), vec2(10.0, 3.0), vec2(10.0, 1.0)] {
|
||||
let (r, a, s) = shift_drag(raw, applied, d, true);
|
||||
(raw, applied) = (r, a);
|
||||
sum += s;
|
||||
}
|
||||
assert!(sum.y.abs() < 1e-4 && (sum.x - 30.0).abs() < 1.0, "{sum:?}");
|
||||
// without shift the delta passes through unchanged
|
||||
let (_, _, s) = shift_drag(egui::Vec2::ZERO, egui::Vec2::ZERO, vec2(3.0, 4.0), false);
|
||||
assert_eq!(s, vec2(3.0, 4.0));
|
||||
// a non-finite delta is ignored
|
||||
let (_, _, s) = shift_drag(egui::Vec2::ZERO, egui::Vec2::ZERO, vec2(f32::NAN, 1.0), true);
|
||||
assert_eq!(s, egui::Vec2::ZERO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrain_45_snaps_to_the_nearest_eighth_turn() {
|
||||
let o = pos2(10.0, 20.0);
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ platforms** G4, G9, G17; **performance** G16; **ecosystem** G11; **measurement**
|
|||
|
||||
- **Missing:** Warp Stabilizer is 2-D (Premiere's is 3-D subspace), Morph Cut has no face tracking
|
||||
(and occlusion edges are soft), Lumetri HDR-aware maths, multicam paging beyond 16 angles,
|
||||
redesigned 26.0 mask tools (Shift-constrained vertex and whole-mask drags; a stored, re-editable
|
||||
redesigned 26.0 mask tools (a stored, re-editable
|
||||
corner radius: rounding bakes fillets into the path, so a rounded corner cannot be re-rounded later).
|
||||
- **Have:** Optical Flow time interpolation synthesises motion-compensated in-between frames (dense
|
||||
coarse-to-fine Lucas–Kanade, CPU; #780); Morph Cut morphs the subject along that optical flow
|
||||
|
|
@ -286,7 +286,8 @@ platforms** G4, G9, G17; **performance** G16; **ecosystem** G11; **measurement**
|
|||
Setting applied as a parameter preset (#458); rectangle and polygon masks round their corners
|
||||
with circular fillets (`masks.add` `cornerRadius`, `masks.roundCorners`, every path keyframe;
|
||||
#813), from an on-monitor corner-roundness handle with a live preview (one undo step per drag;
|
||||
#844); Shift snaps pen mask segments and dragged-out Bézier handles to 45° steps (#853).
|
||||
#844); Shift snaps pen mask segments and dragged-out Bézier handles to 45° steps (#853), and
|
||||
Shift constrains vertex and whole-mask drags to 45° steps from the press point (#1408).
|
||||
- **Estimate:** 60–110 h.
|
||||
|
||||
## G19. Speech to text off in release builds
|
||||
|
|
|
|||
Loading…
Reference in New Issue