use crate::registry::{PlaneCapabilities, PlaneRegistry};
#[must_use]
pub fn zpos_fixed(plane: &PlaneCapabilities) -> bool {
matches!((plane.zpos_min, plane.zpos_max), (Some(min), Some(max)) if min == max)
}
#[must_use]
pub fn effective_zpos(plane: &PlaneCapabilities, layer_zpos: Option<u64>) -> Option<u64> {
if zpos_fixed(plane) {
plane.zpos_min
} else {
layer_zpos
}
}
#[must_use]
pub fn stacking_consistent(
pa: &PlaneCapabilities,
za: Option<u64>,
pb: &PlaneCapabilities,
zb: Option<u64>,
) -> bool {
let (Some(za), Some(zb)) = (za, zb) else {
return true;
};
if za == zb {
return true;
}
let (Some(ea), Some(eb)) = (effective_zpos(pa, Some(za)), effective_zpos(pb, Some(zb))) else {
return true;
};
ea != eb && (za < zb) == (ea < eb)
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct StackEntry<'a> {
pub plane: &'a PlaneCapabilities,
pub requested: Option<u64>,
pub written: Option<u64>,
pub below_ties: bool,
}
impl<'a> StackEntry<'a> {
#[must_use]
pub const fn new(plane: &'a PlaneCapabilities, requested: Option<u64>) -> Self {
Self {
plane,
requested,
written: None,
below_ties: false,
}
}
}
pub fn stack_zpos(entries: &mut [StackEntry<'_>]) -> bool {
struct Rankable {
index: usize,
requested: u64,
after_ties: bool,
plane_id: u32,
min: u64,
max: u64,
fixed: bool,
}
let mut order = Vec::with_capacity(entries.len());
for (index, entry) in entries.iter_mut().enumerate() {
entry.written = entry.requested;
if let (Some(requested), Some(min), Some(max)) =
(entry.requested, entry.plane.zpos_min, entry.plane.zpos_max)
{
order.push(Rankable {
index,
requested,
after_ties: !entry.below_ties,
plane_id: entry.plane.id,
min,
max,
fixed: min == max,
});
}
}
order.sort_by_key(|rank| (rank.requested, rank.after_ties, rank.plane_id));
let mut values = Vec::with_capacity(order.len());
let mut previous: Option<u64> = None;
for rank in &order {
let value = match previous {
Some(previous) if rank.fixed && rank.min <= previous => return false,
Some(previous) if !rank.fixed => previous.saturating_add(1).max(rank.min),
_ => rank.min,
};
if value > rank.max {
return false;
}
values.push(value);
previous = Some(value);
}
for (rank, value) in order.iter().zip(values) {
entries[rank.index].written = Some(value);
}
true
}
#[must_use]
pub fn stacked_zpos(registry: &PlaneRegistry, armed: &[(u32, Option<u64>)]) -> Vec<(u32, u64)> {
let mut entries = Vec::with_capacity(armed.len());
let mut ids = Vec::with_capacity(armed.len());
for &(plane_id, requested) in armed {
if let Some(plane) = registry.by_id(plane_id) {
entries.push(StackEntry::new(plane, requested));
ids.push(plane_id);
}
}
let _ = stack_zpos(&mut entries);
ids.into_iter()
.zip(entries)
.filter_map(|(id, entry)| entry.written.map(|value| (id, value)))
.collect()
}
#[must_use]
pub fn stacked_zpos_beneath(
registry: &PlaneRegistry,
armed: &[(u32, Option<u64>)],
beneath: u32,
) -> Option<Vec<(u32, u64)>> {
let mut entries = Vec::with_capacity(armed.len());
let mut ids = Vec::with_capacity(armed.len());
for &(plane_id, requested) in armed {
if let Some(plane) = registry.by_id(plane_id) {
let mut entry = StackEntry::new(plane, requested);
entry.below_ties = plane_id == beneath;
entries.push(entry);
ids.push(plane_id);
}
}
stack_zpos(&mut entries).then(|| {
ids.into_iter()
.zip(entries)
.filter_map(|(id, entry)| entry.written.map(|value| (id, value)))
.collect()
})
}