1
0
Fork 0
forked from wry/wry

Choose swap lanes by motion direction

This commit is contained in:
atagen 2026-05-21 19:48:13 +10:00
parent b109cdf6f2
commit a712786ecf
2 changed files with 69 additions and 3 deletions

View file

@ -217,6 +217,9 @@ Preferred approach:
Current pure planner status:
- Two-window same-axis swaps use shrink lanes, move, then grow.
- Swap lane choice follows motion direction, not node identity: right/down
moving windows take the first lane, and left/up moving windows take the second
lane.
- Stack extraction/return patterns are covered in both horizontal and vertical
orientations: peer/container space scales first, the extracted child moves
only after space exists, and orthogonal growth happens in the final phase.

View file

@ -173,7 +173,12 @@ fn plan_axis_crossing_lanes(
}
let mut windows = request.windows.clone();
windows.sort_by_key(|window| window.node_id.0);
windows.sort_by_key(|window| lane_index_for_direction(*window, axis));
if windows.windows(2).any(|pair| {
lane_index_for_direction(pair[0], axis) == lane_index_for_direction(pair[1], axis)
}) {
return None;
}
let mut phase1 = vec![];
let mut phase2 = vec![];
let mut phase3 = vec![];
@ -205,6 +210,15 @@ fn plan_axis_crossing_lanes(
)
}
fn lane_index_for_direction(window: MultiphaseWindow, axis: PhaseAxis) -> Option<usize> {
let delta = main_start(window.to, axis) - main_start(window.from, axis);
match delta.cmp(&0) {
std::cmp::Ordering::Greater => Some(0),
std::cmp::Ordering::Less => Some(1),
std::cmp::Ordering::Equal => None,
}
}
fn plan_space_then_orthogonal_growth(
request: &MultiphaseRequest,
axis: PhaseAxis,
@ -627,6 +641,15 @@ mod tests {
plan.phases.iter().map(|phase| phase.action).collect()
}
fn step_to(plan: &MultiphasePlan, phase: usize, node_id: NodeId) -> Rect {
plan.phases[phase]
.steps
.iter()
.find(|step| step.node_id == node_id)
.unwrap()
.to
}
#[test]
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
let req = request(vec![
@ -679,8 +702,48 @@ mod tests {
},
]);
let plan = plan_no_overlap(&req).unwrap();
assert_eq!(plan.phases[0].steps[0].to, rect(100, 0, 200, 50));
assert_eq!(plan.phases[0].steps[1].to, rect(0, 50, 100, 100));
assert_eq!(step_to(&plan, 0, id(2)), rect(0, 0, 100, 50));
assert_eq!(step_to(&plan, 0, id(1)), rect(100, 50, 200, 100));
assert!(validate_plan_continuous(&req, &plan));
}
#[test]
fn horizontal_swap_lanes_follow_motion_direction_not_node_id() {
let req = request(vec![
MultiphaseWindow {
node_id: id(1),
from: rect(100, 0, 200, 100),
to: rect(0, 0, 100, 100),
},
MultiphaseWindow {
node_id: id(2),
from: rect(0, 0, 100, 100),
to: rect(100, 0, 200, 100),
},
]);
let plan = plan_no_overlap(&req).unwrap();
assert_eq!(step_to(&plan, 0, id(2)), rect(0, 0, 100, 50));
assert_eq!(step_to(&plan, 0, id(1)), rect(100, 50, 200, 100));
assert!(validate_plan_continuous(&req, &plan));
}
#[test]
fn vertical_swap_lanes_follow_motion_direction_not_node_id() {
let req = request(vec![
MultiphaseWindow {
node_id: id(1),
from: rect(0, 100, 100, 200),
to: rect(0, 0, 100, 100),
},
MultiphaseWindow {
node_id: id(2),
from: rect(0, 0, 100, 100),
to: rect(0, 100, 100, 200),
},
]);
let plan = plan_no_overlap(&req).unwrap();
assert_eq!(step_to(&plan, 0, id(2)), rect(0, 0, 50, 100));
assert_eq!(step_to(&plan, 0, id(1)), rect(50, 100, 100, 200));
assert!(validate_plan_continuous(&req, &plan));
}