use crate::{ahash, epaint, Id, IdMap, Rect};
use epaint::{emath::TSTransform, ClippedShape, Shape};
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Order {
Background,
Middle,
Foreground,
Tooltip,
Debug,
}
impl Order {
const COUNT: usize = 5;
const ALL: [Self; Self::COUNT] = [
Self::Background,
Self::Middle,
Self::Foreground,
Self::Tooltip,
Self::Debug,
];
pub const TOP: Self = Self::Debug;
#[inline(always)]
pub fn allow_interaction(&self) -> bool {
match self {
Self::Background | Self::Middle | Self::Foreground | Self::Tooltip | Self::Debug => {
true
}
}
}
pub fn short_debug_format(&self) -> &'static str {
match self {
Self::Background => "backg",
Self::Middle => "middl",
Self::Foreground => "foreg",
Self::Tooltip => "toolt",
Self::Debug => "debug",
}
}
}
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct LayerId {
pub order: Order,
pub id: Id,
}
impl LayerId {
pub fn new(order: Order, id: Id) -> Self {
Self { order, id }
}
pub fn debug() -> Self {
Self {
order: Order::Debug,
id: Id::new("debug"),
}
}
pub fn background() -> Self {
Self {
order: Order::Background,
id: Id::new("background"),
}
}
#[inline(always)]
#[deprecated = "Use `Memory::allows_interaction` instead"]
pub fn allow_interaction(&self) -> bool {
self.order.allow_interaction()
}
pub fn short_debug_format(&self) -> String {
format!(
"{} {}",
self.order.short_debug_format(),
self.id.short_debug_format()
)
}
}
impl std::fmt::Debug for LayerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { order, id } = self;
write!(f, "LayerId {{ {order:?} {id:?} }}")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeIdx(pub usize);
#[derive(Clone, Default)]
pub struct PaintList(Vec<ClippedShape>);
impl PaintList {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn next_idx(&self) -> ShapeIdx {
ShapeIdx(self.0.len())
}
#[inline(always)]
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
let idx = self.next_idx();
self.0.push(ClippedShape { clip_rect, shape });
idx
}
pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I) {
self.0.extend(
shapes
.into_iter()
.map(|shape| ClippedShape { clip_rect, shape }),
);
}
#[inline(always)]
pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
if self.0.len() <= idx.0 {
#[cfg(feature = "log")]
log::warn!("Index {} is out of bounds for PaintList", idx.0);
return;
}
self.0[idx.0] = ClippedShape { clip_rect, shape };
}
#[inline(always)]
pub fn reset_shape(&mut self, idx: ShapeIdx) {
self.0[idx.0].shape = Shape::Noop;
}
pub fn mutate_shape(&mut self, idx: ShapeIdx, f: impl FnOnce(&mut ClippedShape)) {
self.0.get_mut(idx.0).map(f);
}
pub fn transform(&mut self, transform: TSTransform) {
for ClippedShape { clip_rect, shape } in &mut self.0 {
*clip_rect = transform.mul_rect(*clip_rect);
shape.transform(transform);
}
}
pub fn transform_range(&mut self, start: ShapeIdx, end: ShapeIdx, transform: TSTransform) {
for ClippedShape { clip_rect, shape } in &mut self.0[start.0..end.0] {
*clip_rect = transform.mul_rect(*clip_rect);
shape.transform(transform);
}
}
pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {
self.0.iter()
}
}
#[derive(Clone, Default)]
pub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);
impl GraphicLayers {
pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {
self.0[layer_id.order as usize]
.entry(layer_id.id)
.or_default()
}
pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {
self.0[layer_id.order as usize].get(&layer_id.id)
}
pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {
self.0[layer_id.order as usize].get_mut(&layer_id.id)
}
pub fn drain(
&mut self,
area_order: &[LayerId],
to_global: &ahash::HashMap<LayerId, TSTransform>,
) -> Vec<ClippedShape> {
profiling::function_scope!();
let mut all_shapes: Vec<_> = Default::default();
for &order in &Order::ALL {
let order_map = &mut self.0[order as usize];
order_map.retain(|_, list| !list.is_empty());
for layer_id in area_order {
if layer_id.order == order {
if let Some(list) = order_map.get_mut(&layer_id.id) {
if let Some(to_global) = to_global.get(layer_id) {
for clipped_shape in &mut list.0 {
clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect;
clipped_shape.shape.transform(*to_global);
}
}
all_shapes.append(&mut list.0);
}
}
}
for (id, list) in order_map {
let layer_id = LayerId::new(order, *id);
if let Some(to_global) = to_global.get(&layer_id) {
for clipped_shape in &mut list.0 {
clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect;
clipped_shape.shape.transform(*to_global);
}
}
all_shapes.append(&mut list.0);
}
}
all_shapes
}
}