use euclid::point2;
use smallvec::SmallVec;
use api::units::*;
#[derive(Debug, PartialEq)]
pub struct Item {
pub rectangle: DeviceBox2D,
pub key: usize,
}
pub struct FrontToBackBuilder {
opaque_items: Vec<Item>,
alpha_items: Vec<Item>,
}
impl FrontToBackBuilder {
pub fn with_capacity(opaque: usize, alpha: usize) -> Self {
FrontToBackBuilder {
opaque_items: Vec::with_capacity(opaque),
alpha_items: Vec::with_capacity(alpha),
}
}
pub fn add(&mut self, rect: &DeviceBox2D, is_opaque: bool, key: usize) -> bool {
let mut fragments: SmallVec<[DeviceBox2D; 16]> = SmallVec::new();
fragments.push(*rect);
for item in &self.opaque_items {
if fragments.is_empty() {
break;
}
if item.rectangle.intersects(rect) {
apply_occluder(&item.rectangle, &mut fragments);
}
}
let list = if is_opaque {
&mut self.opaque_items
} else {
&mut self.alpha_items
};
for rect in &fragments {
list.push(Item {
rectangle: *rect,
key,
});
}
!fragments.is_empty()
}
pub fn test(&self, rect: &DeviceBox2D) -> bool {
let mut fragments: SmallVec<[DeviceBox2D; 16]> = SmallVec::new();
fragments.push(*rect);
for item in &self.opaque_items {
if item.rectangle.intersects(rect) {
apply_occluder(&item.rectangle, &mut fragments);
}
}
!fragments.is_empty()
}
pub fn opaque_items(&self) -> &[Item] {
&self.opaque_items
}
pub fn alpha_items(&self) -> &[Item] {
&self.alpha_items
}
}
fn apply_occluder(occluder: &DeviceBox2D, rects: &mut SmallVec<[DeviceBox2D; 16]>) {
let mut i = rects.len() - 1;
loop {
let r = rects[i];
if r.intersects(occluder) {
let top = r.min.y < occluder.min.y;
let bottom = r.max.y > occluder.max.y;
let left = r.min.x < occluder.min.x;
let right = r.max.x > occluder.max.x;
if top {
rects.push(DeviceBox2D {
min: r.min,
max: point2(r.max.x, occluder.min.y),
});
}
if bottom {
rects.push(DeviceBox2D {
min: point2(r.min.x, occluder.max.y),
max: r.max,
});
}
if left {
let min_y = r.min.y.max(occluder.min.y);
let max_y = r.max.y.min(occluder.max.y);
rects.push(DeviceBox2D {
min: point2(r.min.x, min_y),
max: point2(occluder.min.x, max_y),
});
}
if right {
let min_y = r.min.y.max(occluder.min.y);
let max_y = r.max.y.min(occluder.max.y);
rects.push(DeviceBox2D {
min: point2(occluder.max.x, min_y),
max: point2(r.max.x, max_y),
});
}
if i == rects.len() {
rects.pop();
} else {
rects.swap_remove(i);
}
}
if i == 0 {
break;
}
i -= 1;
}
}