Carry hierarchy metadata into multiphase planning
This commit is contained in:
parent
a712786ecf
commit
90c00bcdf3
4 changed files with 276 additions and 16 deletions
|
|
@ -13,6 +13,104 @@ pub struct MultiphaseWindow {
|
|||
pub node_id: NodeId,
|
||||
pub from: Rect,
|
||||
pub to: Rect,
|
||||
pub hierarchy: MultiphaseWindowHierarchy,
|
||||
}
|
||||
|
||||
impl MultiphaseWindow {
|
||||
pub fn new(node_id: NodeId, from: Rect, to: Rect) -> Self {
|
||||
Self {
|
||||
node_id,
|
||||
from,
|
||||
to,
|
||||
hierarchy: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hierarchy(
|
||||
node_id: NodeId,
|
||||
from: Rect,
|
||||
to: Rect,
|
||||
hierarchy: MultiphaseWindowHierarchy,
|
||||
) -> Self {
|
||||
Self {
|
||||
node_id,
|
||||
from,
|
||||
to,
|
||||
hierarchy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct MultiphaseWindowHierarchy {
|
||||
pub source: MultiphaseHierarchyPosition,
|
||||
pub target: MultiphaseHierarchyPosition,
|
||||
pub transition: MultiphaseHierarchyTransition,
|
||||
}
|
||||
|
||||
impl MultiphaseWindowHierarchy {
|
||||
pub fn new(source: MultiphaseHierarchyPosition, target: MultiphaseHierarchyPosition) -> Self {
|
||||
let transition = if !source.parent_is_mono && target.parent_is_mono {
|
||||
MultiphaseHierarchyTransition::EnteringMono
|
||||
} else if source.parent_is_mono && !target.parent_is_mono {
|
||||
MultiphaseHierarchyTransition::ExitingMono
|
||||
} else if source.parent.is_none() || target.parent.is_none() {
|
||||
MultiphaseHierarchyTransition::Unknown
|
||||
} else if target.depth < source.depth {
|
||||
MultiphaseHierarchyTransition::Ascending
|
||||
} else if target.depth > source.depth {
|
||||
MultiphaseHierarchyTransition::Descending
|
||||
} else {
|
||||
MultiphaseHierarchyTransition::SameLevel
|
||||
};
|
||||
Self {
|
||||
source,
|
||||
target,
|
||||
transition,
|
||||
}
|
||||
}
|
||||
|
||||
fn reversed(self) -> Self {
|
||||
Self {
|
||||
source: self.target,
|
||||
target: self.source,
|
||||
transition: self.transition.reversed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct MultiphaseHierarchyPosition {
|
||||
pub parent: Option<NodeId>,
|
||||
pub depth: u16,
|
||||
pub sibling_index: Option<u16>,
|
||||
pub split_axis: Option<PhaseAxis>,
|
||||
pub parent_is_mono: bool,
|
||||
pub mono_active: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum MultiphaseHierarchyTransition {
|
||||
#[default]
|
||||
Unknown,
|
||||
SameLevel,
|
||||
Ascending,
|
||||
Descending,
|
||||
EnteringMono,
|
||||
ExitingMono,
|
||||
}
|
||||
|
||||
impl MultiphaseHierarchyTransition {
|
||||
fn reversed(self) -> Self {
|
||||
match self {
|
||||
Self::Unknown => Self::Unknown,
|
||||
Self::SameLevel => Self::SameLevel,
|
||||
Self::Ascending => Self::Descending,
|
||||
Self::Descending => Self::Ascending,
|
||||
Self::EnteringMono => Self::ExitingMono,
|
||||
Self::ExitingMono => Self::EnteringMono,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -519,6 +617,7 @@ fn reverse_request(request: &MultiphaseRequest) -> MultiphaseRequest {
|
|||
node_id: window.node_id,
|
||||
from: window.to,
|
||||
to: window.from,
|
||||
hierarchy: window.hierarchy.reversed(),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
|
|
@ -628,6 +727,10 @@ mod tests {
|
|||
Rect::new_saturating(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
fn window(raw: u32, from: Rect, to: Rect) -> MultiphaseWindow {
|
||||
MultiphaseWindow::new(id(raw), from, to)
|
||||
}
|
||||
|
||||
fn request(windows: Vec<MultiphaseWindow>) -> MultiphaseRequest {
|
||||
let bounds = windows
|
||||
.iter()
|
||||
|
|
@ -653,15 +756,12 @@ mod tests {
|
|||
#[test]
|
||||
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
|
||||
let req = request(vec![
|
||||
MultiphaseWindow {
|
||||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
},
|
||||
window(1, rect(0, 0, 100, 100), rect(100, 0, 200, 100)),
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -694,11 +794,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -714,11 +816,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -734,11 +838,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 100, 100, 200),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 100, 100, 200),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -754,16 +860,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(200, 0, 400, 50),
|
||||
to: rect(100, 0, 300, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(200, 50, 400, 100),
|
||||
to: rect(300, 0, 400, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -799,16 +908,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 300, 100),
|
||||
to: rect(200, 0, 400, 50),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(300, 0, 400, 100),
|
||||
to: rect(200, 50, 400, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -839,16 +951,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 200),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 200, 50, 400),
|
||||
to: rect(0, 100, 100, 300),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(50, 200, 100, 400),
|
||||
to: rect(0, 300, 100, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -884,16 +999,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 0, 100, 200),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 100, 100, 300),
|
||||
to: rect(0, 200, 50, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(0, 300, 100, 400),
|
||||
to: rect(50, 200, 100, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -923,10 +1041,50 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 100, 200, 200),
|
||||
hierarchy: Default::default(),
|
||||
}]);
|
||||
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hierarchy_metadata_classifies_depth_and_mono_transitions() {
|
||||
let source = MultiphaseHierarchyPosition {
|
||||
parent: Some(id(10)),
|
||||
depth: 2,
|
||||
sibling_index: Some(0),
|
||||
split_axis: Some(PhaseAxis::Vertical),
|
||||
..Default::default()
|
||||
};
|
||||
let target = MultiphaseHierarchyPosition {
|
||||
parent: Some(id(11)),
|
||||
depth: 1,
|
||||
sibling_index: Some(2),
|
||||
split_axis: Some(PhaseAxis::Horizontal),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
MultiphaseWindowHierarchy::new(source, target).transition,
|
||||
MultiphaseHierarchyTransition::Ascending
|
||||
);
|
||||
|
||||
let entering_mono = MultiphaseWindowHierarchy::new(
|
||||
source,
|
||||
MultiphaseHierarchyPosition {
|
||||
parent_is_mono: true,
|
||||
mono_active: true,
|
||||
..target
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
entering_mono.transition,
|
||||
MultiphaseHierarchyTransition::EnteringMono
|
||||
);
|
||||
assert_eq!(
|
||||
entering_mono.reversed().transition,
|
||||
MultiphaseHierarchyTransition::ExitingMono
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continuous_validation_rejects_narrow_mid_phase_overlap() {
|
||||
let req = request(vec![
|
||||
|
|
@ -934,11 +1092,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(100, 0, 110, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(13, 0, 14, 10),
|
||||
to: rect(13, 0, 14, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = MultiphasePlan {
|
||||
|
|
@ -965,11 +1125,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(10, 0, 20, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(20, 0, 30, 10),
|
||||
to: rect(20, 0, 30, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = MultiphasePlan {
|
||||
|
|
@ -995,6 +1157,7 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(20, 0, 30, 10),
|
||||
hierarchy: Default::default(),
|
||||
}]);
|
||||
let plan = MultiphasePlan {
|
||||
phases: vec![MultiphasePhase {
|
||||
|
|
@ -1020,16 +1183,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(300, 0, 400, 100),
|
||||
to: rect(400, 0, 500, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
];
|
||||
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1], vec![2]]);
|
||||
|
|
@ -1042,16 +1208,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(80, 0, 180, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(170, 0, 270, 100),
|
||||
to: rect(250, 0, 350, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(90, 0, 180, 100),
|
||||
to: rect(180, 0, 260, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
];
|
||||
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1, 2]]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue