1
0
Fork 0
forked from wry/wry

Skip tiny swap redistribution phases

This commit is contained in:
atagen 2026-05-28 17:13:24 +10:00
parent 09305ab026
commit 158682757a

View file

@ -1,6 +1,9 @@
use {crate::rect::Rect, crate::tree::NodeId}; use {crate::rect::Rect, crate::tree::NodeId};
const MIN_SHRINK_DENOMINATOR: i32 = 8; const MIN_SHRINK_DENOMINATOR: i32 = 8;
// Integer split remainders can make swapped siblings differ by one pixel. Do
// not spend a full animation phase on that imperceptible bookkeeping step.
const SWAP_AXIS_SNAP_PX: i32 = 1;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MultiphaseRequest { pub struct MultiphaseRequest {
@ -876,7 +879,10 @@ fn plan_axis_crossing_lanes(
main_start(window.to, axis), main_start(window.to, axis),
main_end(window.to, axis), main_end(window.to, axis),
); );
let lane_move = crossing_lane_move_rect(lane_from, window.to, axis); let mut lane_move = crossing_lane_move_rect(lane_from, window.to, axis);
if same_axis_delta_within(lane_move, lane_to, axis, SWAP_AXIS_SNAP_PX) {
lane_move = lane_to;
}
push_step(&mut phase1, window.node_id, window.from, lane_from); push_step(&mut phase1, window.node_id, window.from, lane_from);
push_step(&mut phase2, window.node_id, lane_from, lane_move); push_step(&mut phase2, window.node_id, lane_from, lane_move);
push_step(&mut phase3, window.node_id, lane_move, lane_to); push_step(&mut phase3, window.node_id, lane_move, lane_to);
@ -897,12 +903,10 @@ fn plan_axis_crossing_lanes(
lane_axis: axis.other(), lane_axis: axis.other(),
}, },
), ),
phase_draft( phase_draft_classified(
PhaseKind::Move,
axis,
phase2, phase2,
PhaseReason::MoveThroughFreedSpace, PhaseReason::MoveThroughFreedSpace,
), )?,
phase_draft( phase_draft(
PhaseKind::Scale, PhaseKind::Scale,
axis, axis,
@ -919,6 +923,17 @@ fn plan_axis_crossing_lanes(
) )
} }
fn phase_draft_classified(
steps: Vec<MultiphaseStep>,
reason: PhaseReason,
) -> Result<MultiphasePhaseDraft, MultiphasePlanFailure> {
let actions = steps
.iter()
.map(|step| classify_step(*step).ok_or(MultiphasePlanFailure::NoPattern))
.collect::<Result<Vec<_>, _>>()?;
Ok(phase_draft_mixed(steps, actions, reason))
}
fn crossing_lane_move_rect(from: Rect, target: Rect, axis: PhaseAxis) -> Rect { fn crossing_lane_move_rect(from: Rect, target: Rect, axis: PhaseAxis) -> Rect {
let size = main_size(from, axis); let size = main_size(from, axis);
if main_start(target, axis) > main_start(from, axis) { if main_start(target, axis) > main_start(from, axis) {
@ -930,6 +945,12 @@ fn crossing_lane_move_rect(from: Rect, target: Rect, axis: PhaseAxis) -> Rect {
} }
} }
fn same_axis_delta_within(from: Rect, to: Rect, axis: PhaseAxis, max_delta: i32) -> bool {
let start_delta = (main_start(from, axis) - main_start(to, axis)).abs();
let end_delta = (main_end(from, axis) - main_end(to, axis)).abs();
start_delta.max(end_delta) <= max_delta
}
fn lane_sort_key(window: MultiphaseWindow, axis: PhaseAxis) -> (usize, i32, i32, u32) { fn lane_sort_key(window: MultiphaseWindow, axis: PhaseAxis) -> (usize, i32, i32, u32) {
let delta = main_start(window.to, axis) - main_start(window.from, axis); let delta = main_start(window.to, axis) - main_start(window.from, axis);
let direction = match delta.cmp(&0) { let direction = match delta.cmp(&0) {
@ -2092,7 +2113,7 @@ mod tests {
} }
#[test] #[test]
fn uneven_swap_lanes_split_move_and_same_axis_scale() { fn one_pixel_uneven_swap_lanes_fold_remainder_into_crossing_phase() {
let req = request(vec![ let req = request(vec![
window(1, rect(0, 0, 101, 100), rect(101, 0, 201, 100)), window(1, rect(0, 0, 101, 100), rect(101, 0, 201, 100)),
window(2, rect(101, 0, 201, 100), rect(0, 0, 101, 100)), window(2, rect(101, 0, 201, 100), rect(0, 0, 101, 100)),
@ -2100,6 +2121,43 @@ mod tests {
let planned = plan_no_overlap_explained(&req).unwrap(); let planned = plan_no_overlap_explained(&req).unwrap();
let plan = &planned.plan; let plan = &planned.plan;
assert_eq!(
planned.explanation.strategy,
PlanStrategy::SwapLanes {
axis: PhaseAxis::Horizontal,
}
);
assert_eq!(
actions(plan),
vec![
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Vertical,
},
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Horizontal,
},
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Vertical,
},
]
);
assert_eq!(step_to(plan, 1, id(1)), rect(101, 0, 201, 50));
assert_eq!(step_to(plan, 1, id(2)), rect(0, 50, 101, 100));
assert!(validate_plan_continuous(&req, plan));
}
#[test]
fn large_uneven_swap_lanes_split_move_and_same_axis_scale() {
let req = request(vec![
window(1, rect(0, 0, 120, 100), rect(120, 0, 200, 100)),
window(2, rect(120, 0, 200, 100), rect(0, 0, 120, 100)),
]);
let planned = plan_no_overlap_explained(&req).unwrap();
let plan = &planned.plan;
assert_eq!( assert_eq!(
planned.explanation.strategy, planned.explanation.strategy,
PlanStrategy::SwapLanes { PlanStrategy::SwapLanes {
@ -2127,10 +2185,10 @@ mod tests {
}, },
] ]
); );
assert_eq!(step_to(plan, 1, id(1)), rect(100, 0, 201, 50)); assert_eq!(step_to(plan, 1, id(1)), rect(80, 0, 200, 50));
assert_eq!(step_to(plan, 1, id(2)), rect(0, 50, 100, 100)); assert_eq!(step_to(plan, 1, id(2)), rect(0, 50, 80, 100));
assert_eq!(step_to(plan, 2, id(1)), rect(101, 0, 201, 50)); assert_eq!(step_to(plan, 2, id(1)), rect(120, 0, 200, 50));
assert_eq!(step_to(plan, 2, id(2)), rect(0, 50, 101, 100)); assert_eq!(step_to(plan, 2, id(2)), rect(0, 50, 120, 100));
assert!(validate_plan_continuous(&req, plan)); assert!(validate_plan_continuous(&req, plan));
} }
@ -2149,6 +2207,23 @@ mod tests {
axis: PhaseAxis::Horizontal, axis: PhaseAxis::Horizontal,
} }
); );
assert_eq!(
actions(&planned.plan),
vec![
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Vertical,
},
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Horizontal,
},
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Vertical,
},
]
);
assert!(validate_plan_continuous(&req, &planned.plan)); assert!(validate_plan_continuous(&req, &planned.plan));
} }