Report multiphase planning diagnostics
This commit is contained in:
parent
90c00bcdf3
commit
cc898590d2
3 changed files with 230 additions and 56 deletions
|
|
@ -233,6 +233,10 @@ Current pure planner status:
|
||||||
parent, depth, sibling index, split axis, mono state, and transition kind.
|
parent, depth, sibling index, split axis, mono state, and transition kind.
|
||||||
The current planner records this information but does not yet use it to order
|
The current planner records this information but does not yet use it to order
|
||||||
nested-container phases.
|
nested-container phases.
|
||||||
|
- Multiphase planning has a diagnostic entry point used by live fallback logs.
|
||||||
|
It distinguishes request validation errors, missing patterns, shrink-bound
|
||||||
|
rejections, invalid phase steps, and exact validation failures such as stale
|
||||||
|
starts or phase overlap.
|
||||||
|
|
||||||
Tests:
|
Tests:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,8 +159,59 @@ pub enum MultiphaseError {
|
||||||
NoPlan,
|
NoPlan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct MultiphasePlanDiagnostic {
|
||||||
|
pub forward: MultiphasePlanFailure,
|
||||||
|
pub reverse: Option<MultiphasePlanFailure>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiphasePlanDiagnostic {
|
||||||
|
fn legacy_error(self) -> MultiphaseError {
|
||||||
|
match self.forward {
|
||||||
|
MultiphasePlanFailure::Request(error) => error,
|
||||||
|
_ => MultiphaseError::NoPlan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MultiphasePlanFailure {
|
||||||
|
Request(MultiphaseError),
|
||||||
|
NoPattern,
|
||||||
|
ShrinkBound {
|
||||||
|
axis: PhaseAxis,
|
||||||
|
available: i32,
|
||||||
|
required: i32,
|
||||||
|
},
|
||||||
|
InvalidPhaseStep {
|
||||||
|
action: PhaseAction,
|
||||||
|
node_id: NodeId,
|
||||||
|
},
|
||||||
|
Validation(MultiphaseValidationError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MultiphaseValidationError {
|
||||||
|
DuplicatePhaseStep { phase: usize, node_id: NodeId },
|
||||||
|
UnknownPhaseStep { phase: usize, node_id: NodeId },
|
||||||
|
StaleStepStart { phase: usize, node_id: NodeId },
|
||||||
|
PhaseOverlap { phase: usize, a: NodeId, b: NodeId },
|
||||||
|
FinalMismatch { node_id: NodeId },
|
||||||
|
}
|
||||||
|
|
||||||
pub fn plan_no_overlap(request: &MultiphaseRequest) -> Result<MultiphasePlan, MultiphaseError> {
|
pub fn plan_no_overlap(request: &MultiphaseRequest) -> Result<MultiphasePlan, MultiphaseError> {
|
||||||
validate_request(request)?;
|
plan_no_overlap_with_diagnostics(request).map_err(|diagnostic| diagnostic.legacy_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plan_no_overlap_with_diagnostics(
|
||||||
|
request: &MultiphaseRequest,
|
||||||
|
) -> Result<MultiphasePlan, MultiphasePlanDiagnostic> {
|
||||||
|
if let Err(error) = validate_request(request) {
|
||||||
|
return Err(MultiphasePlanDiagnostic {
|
||||||
|
forward: MultiphasePlanFailure::Request(error),
|
||||||
|
reverse: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
if request
|
if request
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -168,14 +219,18 @@ pub fn plan_no_overlap(request: &MultiphaseRequest) -> Result<MultiphasePlan, Mu
|
||||||
{
|
{
|
||||||
return Ok(MultiphasePlan { phases: vec![] });
|
return Ok(MultiphasePlan { phases: vec![] });
|
||||||
}
|
}
|
||||||
if let Some(plan) = plan_forward(request) {
|
let forward = match plan_forward(request) {
|
||||||
return Ok(plan);
|
Ok(plan) => return Ok(plan),
|
||||||
}
|
Err(error) => error,
|
||||||
|
};
|
||||||
let reversed = reverse_request(request);
|
let reversed = reverse_request(request);
|
||||||
if let Some(plan) = plan_forward(&reversed) {
|
match plan_forward(&reversed) {
|
||||||
return Ok(reverse_plan(plan));
|
Ok(plan) => Ok(reverse_plan(plan)),
|
||||||
|
Err(reverse) => Err(MultiphasePlanDiagnostic {
|
||||||
|
forward,
|
||||||
|
reverse: Some(reverse),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
Err(MultiphaseError::NoPlan)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn partition_motion_groups(windows: &[MultiphaseWindow]) -> Vec<Vec<usize>> {
|
pub(crate) fn partition_motion_groups(windows: &[MultiphaseWindow]) -> Vec<Vec<usize>> {
|
||||||
|
|
@ -228,33 +283,48 @@ fn validate_request(request: &MultiphaseRequest) -> Result<(), MultiphaseError>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plan_forward(request: &MultiphaseRequest) -> Option<MultiphasePlan> {
|
fn plan_forward(request: &MultiphaseRequest) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||||
|
let mut rejection = None;
|
||||||
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
||||||
if let Some(plan) = plan_axis_crossing_lanes(request, axis) {
|
match plan_axis_crossing_lanes(request, axis) {
|
||||||
return Some(plan);
|
Ok(plan) => return Ok(plan),
|
||||||
|
Err(MultiphasePlanFailure::NoPattern) => {}
|
||||||
|
Err(error) => {
|
||||||
|
rejection.get_or_insert(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plan_space_then_orthogonal_growth(request, PhaseAxis::Horizontal)
|
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
||||||
.or_else(|| plan_space_then_orthogonal_growth(request, PhaseAxis::Vertical))
|
match plan_space_then_orthogonal_growth(request, axis) {
|
||||||
|
Ok(plan) => return Ok(plan),
|
||||||
|
Err(MultiphasePlanFailure::NoPattern) => {}
|
||||||
|
Err(error) => {
|
||||||
|
rejection.get_or_insert(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rejection.unwrap_or(MultiphasePlanFailure::NoPattern))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plan_axis_crossing_lanes(
|
fn plan_axis_crossing_lanes(
|
||||||
request: &MultiphaseRequest,
|
request: &MultiphaseRequest,
|
||||||
axis: PhaseAxis,
|
axis: PhaseAxis,
|
||||||
) -> Option<MultiphasePlan> {
|
) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||||
if request.windows.len() != 2 {
|
if request.windows.len() != 2 {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
}
|
}
|
||||||
let orth_min = request
|
let orth_min = request
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|window| orth_start(window.from, axis))
|
.map(|window| orth_start(window.from, axis))
|
||||||
.min()?;
|
.min()
|
||||||
|
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||||
let orth_max = request
|
let orth_max = request
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|window| orth_end(window.from, axis))
|
.map(|window| orth_end(window.from, axis))
|
||||||
.max()?;
|
.max()
|
||||||
|
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||||
if request.windows.iter().any(|window| {
|
if request.windows.iter().any(|window| {
|
||||||
main_size(window.from, axis) != main_size(window.to, axis)
|
main_size(window.from, axis) != main_size(window.to, axis)
|
||||||
|| orth_start(window.from, axis) != orth_min
|
|| orth_start(window.from, axis) != orth_min
|
||||||
|
|
@ -263,11 +333,16 @@ fn plan_axis_crossing_lanes(
|
||||||
|| orth_end(window.to, axis) != orth_max
|
|| orth_end(window.to, axis) != orth_max
|
||||||
|| main_start(window.from, axis) == main_start(window.to, axis)
|
|| main_start(window.from, axis) == main_start(window.to, axis)
|
||||||
}) {
|
}) {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
}
|
}
|
||||||
let lane_size = (orth_max - orth_min) / request.windows.len() as i32;
|
let lane_size = (orth_max - orth_min) / request.windows.len() as i32;
|
||||||
if lane_size < sane_min_size(orth_max - orth_min) {
|
let required = sane_min_size(orth_max - orth_min);
|
||||||
return None;
|
if lane_size < required {
|
||||||
|
return Err(MultiphasePlanFailure::ShrinkBound {
|
||||||
|
axis: axis.other(),
|
||||||
|
available: lane_size,
|
||||||
|
required,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut windows = request.windows.clone();
|
let mut windows = request.windows.clone();
|
||||||
|
|
@ -275,7 +350,7 @@ fn plan_axis_crossing_lanes(
|
||||||
if windows.windows(2).any(|pair| {
|
if windows.windows(2).any(|pair| {
|
||||||
lane_index_for_direction(pair[0], axis) == lane_index_for_direction(pair[1], axis)
|
lane_index_for_direction(pair[0], axis) == lane_index_for_direction(pair[1], axis)
|
||||||
}) {
|
}) {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
}
|
}
|
||||||
let mut phase1 = vec![];
|
let mut phase1 = vec![];
|
||||||
let mut phase2 = vec![];
|
let mut phase2 = vec![];
|
||||||
|
|
@ -320,9 +395,9 @@ fn lane_index_for_direction(window: MultiphaseWindow, axis: PhaseAxis) -> Option
|
||||||
fn plan_space_then_orthogonal_growth(
|
fn plan_space_then_orthogonal_growth(
|
||||||
request: &MultiphaseRequest,
|
request: &MultiphaseRequest,
|
||||||
axis: PhaseAxis,
|
axis: PhaseAxis,
|
||||||
) -> Option<MultiphasePlan> {
|
) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||||
if request.windows.len() < 2 {
|
if request.windows.len() < 2 {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
}
|
}
|
||||||
let orth_axis = axis.other();
|
let orth_axis = axis.other();
|
||||||
let min_width = sane_min_size(request.bounds.width());
|
let min_width = sane_min_size(request.bounds.width());
|
||||||
|
|
@ -331,8 +406,19 @@ fn plan_space_then_orthogonal_growth(
|
||||||
let mut phase2 = vec![];
|
let mut phase2 = vec![];
|
||||||
let mut phase3 = vec![];
|
let mut phase3 = vec![];
|
||||||
for window in &request.windows {
|
for window in &request.windows {
|
||||||
if window.to.width() < min_width || window.to.height() < min_height {
|
if window.to.width() < min_width {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::ShrinkBound {
|
||||||
|
axis: PhaseAxis::Horizontal,
|
||||||
|
available: window.to.width(),
|
||||||
|
required: min_width,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if window.to.height() < min_height {
|
||||||
|
return Err(MultiphasePlanFailure::ShrinkBound {
|
||||||
|
axis: PhaseAxis::Vertical,
|
||||||
|
available: window.to.height(),
|
||||||
|
required: min_height,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let main_changes = main_start(window.from, axis) != main_start(window.to, axis)
|
let main_changes = main_start(window.from, axis) != main_start(window.to, axis)
|
||||||
|| main_end(window.from, axis) != main_end(window.to, axis);
|
|| main_end(window.from, axis) != main_end(window.to, axis);
|
||||||
|
|
@ -365,7 +451,7 @@ fn plan_space_then_orthogonal_growth(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if phase1.is_empty() || phase2.is_empty() || phase3.is_empty() {
|
if phase1.is_empty() || phase2.is_empty() || phase3.is_empty() {
|
||||||
return None;
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
}
|
}
|
||||||
build_validated_plan(
|
build_validated_plan(
|
||||||
request,
|
request,
|
||||||
|
|
@ -380,7 +466,7 @@ fn plan_space_then_orthogonal_growth(
|
||||||
fn build_validated_plan<const N: usize>(
|
fn build_validated_plan<const N: usize>(
|
||||||
request: &MultiphaseRequest,
|
request: &MultiphaseRequest,
|
||||||
phases: [(PhaseKind, PhaseAxis, Vec<MultiphaseStep>); N],
|
phases: [(PhaseKind, PhaseAxis, Vec<MultiphaseStep>); N],
|
||||||
) -> Option<MultiphasePlan> {
|
) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||||
let phases: Vec<_> = phases
|
let phases: Vec<_> = phases
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(kind, axis, steps)| {
|
.filter_map(|(kind, axis, steps)| {
|
||||||
|
|
@ -390,41 +476,58 @@ fn build_validated_plan<const N: usize>(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if phases.iter().any(|phase| {
|
for phase in &phases {
|
||||||
phase
|
for step in &phase.steps {
|
||||||
.steps
|
if classify_step(*step) != Some(phase.action) {
|
||||||
.iter()
|
return Err(MultiphasePlanFailure::InvalidPhaseStep {
|
||||||
.any(|step| classify_step(*step) != Some(phase.action))
|
action: phase.action,
|
||||||
}) {
|
node_id: step.node_id,
|
||||||
return None;
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let plan = MultiphasePlan { phases };
|
let plan = MultiphasePlan { phases };
|
||||||
validate_plan_continuous(request, &plan).then_some(plan)
|
validate_plan_continuous_diagnostic(request, &plan)
|
||||||
|
.map(|_| plan)
|
||||||
|
.map_err(MultiphasePlanFailure::Validation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_plan_continuous(request: &MultiphaseRequest, plan: &MultiphasePlan) -> bool {
|
fn validate_plan_continuous(request: &MultiphaseRequest, plan: &MultiphasePlan) -> bool {
|
||||||
|
validate_plan_continuous_diagnostic(request, plan).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_plan_continuous_diagnostic(
|
||||||
|
request: &MultiphaseRequest,
|
||||||
|
plan: &MultiphasePlan,
|
||||||
|
) -> Result<(), MultiphaseValidationError> {
|
||||||
let mut current: Vec<_> = request
|
let mut current: Vec<_> = request
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|window| (window.node_id, window.from))
|
.map(|window| (window.node_id, window.from))
|
||||||
.collect();
|
.collect();
|
||||||
if overlaps(current.iter().map(|(_, rect)| *rect)) {
|
for (phase_idx, phase) in plan.phases.iter().enumerate() {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for phase in &plan.phases {
|
|
||||||
for (idx, step) in phase.steps.iter().enumerate() {
|
for (idx, step) in phase.steps.iter().enumerate() {
|
||||||
if phase.steps[..idx]
|
if phase.steps[..idx]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|prev| prev.node_id == step.node_id)
|
.any(|prev| prev.node_id == step.node_id)
|
||||||
{
|
{
|
||||||
return false;
|
return Err(MultiphaseValidationError::DuplicatePhaseStep {
|
||||||
|
phase: phase_idx,
|
||||||
|
node_id: step.node_id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let Some((_, rect)) = current.iter().find(|(node_id, _)| *node_id == step.node_id)
|
let Some((_, rect)) = current.iter().find(|(node_id, _)| *node_id == step.node_id)
|
||||||
else {
|
else {
|
||||||
return false;
|
return Err(MultiphaseValidationError::UnknownPhaseStep {
|
||||||
|
phase: phase_idx,
|
||||||
|
node_id: step.node_id,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
if *rect != step.from {
|
if *rect != step.from {
|
||||||
return false;
|
return Err(MultiphaseValidationError::StaleStepStart {
|
||||||
|
phase: phase_idx,
|
||||||
|
node_id: step.node_id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let motions: Vec<_> = current
|
let motions: Vec<_> = current
|
||||||
|
|
@ -440,11 +543,16 @@ fn validate_plan_continuous(request: &MultiphaseRequest, plan: &MultiphasePlan)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
for (idx, motion) in motions.iter().enumerate() {
|
for (idx, motion) in motions.iter().enumerate() {
|
||||||
if motions[idx + 1..]
|
if let Some((other_idx, _)) = motions[idx + 1..]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|other| motions_overlap_during_phase(*motion, *other))
|
.enumerate()
|
||||||
|
.find(|(_, other)| motions_overlap_during_phase(*motion, **other))
|
||||||
{
|
{
|
||||||
return false;
|
return Err(MultiphaseValidationError::PhaseOverlap {
|
||||||
|
phase: phase_idx,
|
||||||
|
a: current[idx].0,
|
||||||
|
b: current[idx + 1 + other_idx].0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for step in &phase.steps {
|
for step in &phase.steps {
|
||||||
|
|
@ -454,16 +562,19 @@ fn validate_plan_continuous(request: &MultiphaseRequest, plan: &MultiphasePlan)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
*rect = step.to;
|
*rect = step.to;
|
||||||
}
|
}
|
||||||
if overlaps(current.iter().map(|(_, rect)| *rect)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
request.windows.iter().all(|window| {
|
for window in &request.windows {
|
||||||
current
|
if !current
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(node_id, _)| *node_id == window.node_id)
|
.find(|(node_id, _)| *node_id == window.node_id)
|
||||||
.is_some_and(|(_, rect)| *rect == window.to)
|
.is_some_and(|(_, rect)| *rect == window.to)
|
||||||
})
|
{
|
||||||
|
return Err(MultiphaseValidationError::FinalMismatch {
|
||||||
|
node_id: window.node_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
|
@ -1044,6 +1155,43 @@ mod tests {
|
||||||
hierarchy: Default::default(),
|
hierarchy: Default::default(),
|
||||||
}]);
|
}]);
|
||||||
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
||||||
|
assert_eq!(
|
||||||
|
plan_no_overlap_with_diagnostics(&req).unwrap_err(),
|
||||||
|
MultiphasePlanDiagnostic {
|
||||||
|
forward: MultiphasePlanFailure::NoPattern,
|
||||||
|
reverse: Some(MultiphasePlanFailure::NoPattern),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diagnostics_report_shrink_bound_rejections() {
|
||||||
|
let req = MultiphaseRequest {
|
||||||
|
bounds: rect(0, 0, 400, 100),
|
||||||
|
windows: vec![
|
||||||
|
MultiphaseWindow {
|
||||||
|
node_id: id(1),
|
||||||
|
from: rect(0, 0, 200, 100),
|
||||||
|
to: rect(0, 0, 10, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
|
},
|
||||||
|
MultiphaseWindow {
|
||||||
|
node_id: id(2),
|
||||||
|
from: rect(200, 0, 400, 100),
|
||||||
|
to: rect(10, 0, 400, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
plan_no_overlap_with_diagnostics(&req).unwrap_err().forward,
|
||||||
|
MultiphasePlanFailure::ShrinkBound {
|
||||||
|
axis: PhaseAxis::Horizontal,
|
||||||
|
available: 10,
|
||||||
|
required: 100,
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1115,7 +1263,14 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!validate_plan_continuous(&req, &plan));
|
assert_eq!(
|
||||||
|
validate_plan_continuous_diagnostic(&req, &plan),
|
||||||
|
Err(MultiphaseValidationError::PhaseOverlap {
|
||||||
|
phase: 0,
|
||||||
|
a: id(1),
|
||||||
|
b: id(2),
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1173,7 +1328,13 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!validate_plan_continuous(&req, &plan));
|
assert_eq!(
|
||||||
|
validate_plan_continuous_diagnostic(&req, &plan),
|
||||||
|
Err(MultiphaseValidationError::StaleStepStart {
|
||||||
|
phase: 0,
|
||||||
|
node_id: id(1),
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
17
src/state.rs
17
src/state.rs
|
|
@ -7,7 +7,7 @@ use {
|
||||||
expand_damage_rect,
|
expand_damage_rect,
|
||||||
multiphase::{
|
multiphase::{
|
||||||
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||||
partition_motion_groups, plan_no_overlap,
|
partition_motion_groups, plan_no_overlap_with_diagnostics,
|
||||||
},
|
},
|
||||||
spawn_in_start_rect,
|
spawn_in_start_rect,
|
||||||
},
|
},
|
||||||
|
|
@ -1661,11 +1661,20 @@ impl State {
|
||||||
for window in &request_windows[1..] {
|
for window in &request_windows[1..] {
|
||||||
bounds = bounds.union(window.from).union(window.to);
|
bounds = bounds.union(window.from).union(window.to);
|
||||||
}
|
}
|
||||||
let Ok(plan) = plan_no_overlap(&MultiphaseRequest {
|
let request = MultiphaseRequest {
|
||||||
bounds,
|
bounds,
|
||||||
windows: request_windows,
|
windows: request_windows,
|
||||||
}) else {
|
};
|
||||||
return false;
|
let plan = match plan_no_overlap_with_diagnostics(&request) {
|
||||||
|
Ok(plan) => plan,
|
||||||
|
Err(diagnostic) => {
|
||||||
|
log::debug!(
|
||||||
|
"falling back to linear layout animation for group {:?}: {:?}",
|
||||||
|
group,
|
||||||
|
diagnostic
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if plan.phases.is_empty() {
|
if plan.phases.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue