use std::collections::BTreeSet;
use crate::Point;
use super::ShapeId;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SegId(u32);
impl SegId {
pub(super) const fn new(index: usize) -> Self {
Self(index as u32)
}
pub(super) const fn index(self) -> usize {
self.0 as usize
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Clone, Debug)]
pub struct Segment {
pub(super) parent: ShapeId,
pub(super) start: f64,
pub(super) point: Point,
pub(super) next: SegId,
pub(super) prev: SegId,
pub(super) overlapping: BTreeSet<ShapeId>,
pub(super) force_inactive: bool,
}
impl Segment {
#[cfg(test)]
#[must_use]
pub const fn parent(&self) -> ShapeId {
self.parent
}
#[must_use]
pub const fn start(&self) -> f64 {
self.start
}
#[must_use]
pub const fn point(&self) -> Point {
self.point
}
#[must_use]
pub const fn next(&self) -> SegId {
self.next
}
#[must_use]
pub const fn prev(&self) -> SegId {
self.prev
}
#[cfg(test)]
pub fn overlapping(&self) -> impl Iterator<Item = ShapeId> + '_ {
self.overlapping.iter().copied()
}
#[must_use]
pub fn is_active(&self) -> bool {
self.overlapping.is_empty() && !self.force_inactive
}
}
#[derive(Clone, Debug, Default)]
pub(super) struct SegmentArena {
slots: Vec<Option<Segment>>,
free: Vec<SegId>,
live: usize,
}
impl SegmentArena {
pub(super) const fn new() -> Self {
Self {
slots: Vec::new(),
free: Vec::new(),
live: 0,
}
}
pub(super) const fn len(&self) -> usize {
self.live
}
pub(super) fn get(&self, id: SegId) -> Option<&Segment> {
self.slots.get(id.index())?.as_ref()
}
pub(super) fn get_mut(&mut self, id: SegId) -> Option<&mut Segment> {
self.slots.get_mut(id.index())?.as_mut()
}
pub(super) fn insert(&mut self, segment: Segment) -> SegId {
let id = self.free.pop().unwrap_or_else(|| {
let fresh = SegId::new(self.slots.len());
self.slots.push(None);
fresh
});
if let Some(slot) = self.slots.get_mut(id.index()) {
debug_assert!(slot.is_none(), "arena handed out a slot that was live");
*slot = Some(segment);
self.live += 1;
}
id
}
pub(super) fn remove(&mut self, id: SegId) -> Option<Segment> {
let taken = self.slots.get_mut(id.index())?.take();
if taken.is_some() {
self.live -= 1;
self.free.push(id);
}
taken
}
pub(super) fn iter(&self) -> impl Iterator<Item = (SegId, &Segment)> {
self.slots
.iter()
.enumerate()
.filter_map(|(index, slot)| slot.as_ref().map(|seg| (SegId::new(index), seg)))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::collections::BTreeSet;
use super::{SegId, Segment, SegmentArena};
use crate::Point;
use crate::clipping::ShapeId;
fn segment() -> Segment {
Segment {
parent: ShapeId::new(0),
start: 0.0,
point: Point::new(1.0, 2.0),
next: SegId::new(0),
prev: SegId::new(0),
overlapping: BTreeSet::new(),
force_inactive: false,
}
}
#[test]
fn a_freed_slot_is_handed_out_again() {
let mut arena = SegmentArena::new();
let first = arena.insert(segment());
let second = arena.insert(segment());
assert_eq!(arena.len(), 2);
arena.remove(first);
assert_eq!(arena.len(), 1);
assert!(arena.get(first).is_none());
let third = arena.insert(segment());
assert_eq!(third, first, "the freed slot should be reused");
assert_eq!(arena.len(), 2);
assert!(arena.get(second).is_some());
}
#[test]
fn removing_twice_is_harmless() {
let mut arena = SegmentArena::new();
let id = arena.insert(segment());
assert!(arena.remove(id).is_some());
assert!(arena.remove(id).is_none());
assert_eq!(arena.len(), 0);
}
#[test]
fn iteration_yields_only_live_segments() {
let mut arena = SegmentArena::new();
let a = arena.insert(segment());
let b = arena.insert(segment());
let c = arena.insert(segment());
arena.remove(b);
let live: Vec<SegId> = arena.iter().map(|(id, _)| id).collect();
assert_eq!(live, vec![a, c]);
}
#[test]
fn a_segment_is_active_only_when_nothing_covers_it() {
let mut seg = segment();
assert!(seg.is_active());
seg.overlapping.insert(ShapeId::new(7));
assert!(!seg.is_active());
seg.overlapping.clear();
seg.force_inactive = true;
assert!(!seg.is_active());
}
}