pixelset/cache/mod.rs
1#[cfg(feature = "rand")]
2use rand::seq::IteratorRandom;
3#[cfg(feature = "rand")]
4use crate::cache::grow::grow_pixel_into_box;
5
6use crate::{PixelSet, shapes::{Rectangle, Shape}};
7
8pub mod grow;
9
10/// A high-performance spatial cache for `PixelSet` data, organized as a
11/// collection of `Rectangle` containers.
12///
13/// ## Overview
14///
15/// `PixelCache` is designed to store pixels in **spatially contiguous boxes**
16/// (`Rectangle`), making it easily to compactly store continuous sets of
17/// pixels.
18///
19/// A `PixelCache` can be very quickly iterated over, and converted to a
20/// `PixelSet`, but cannot be directly operated on, and generating one
21/// may take longer.
22#[derive(Clone)]
23pub struct PixelCache {
24 pub boxes: Vec<Rectangle>
25}
26
27impl PixelCache {
28 /// Creates a new empty `PixelCache`.
29 pub fn new() -> Self {
30 Self { boxes: vec![] }
31 }
32
33 /// Combines all cached `Rectangle` containers into a single, sorted `PixelSet`.
34 pub fn set(&self) -> PixelSet {
35 let mut pixels = Vec::with_capacity(self.len());
36 for rectangle in &self.boxes {
37 pixels.extend(rectangle.iter_pixels());
38 }
39
40 PixelSet::new(pixels)
41 }
42
43 /// Builds a `PixelCache` by repeatedly selecting **random pixels** from a
44 /// given `PixelSet` and expanding each into a `Rectangle`.
45 ///
46 /// Creates a far more efficient and compact set than the original
47 /// `PixelSet`.
48 #[cfg(feature = "rand")]
49 pub fn generate_from_set(set: &PixelSet) -> Self {
50 let mut set = set.clone();
51 let mut rng = rand::rng();
52
53 let mut rectangles = vec![];
54 while set.len() > 0 {
55 let &pixel = set.iter().choose(&mut rng).unwrap();
56 let rectangle = grow_pixel_into_box(pixel, &set);
57 set = set.without(&rectangle.set());
58
59 rectangles.push(rectangle);
60 }
61
62 Self { boxes: rectangles }
63 }
64
65 /// Returns the total number of pixels contained across all cached boxes.
66 pub fn len(&self) -> usize {
67 self.boxes.iter()
68 .map(|rectangle| rectangle.len())
69 .sum()
70 }
71
72 /// Returns `true` if the cache contains no pixels.
73 pub fn is_empty(&self) -> bool {
74 self.len() == 0
75 }
76}