pixel_map/pixel_map.rs
1#[cfg(feature = "serialize")]
2use serde::{Deserialize, Serialize};
3
4use super::{
5 ICircle, ILine, IsoLine, PNode, RayCast, RayCastContext, RayCastQuery, RayCastResult, Region,
6};
7use crate::isocontour::FragmentAccumulator;
8use crate::{
9 exclusive_urect, iline, to_cropped_urect, urect_points, CellFill, NeighborOrientation,
10 NodePath, RotatedIRect,
11};
12use bevy_math::{ivec2, IVec2, URect, UVec2};
13use fxhash::{FxBuildHasher, FxHasher};
14use num_traits::{NumCast, Unsigned, Zero};
15use std::collections::{HashMap, HashSet};
16use std::fmt::{Debug, Formatter};
17use std::hash::{BuildHasher, BuildHasherDefault};
18
19/// A two-dimensional map of pixels implemented by an MX quadtree.
20/// The coordinate origin is at the bottom left.
21///
22/// # Type Parameters
23///
24/// - `T`: The type of pixel data. By default, a `bool`, to denote the pixel is on or off.
25/// A more useful type could be a `Color`.
26/// - `U`: The unsigned integer type of the coordinates used to index the pixels, typically `u16` (default), or `u32`.
27#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
28#[derive(Clone, PartialEq)]
29pub struct PixelMap<T: Copy + PartialEq = bool, U: Unsigned + NumCast + Copy + Debug = u16> {
30 pub(crate) root: PNode<T, U>,
31 pub(crate) map_rect: URect,
32 pub(crate) pixel_size: u8,
33}
34
35impl<T: Copy + PartialEq, U: Unsigned + NumCast + Copy + Debug> PixelMap<T, U> {
36 /// Create a new [PixelMap].
37 ///
38 /// # Parameters
39 ///
40 /// - `dimensions`: The size of this [PixelMap].
41 /// - `value`: The initial value of all pixels in this [PixelMap].
42 /// - `pixel_size`: The pixel size of this [PixelMap] that is considered the smallest divisible unit.
43 /// Must be a power of two.
44 ///
45 /// # Panics
46 ///
47 /// If `dimensions` size is not a multiple of pixel size on each axis.
48 /// If `pixel_size` is not a power of two.
49 #[inline]
50 #[must_use]
51 pub fn new(dimensions: &UVec2, value: T, pixel_size: u8) -> Self {
52 assert!(
53 dimensions.x % pixel_size as u32 == 0 && dimensions.y % pixel_size as u32 == 0,
54 "dimensions must be a multiple of pixel_size on each axis"
55 );
56 assert!(
57 pixel_size.is_power_of_two(),
58 "pixel_size must be a power of 2"
59 );
60 let region_size = next_pow2(dimensions.x.max(dimensions.y));
61 let size = U::from(region_size).unwrap();
62 let zero = U::from(0).unwrap();
63 let region = Region::new(zero, zero, size);
64 Self {
65 root: PNode::new(region, value, true),
66 map_rect: URect::from_corners(UVec2::ZERO, *dimensions),
67 pixel_size,
68 }
69 }
70
71 /// Obtain the dimensions of this [PixelMap].
72 #[inline]
73 #[must_use]
74 pub fn map_size(&self) -> UVec2 {
75 self.map_rect.size()
76 }
77
78 /// Obtain the dimensions of this [PixelMap] as a rectangle.
79 #[inline]
80 #[must_use]
81 pub fn map_rect(&self) -> URect {
82 self.map_rect
83 }
84
85 /// Obtain the pixel size of this [PixelMap]. When a node's region is of this size, it cannot
86 /// be subdivided further.
87 #[inline]
88 #[must_use]
89 pub fn pixel_size(&self) -> u8 {
90 self.pixel_size
91 }
92
93 /// Obtain the region that this [PixelMap]'s quadtree root node covers.
94 /// This value differs from `map_size` in that it the nearest power of two larger
95 /// than the map size, and it is square.
96 #[inline]
97 #[must_use]
98 pub fn region(&self) -> &Region<U> {
99 self.root.region()
100 }
101
102 /// Discard any existing pixel data and set the root node's value to that provided.
103 ///
104 /// # Parameters
105 ///
106 /// - `value`: The value to assign to the root node.
107 #[inline]
108 pub fn clear(&mut self, value: T) {
109 self.root.set_value(value);
110 }
111
112 /// Determine if this [PixelMap] is empty, which means that it has no pixel data.
113 #[inline]
114 pub fn empty(&self) -> bool {
115 self.root.is_leaf()
116 }
117
118 /// Determine if the given point is within the [PixelMap::map_size] bounds.
119 #[inline]
120 #[must_use]
121 pub fn contains(&self, point: UVec2) -> bool {
122 self.map_rect.contains(point)
123 }
124
125 /// Get the value of the pixel at the given coordinates. If the coordinates are outside the
126 /// region covered by this [PixelMap], None is returned.
127 ///
128 /// # Parameters
129 ///
130 /// - `point`: The coordinates of the pixel for which to retrieve the associated value.
131 #[inline]
132 #[must_use]
133 pub fn get_pixel<P>(&self, point: P) -> Option<&T>
134 where
135 P: Into<UVec2>,
136 {
137 let point = point.into();
138 if self.contains(point) {
139 Some(self.root.find_node(point).value())
140 } else {
141 None
142 }
143 }
144
145 /// Get the node that represents the pixel at the given coordinates. If the coordinates
146 /// are outside the region covered by this [PixelMap], None is returned.
147 ///
148 /// # Parameters
149 ///
150 /// - `point`: The coordinates of the pixel for which to retrieve the representing node.
151 #[inline]
152 #[must_use]
153 pub fn find_node<P>(&self, point: P) -> Option<&PNode<T, U>>
154 where
155 P: Into<UVec2>,
156 {
157 let point = point.into();
158 if self.contains(point) {
159 Some(self.root.find_node(point))
160 } else {
161 None
162 }
163 }
164
165 /// Get the path to the node that stores the pixel at the given point.
166 ///
167 /// # Parameters
168 ///
169 /// - `point`: The coordinates of the pixel for which to retrieve the node path.
170 ///
171 /// # Returns
172 ///
173 /// If the coordinates are outside the region covered by this [PixelMap], `None` is returned.
174 #[inline]
175 #[must_use]
176 pub fn get_path<P>(&self, point: P) -> Option<NodePath>
177 where
178 P: Into<UVec2>,
179 {
180 let point = point.into();
181 if self.contains(point) {
182 let (_, path) = self.root.node_path(point);
183 Some(path)
184 } else {
185 None
186 }
187 }
188
189 /// Set the value of the pixel at the given coordinates.
190 ///
191 /// # Parameters
192 ///
193 /// - `point`: The coordinates of the pixel for which to set the associated value.
194 ///
195 /// # Returns
196 ///
197 /// If the coordinates are outside the [PixelMap::map_rect], `false` is returned.
198 /// Otherwise, `true` is returned.
199 #[inline]
200 pub fn set_pixel<P>(&mut self, point: P, value: T) -> bool
201 where
202 P: Into<UVec2>,
203 {
204 let point = point.into();
205 if self.contains(point) {
206 self.root.set_pixel(point, self.pixel_size, value);
207 true
208 } else {
209 false
210 }
211 }
212
213 /// Set the value of all pixel coordinates yielded by the given iterator.
214 ///
215 /// # Parameters
216 ///
217 /// - `points`: An iterator that yields pixel coordinates.
218 ///
219 /// # Returns
220 ///
221 /// If any of the coordinates are inside the [PixelMap::map_rect],
222 /// `true` is returned, `false` otherwise.
223 #[inline]
224 pub fn set_pixels<I>(&mut self, points: I, value: T) -> bool
225 where
226 I: Iterator<Item = UVec2>,
227 {
228 let mut changed = false;
229 for point in points {
230 if self.set_pixel(point, value) {
231 changed = true;
232 }
233 }
234 changed
235 }
236
237 /// Set the value of the pixels within the given rectangle.
238 ///
239 /// # Parameters
240 ///
241 /// - `rect`: The rectangle in which pixels will be set to associated value.
242 /// - `value`: The value to assign to the pixels within the given rectangle.
243 ///
244 /// # Returns
245 ///
246 /// If the rectangle overlaps the [PixelMap::map_rect], `true` is returned. Otherwise, `false` is returned.
247 #[inline]
248 pub fn draw_rect(&mut self, rect: &URect, value: T) -> bool {
249 let rect = rect.intersect(self.map_rect());
250 if rect.is_empty() {
251 return false;
252 }
253 self.root.draw_rect(&rect, self.pixel_size, value);
254 true
255 }
256
257 /// Set the value of the pixels within the given rotated rectangle.
258 ///
259 /// # Parameters
260 ///
261 /// - `rrect`: The rotated rectangle in which pixels will be set to associated value.
262 /// - `value`: The value to assign to the pixels within the given rectangle.
263 ///
264 /// # Returns
265 ///
266 /// If the rectangle overlaps the [PixelMap::map_rect], `true` is returned. Otherwise, `false` is returned.
267 #[inline]
268 pub fn draw_rotated_rect(&mut self, rrect: &RotatedIRect, value: T) -> bool {
269 if rrect.rotation.is_zero() {
270 return self.draw_rect(&to_cropped_urect(&rrect.rect), value);
271 }
272 let rect = rrect.aabb().intersect(self.map_rect().as_irect());
273 if rect.is_empty() {
274 return false;
275 }
276 let inner_rect = to_cropped_urect(&rrect.inner_rect());
277 self.root.draw_rect(&inner_rect, self.pixel_size, value);
278 let inner_rect = exclusive_urect(&inner_rect);
279 for point in rrect.unsigned_pixels() {
280 if inner_rect.contains(point) {
281 continue;
282 }
283 self.set_pixel(point, value);
284 }
285 true
286 }
287
288 /// Set the value of the pixels within the given circle.
289 ///
290 /// # Parameters
291 ///
292 /// - `circle`: The circle in which pixels will be set to associated value.
293 /// - `value`: The value to assign to the pixels within the given circle.
294 ///
295 /// # Returns
296 ///
297 /// If the circle's aabb does not overlap
298 /// the region covered by this [PixelMap], false is returned. Otherwise, true is returned.
299 #[inline]
300 pub fn draw_circle(&mut self, circle: &ICircle, value: T) -> bool {
301 let aabb = to_cropped_urect(&circle.aabb());
302 let rect = aabb.intersect(self.map_rect());
303 if rect.is_empty() {
304 return false;
305 }
306 // Implementation note: Despite the aabb check, this still allows drawing circle pixels
307 // beyond the map bounds, within the quadtree region space. Fix me.
308 self.root.draw_circle(circle, self.pixel_size, value);
309 true
310 }
311
312 /// Visit all leaf nodes in this [PixelMap] in pre-order.
313 ///
314 /// # Parameters
315 ///
316 /// - `visitor`: A closure that takes a reference to a leaf node as its only parameter.
317 ///
318 /// # Returns
319 ///
320 /// The number of nodes traversed.
321 #[inline]
322 pub fn visit<F>(&self, mut visitor: F) -> u32
323 where
324 F: FnMut(&PNode<T, U>, &URect),
325 {
326 let mut traversed = 0u32;
327 self.root
328 .visit_leaves_in_rect(&self.map_rect(), &mut visitor, &mut traversed);
329 traversed
330 }
331
332 /// Visit all leaf nodes in this [PixelMap] that overlap with the given rectangle.
333 ///
334 /// # Parameters
335 ///
336 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
337 /// - `visitor`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
338 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
339 ///
340 /// # Returns
341 ///
342 /// The number of nodes traversed.
343 #[inline]
344 pub fn visit_in_rect<F>(&self, rect: &URect, mut visitor: F) -> u32
345 where
346 F: FnMut(&PNode<T, U>, &URect),
347 {
348 let rect = rect.intersect(self.map_rect());
349 if rect.is_empty() {
350 return 0;
351 }
352 let mut traversed = 0u32;
353 self.root
354 .visit_leaves_in_rect(&rect, &mut visitor, &mut traversed);
355 traversed
356 }
357
358 /// Visit all nodes in this [PixelMap] that overlap with the given rectangle, controlling
359 /// navigation with the visitor return value.
360 ///
361 /// # Parameters
362 ///
363 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
364 /// - `visitor`: A closure that takes a reference to a node, and a reference to a
365 /// rectangle as parameters. This rectangle represents the intersection of the node's
366 /// region and the `rect` parameter supplied to this method. It returns a [CellFill]
367 /// that denotes which child nodes should be visited.
368 ///
369 /// # Returns
370 ///
371 /// The number of nodes traversed.
372 #[inline]
373 pub fn visit_nodes_in_rect<F>(&self, rect: &URect, mut visitor: F) -> u32
374 where
375 F: FnMut(&PNode<T, U>, &URect) -> CellFill,
376 {
377 let rect = rect.intersect(self.map_rect());
378 if rect.is_empty() {
379 return 0;
380 }
381 let mut traversed = 0u32;
382 self.root
383 .visit_nodes_in_rect(&rect, &mut visitor, &mut traversed);
384 traversed
385 }
386
387 /// Determine if any of the leaf nodes within the bounds of the given rectangle match the predicate.
388 /// Node visitation short-circuits upon the first match.
389 ///
390 /// # Parameters
391 ///
392 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
393 /// - `f`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
394 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
395 /// It returns `true` if the node matches the predicate, or `false` otherwise.
396 ///
397 /// # Returns
398 ///
399 /// `Some(true)` if any of the leaf nodes within the bounds of `rect` match the
400 /// predicate. Or `Some(false)` if no nodes within `rect` match the predicate.
401 /// `None` if `rect` does not overlap the region covered by this [PixelMap].
402 #[inline]
403 #[must_use]
404 pub fn any_in_rect<F>(&self, rect: &URect, mut f: F) -> Option<bool>
405 where
406 F: FnMut(&PNode<T, U>, &URect) -> bool,
407 {
408 let rect = rect.intersect(self.map_rect());
409 if rect.is_empty() {
410 return None;
411 }
412 self.root.any_leaves_in_rect(&rect, &mut f)
413 }
414
415 /// Determine if all the leaf nodes within the bounds of the given rectangle match the predicate.
416 /// Node visitation short-circuits upon the first match.
417 ///
418 /// # Parameters
419 ///
420 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
421 /// - `f`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
422 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
423 /// It returns `true` if the node matches the predicate, or `false` otherwise.
424 ///
425 /// # Returns
426 ///
427 /// `Some(true)` if all of the leaf nodes within the bounds of `rect` match the
428 /// predicate. Or `Some(false)` if none or some of the nodes within `rect` match the predicate.
429 /// `None` if `rect` does not overlap the region covered by this [PixelMap].
430 #[inline]
431 #[must_use]
432 pub fn all_in_rect<F>(&self, rect: &URect, mut f: F) -> Option<bool>
433 where
434 F: FnMut(&PNode<T, U>, &URect) -> bool,
435 {
436 let rect = rect.intersect(self.map_rect());
437 if rect.is_empty() {
438 return None;
439 }
440 self.root.all_leaves_in_rect(&rect, &mut f)
441 }
442
443 /// Visit all leaf nodes in this [PixelMap] that are marked as dirty. This is useful for examining
444 /// only leaf nodes that have changed (became dirty), and to limit time spent traversing
445 /// the quadtree. Dirty status is not changed.
446 ///
447 /// # Parameters
448 ///
449 /// - `visitor`: A closure that takes a reference to a leaf node as its only parameter.
450 ///
451 /// # Returns
452 ///
453 /// The number of nodes traversed.
454 #[inline]
455 pub fn visit_dirty<F>(&self, mut visitor: F) -> u32
456 where
457 F: FnMut(&PNode<T, U>, &URect),
458 {
459 let mut traversed = 0u32;
460 if self.root.dirty() {
461 self.root
462 .visit_dirty_leaves_in_rect(&self.map_rect(), &mut visitor, &mut traversed);
463 }
464 traversed
465 }
466
467 /// Visit dirty leaf nodes in this [PixelMap] that overlap with the given rectangle.
468 /// This is useful for examining only leaf nodes that have changed (became dirty), and to
469 /// limit time spent traversing the quadtree. Dirty status is not changed.
470 ///
471 /// # Parameters
472 ///
473 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
474 /// - `visitor`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
475 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
476 ///
477 /// # Returns
478 ///
479 /// The number of nodes traversed.
480 #[inline]
481 pub fn visit_dirty_in_rect<F>(&self, rect: &URect, mut visitor: F) -> u32
482 where
483 F: FnMut(&PNode<T, U>, &URect),
484 {
485 let rect = rect.intersect(self.map_rect());
486 if rect.is_empty() {
487 return 0;
488 }
489 let mut traversed = 0u32;
490 if self.root.dirty() {
491 self.root
492 .visit_dirty_leaves_in_rect(&rect, &mut visitor, &mut traversed);
493 }
494 traversed
495 }
496
497 /// Determine if the root node is marked as dirty, which indicates that at least one leaf node
498 /// has changed (became dirty).
499 #[inline]
500 pub fn dirty(&self) -> bool {
501 self.root.dirty()
502 }
503
504 /// Visit all leaf nodes in this [PixelMap] that are marked as dirty, and consume
505 /// their dirty status (by modifying their dirty state to be `false`). This is useful for operating
506 /// only on leaf nodes that have changed (became dirty), and to limit time spent traversing
507 /// the quadtree.
508 ///
509 /// # Parameters
510 ///
511 /// - `visitor`: A closure that takes a reference to a leaf node as its only parameter.
512 ///
513 /// # Returns
514 ///
515 /// The number of nodes traversed.
516 #[inline]
517 pub fn drain_dirty<F>(&mut self, mut visitor: F) -> usize
518 where
519 F: FnMut(&PNode<T, U>),
520 {
521 let mut traversed = 0;
522 if self.root.dirty() {
523 self.root.drain_dirty_leaves(&mut visitor, &mut traversed);
524 }
525 traversed
526 }
527
528 /// Clear the dirty status of the root of this [PixelMap], according to a shallow or deep strategy.
529 ///
530 /// # Shallow Clear
531 ///
532 /// If dirty state is cleared in a shallow manner, the root node is marked clean, and
533 /// the dirty state at any further depth is retained. Subsequent calls to other methods that
534 /// navigate dirty nodes will not traverse any nodes, as none that are dirty are reachable
535 /// (because the root node is no longer dirty).
536 /// But, if branch `A` was dirty, [Self::clear_dirty] is called, and then branch `B` becomes dirty,
537 /// both `A` and `B` will be traversed by [Self::visit_dirty()] or [Self::drain_dirty()].
538 ///
539 /// # Deep Clear
540 ///
541 /// A deep clear traverses all dirty nodes and marks them as clean.
542 ///
543 /// # Parameters
544 ///
545 /// - `deep`: If `true`, clear the dirty status of all nodes in this [PixelMap], otherwise
546 /// clear the dirty status of just the root node.
547 #[inline]
548 pub fn clear_dirty(&mut self, deep: bool) {
549 if deep {
550 self.drain_dirty(|_| {});
551 } else {
552 self.root.clear_dirty();
553 }
554 }
555
556 /// Obtain the points of node region corners that overlap with the given rectangle, and match
557 /// the given predicate. Calls #[Self::collect_points] internally, but takes a guess at a
558 /// reasonable capacity for the resulting HashSet.
559 ///
560 /// # Parameters
561 ///
562 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
563 /// - `offset`: An offset to apply to returned points.
564 /// - `predicate`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
565 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
566 /// It returns `true` if the node matches the predicate, or `false` otherwise.
567 pub fn points<F>(
568 &self,
569 rect: &URect,
570 offset: IVec2,
571 predicate: F,
572 ) -> HashSet<IVec2, BuildHasherDefault<FxHasher>>
573 where
574 F: FnMut(&PNode<T, U>, &URect) -> bool,
575 {
576 let area = rect.width() * rect.height();
577 let mut result =
578 HashSet::with_capacity_and_hasher(area as usize / 4, FxBuildHasher::default());
579 self.collect_points(rect, offset, predicate, &mut result);
580 result
581 }
582
583 /// Collect the points of node region corners that overlap with the given rectangle, and match
584 /// the given predicate.
585 ///
586 /// # Parameters
587 ///
588 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
589 /// - `offset`: An offset to apply to returned points.
590 /// - `predicate`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
591 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
592 /// It returns `true` if the node matches the predicate, or `false` otherwise.
593 /// - `hash`: A HashSet into which matching points will be inserted.
594 #[inline]
595 pub fn collect_points<F, H>(
596 &self,
597 rect: &URect,
598 offset: IVec2,
599 mut predicate: F,
600 hash: &mut HashSet<IVec2, H>,
601 ) where
602 F: FnMut(&PNode<T, U>, &URect) -> bool,
603 H: BuildHasher,
604 {
605 let rect = rect.intersect(self.map_rect());
606 if rect.is_empty() {
607 return;
608 }
609 self.visit_in_rect(&rect, |node, sub_rect| {
610 debug_assert!(!sub_rect.is_empty());
611 if predicate(node, sub_rect) {
612 for p in urect_points(sub_rect) {
613 hash.insert(p.as_ivec2() + offset);
614 }
615 }
616 });
617 }
618
619 /// Visit all leaf nodes in this [PixelMap] for which the region overlaps with the line
620 /// defined by the [RayCastQuery].
621 ///
622 /// # Parameters
623 ///
624 /// - `query`: A [RayCastQuery] that defines the line to cast.
625 /// - `collision_check`: A closure that takes a reference to a leaf node as its only parameter.
626 /// It returns a [RayCast] value that determines if the node represents a collision or if the
627 /// ray should continue.
628 ///
629 /// # Returns
630 ///
631 /// A [RayCastResult] that contains the collision result and related information.
632 #[inline]
633 #[must_use]
634 pub fn ray_cast<F>(&self, query: RayCastQuery, mut collision_check: F) -> RayCastResult
635 where
636 F: FnMut(&PNode<T, U>) -> RayCast,
637 {
638 // TODO: truncate query line to map_size
639 let mut ctx = RayCastContext {
640 line_iter: query.line.pixels(),
641 traversed: 0,
642 };
643 if let Some(result) = self.root.ray_cast(&query, &mut ctx, &mut collision_check) {
644 return result;
645 }
646 RayCastResult {
647 collision_point: None,
648 distance: 0.0,
649 traversed: ctx.traversed,
650 }
651 }
652
653 /// Collect statistics by traversing the [PixelMap] quadtree.
654 ///
655 /// # Returns
656 ///
657 /// A [Stats] struct that contains information about [PixelMap]'s current state.
658 #[must_use]
659 pub fn stats(&self) -> Stats {
660 let mut stats = Stats::default();
661 self.root.visit_nodes_in_rect(
662 &self.region().into(),
663 &mut |node, _| {
664 stats.node_count += 1;
665 if node.is_leaf() {
666 stats.leaf_count += 1;
667
668 if node.region().is_unit(self.pixel_size) {
669 stats.unit_count += 1;
670 }
671 }
672 CellFill::Full
673 },
674 &mut 0,
675 );
676 stats
677 }
678
679 /// Combine another [PixelMap] with this one using a closure that decides how to combine
680 /// the values of each pixel. This [PixelMap]'s region should overlap with the other [PixelMap]'s region,
681 /// otherwise this operation has no effect.
682 ///
683 /// # Parameters
684 ///
685 /// - `other`: The other [PixelMap] to combine with this one.
686 /// - `offset`: The other [PixelMap] is sampled according to this offset vector.
687 /// - `combiner`: A closure that takes two values and returns a resulting value.
688 ///
689 /// # Examples
690 ///
691 /// This method can be used to implement boolean operations, such as union, intersection,
692 /// disjunction, etc., according to the combiner function's implementation.
693 ///
694 /// To compute the union of two [PixelMap]s:
695 /// ```
696 /// # use bevy_math::UVec2;
697 /// use pixel_map::{PixelMap, Region};
698 /// # #[derive(Copy,Clone,PartialEq)]
699 /// # enum Color { BLACK, WHITE }
700 /// # let mut pixel_map: PixelMap<Color, u16> = PixelMap::new(&UVec2::splat(128), Color::WHITE, 1);
701 /// # let mut other: PixelMap<Color, u16> = PixelMap::new(&UVec2::splat(128), Color::BLACK, 1);
702 /// // Union (OR)
703 /// pixel_map.combine(&other, (0, 0), |c1, c2| {
704 /// if c1 == &Color::BLACK || c2 == &Color::BLACK {
705 /// Color::BLACK
706 /// } else {
707 /// Color::WHITE
708 /// }
709 /// });
710 /// ```
711 ///
712 /// Compute an intersection of two [PixelMap]s:
713 /// ```
714 /// # use bevy_math::UVec2;
715 /// use pixel_map::{PixelMap, Region};
716 /// # #[derive(Copy,Clone,PartialEq)]
717 /// # enum Color { BLACK, WHITE }
718 /// # let mut pixel_map: PixelMap<Color, u16> = PixelMap::new(&UVec2::splat(128), Color::WHITE, 1);
719 /// # let mut other: PixelMap<Color, u16> = PixelMap::new(&UVec2::splat(128), Color::BLACK, 1);
720 /// // Intersection (AND)
721 /// pixel_map.combine(&other, (0, 0), |c1, c2| {
722 /// if c1 == &Color::BLACK && c2 == &Color::BLACK {
723 /// Color::BLACK
724 /// } else {
725 /// Color::WHITE
726 /// }
727 /// });
728 /// ```
729 pub fn combine<P, F>(&mut self, other: &Self, offset: P, combiner: F)
730 where
731 P: Into<UVec2>,
732 F: Fn(&T, &T) -> T,
733 {
734 let offset = offset.into();
735 let mut updates: Vec<(URect, T)> = Vec::new();
736 self.visit(|node, _| {
737 let mut region_rect: URect = node.region().into();
738 region_rect = URect::from_corners(region_rect.min + offset, region_rect.max + offset);
739 other.visit_in_rect(®ion_rect, |other_node, sub_rect| {
740 let value = combiner(node.value(), other_node.value());
741 let sub_rect = URect::from_corners(sub_rect.min - offset, sub_rect.max - offset);
742 updates.push((sub_rect, value));
743 });
744 });
745 for (rect, value) in updates {
746 self.draw_rect(&rect, value);
747 }
748 }
749
750 /// Generate a quad mesh that contains a triangulated quad for each leaf node accepted by
751 /// the predicate function. The returned quad mesh is non-uniform in that neighboring quads
752 /// having differing sizes, according to the layout of the quadtree, will not be fully
753 /// connected via triangulation.
754 ///
755 /// # Parameters
756 ///
757 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
758 /// - `predicate`: A closure that takes a reference to a leaf node, and a reference to a rectangle as parameters.
759 /// This rectangle represents the intersection of the node's region and the `rect` parameter supplied to this method.
760 /// It returns `true` if the node matches the predicate, or `false` otherwise.
761 /// - `size_estimate`: An estimated (or known) capacity of each returned vec.
762 ///
763 /// # Returns
764 ///
765 /// A tuple having a vec of unique vertex points, and a vec of triangle indices. Each element
766 /// in the index vec is a slice of each index in a triangle, in counter-clockwise winding.
767 pub fn non_uniform_quad_mesh<F>(
768 &self,
769 rect: &URect,
770 mut predicate: F,
771 size_estimate: usize,
772 ) -> (Vec<UVec2>, Vec<[u32; 3]>)
773 where
774 F: FnMut(&PNode<T, U>, &URect) -> bool,
775 {
776 let sub_rect = self.map_rect.intersect(*rect);
777 if sub_rect.is_empty() {
778 return (vec![], vec![]);
779 }
780
781 let mut vertex_map: HashMap<[u32; 2], u32, _> =
782 HashMap::with_capacity_and_hasher(size_estimate, FxBuildHasher::default());
783 let mut indices = Vec::with_capacity(size_estimate);
784
785 #[inline]
786 fn create_or_add_vertex(
787 vertex_map: &mut HashMap<[u32; 2], u32, BuildHasherDefault<FxHasher>>,
788 v: UVec2,
789 ) -> u32 {
790 let next_index = vertex_map.len() as u32;
791 *vertex_map.entry(v.into()).or_insert(next_index)
792 }
793
794 self.root.visit_leaves_in_rect(
795 &sub_rect,
796 &mut |n, sub_rect| {
797 if !predicate(n, sub_rect) {
798 return;
799 }
800
801 let c = urect_points(sub_rect);
802 let i = [
803 create_or_add_vertex(&mut vertex_map, c[0]),
804 create_or_add_vertex(&mut vertex_map, c[1]),
805 create_or_add_vertex(&mut vertex_map, c[2]),
806 create_or_add_vertex(&mut vertex_map, c[3]),
807 ];
808
809 indices.push([i[0], i[1], i[2]]);
810 indices.push([i[0], i[2], i[3]]);
811 },
812 &mut 0,
813 );
814
815 let mut vertices = Vec::with_capacity(vertex_map.len());
816 vertices.resize(vertex_map.len(), Default::default());
817 vertex_map.into_iter().for_each(|(k, v)| {
818 vertices[v as usize] = k.into();
819 });
820
821 (vertices, indices)
822 }
823
824 /// Obtain a list of line segments that contour the shapes determined by the given
825 /// `predicate` closure. In other words, if the `predicate` returns `true`,
826 /// the node is considered to be part of the shape for which a contour is being generated.
827 ///
828 /// *Note*: This implementation is likely to change in the future which may affect the
829 /// characteristics of the returned data.
830 ///
831 /// # Parameters
832 ///
833 /// - `rect`: The rectangle in which contained or overlapping nodes will be visited.
834 /// - `predicate`: A closure that takes a reference to a leaf node,
835 /// and a reference to the rectangle that is the effective intersection of the node's
836 /// region and the `rect` parameter supplied to this method.
837 ///
838 /// # Returns
839 ///
840 /// A list of line segments that contour the shapes determined by the given
841 /// `predicate` closure. The number of segments returned are the minimum possible,
842 /// in that one segment does not share continuity with any other segment.
843 #[must_use]
844 pub fn contour<F>(&self, rect: &URect, mut predicate: F) -> Vec<IsoLine>
845 where
846 F: FnMut(&PNode<T, U>, &URect) -> bool,
847 {
848 let sub_rect = self.map_rect.intersect(*rect);
849 if sub_rect.is_empty() {
850 return vec![];
851 }
852
853 let mut fragments = FragmentAccumulator::new(256);
854 self.contour_segments(&sub_rect, &mut predicate, |seg| {
855 fragments.attach(*seg);
856 });
857
858 fragments.result()
859 }
860
861 fn contour_segments<F, G>(&self, rect: &URect, mut predicate: F, mut seg_handler: G)
862 where
863 F: FnMut(&PNode<T, U>, &URect) -> bool,
864 G: FnMut(&ILine),
865 {
866 self.root
867 .visit_neighbor_pairs_face(rect, &mut |or, a, a_rect, b, b_rect| {
868 match or {
869 NeighborOrientation::Horizontal => {
870 let (left, left_rect, right, right_rect) = (a, a_rect, b, b_rect);
871 if predicate(left, left_rect) != predicate(right, right_rect) {
872 // right edge of the left node
873 let left = left.region().as_urect();
874 let left_right_edge = iline(
875 ivec2(left.max.x as i32, left.min.y as i32),
876 left.max.as_ivec2(),
877 );
878 let left_right_edge =
879 left_right_edge.axis_aligned_intersect_rect(&left_rect.as_irect());
880
881 // left edge of the right node
882 let right = right.region().as_urect();
883 let right_left_edge = iline(
884 right.min.as_ivec2(),
885 ivec2(right.min.x as i32, right.max.y as i32),
886 );
887 let right_left_edge =
888 right_left_edge.axis_aligned_intersect_rect(&right_rect.as_irect());
889
890 if let (Some(left_right_edge), Some(right_left_edge)) =
891 (left_right_edge, right_left_edge)
892 {
893 if let Some(common_edge) = left_right_edge.overlap(&right_left_edge)
894 {
895 seg_handler(&common_edge);
896 }
897 }
898 }
899 }
900 NeighborOrientation::Vertical => {
901 let (bottom, bottom_rect, top, top_rect) = (a, a_rect, b, b_rect);
902 if predicate(bottom, bottom_rect) != predicate(top, top_rect) {
903 // top edge of the bottom node
904 let bottom = bottom.region().as_urect();
905 let bottom_top_edge = iline(
906 ivec2(bottom.min.x as i32, bottom.max.y as i32),
907 bottom.max.as_ivec2(),
908 );
909 let bottom_top_edge = bottom_top_edge
910 .axis_aligned_intersect_rect(&bottom_rect.as_irect());
911
912 // bottom edge of the top node
913 let top = top.region().as_urect();
914 let top_bottom_edge = iline(
915 top.min.as_ivec2(),
916 ivec2(top.max.x as i32, top.min.y as i32),
917 );
918 let top_bottom_edge =
919 top_bottom_edge.axis_aligned_intersect_rect(&top_rect.as_irect());
920
921 if let (Some(bottom_top_edge), Some(top_bottom_edge)) =
922 (bottom_top_edge, top_bottom_edge)
923 {
924 if let Some(common_edge) = bottom_top_edge.overlap(&top_bottom_edge)
925 {
926 seg_handler(&common_edge);
927 }
928 }
929 }
930 }
931 }
932 });
933 }
934}
935
936impl<T: Copy + PartialEq, U: Unsigned + NumCast + Copy + Debug> Debug for PixelMap<T, U> {
937 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
938 f.debug_struct("PixelMap")
939 .field("pixel_size", &self.pixel_size)
940 .finish()
941 }
942}
943
944/// Stores statistics about a [PixelMap].
945/// See [PixelMap::stats].
946#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
947#[derive(Debug, Default, Eq, PartialEq)]
948pub struct Stats {
949 /// The number of nodes in the quadtree.
950 pub node_count: usize,
951
952 /// The number of leaf nodes in the quadtree.
953 pub leaf_count: usize,
954
955 /// The number of leaf nodes in the quadtree for which the region is a unit pixel size.
956 /// The unit size is defined by the `pixel_size` parameter of the [PixelMap] constructor.
957 pub unit_count: usize,
958}
959
960#[inline]
961#[must_use]
962fn next_pow2(mut n: u32) -> u32 {
963 if n <= 2 {
964 return 2;
965 }
966 let mut count = 0;
967 if n > 0 && (n & (n - 1)) == 0 {
968 return n;
969 }
970 while n != 0 {
971 n >>= 1;
972 count += 1;
973 }
974
975 1 << count
976}
977
978#[cfg(test)]
979mod test {
980 use crate::pixel_map::next_pow2;
981 use crate::*;
982 use bevy_math::{IVec2, URect, UVec2};
983 use std::collections::HashSet;
984
985 #[test]
986 fn test_u_type_parameters() {
987 let _ = PixelMap::<bool, u8>::new(&UVec2::splat(128), false, 1);
988 let _ = PixelMap::<bool, u16>::new(&UVec2::splat(128), false, 1);
989 let _ = PixelMap::<bool, u32>::new(&UVec2::splat(128), false, 1);
990 let _ = PixelMap::<bool, u64>::new(&UVec2::splat(128), false, 1);
991 let _ = PixelMap::<bool, u128>::new(&UVec2::splat(128), false, 1);
992 }
993
994 #[test]
995 fn test_clear() {
996 let mut pm = PixelMap::<i32, u32>::new(&UVec2::splat(2), 0, 1);
997 pm.set_pixel((1, 1), 1);
998 pm.clear(2);
999 assert_eq!(pm.root.value(), &2);
1000 assert!(pm.root.is_leaf());
1001 }
1002
1003 #[test]
1004 fn test_draw_rect() {
1005 let map_size = 32;
1006 let mut pm = PixelMap::<bool, u32>::new(&UVec2::splat(map_size), false, 1);
1007
1008 for rect_width in 1..=map_size {
1009 for rect_height in 1..=map_size {
1010 pm.clear(false);
1011
1012 let rect = URect::new(0, 0, rect_width, rect_height);
1013 pm.draw_rect(&rect, true);
1014
1015 let rect = exclusive_urect(&rect);
1016 for y in 0..map_size {
1017 for x in 0..map_size {
1018 let p = (x, y).into();
1019 if rect.contains(p) {
1020 assert_eq!(
1021 pm.get_pixel(p),
1022 Some(&true),
1023 "rect_width: {}, rect_height: {}, assert: {}",
1024 rect_width,
1025 rect_height,
1026 p
1027 );
1028 } else {
1029 assert_eq!(
1030 pm.get_pixel(p),
1031 Some(&false),
1032 "rect_width: {}, rect_height: {}, assert: {}",
1033 rect_width,
1034 rect_height,
1035 p
1036 );
1037 }
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 #[test]
1045 fn test_stats_with_root_node() {
1046 let pm = PixelMap::<bool, u32>::new(&UVec2::splat(2), false, 1);
1047 assert_eq!(
1048 pm.stats(),
1049 Stats {
1050 node_count: 1,
1051 leaf_count: 1,
1052 unit_count: 0,
1053 }
1054 );
1055 }
1056
1057 #[test]
1058 fn test_stats_with_children() {
1059 let mut pm = PixelMap::<bool, u32>::new(&UVec2::splat(2), false, 1);
1060 pm.set_pixel((1, 1), true);
1061 assert_eq!(
1062 pm.stats(),
1063 Stats {
1064 node_count: 5,
1065 leaf_count: 4,
1066 unit_count: 4,
1067 }
1068 );
1069 }
1070
1071 #[test]
1072 fn test_stats_with_grandchildren() {
1073 let mut pm = PixelMap::<bool, u32>::new(&UVec2::splat(4), false, 1);
1074 pm.draw_rect(&URect::new(0, 0, 2, 2), true);
1075 pm.set_pixel((0, 0), false);
1076 assert_eq!(
1077 pm.stats(),
1078 Stats {
1079 node_count: 9,
1080 leaf_count: 7,
1081 unit_count: 4,
1082 }
1083 );
1084 }
1085
1086 #[test]
1087 fn test_any_in_rect() {
1088 let mut pm = PixelMap::<bool, u32>::new(&UVec2::splat(2), false, 1);
1089
1090 assert_eq!(
1091 pm.any_in_rect(&URect::new(0, 0, 2, 2), |n, _| *n.value()),
1092 Some(false)
1093 );
1094 assert_eq!(
1095 pm.any_in_rect(&URect::new(2, 2, 4, 4), |n, _| *n.value()),
1096 None
1097 );
1098
1099 pm.set_pixel((0, 0), true);
1100
1101 assert_eq!(
1102 pm.any_in_rect(&URect::new(0, 0, 2, 2), |n, _| *n.value()),
1103 Some(true)
1104 );
1105 assert_eq!(
1106 pm.any_in_rect(&URect::new(0, 0, 2, 2), |n, _| !*n.value()),
1107 Some(true)
1108 );
1109 assert_eq!(
1110 pm.any_in_rect(&URect::new(0, 0, 1, 1), |n, _| *n.value()),
1111 Some(true)
1112 );
1113 assert_eq!(
1114 pm.any_in_rect(&URect::new(1, 1, 2, 2), |n, _| *n.value()),
1115 Some(false)
1116 );
1117 }
1118
1119 #[test]
1120 fn test_all_in_rect() {
1121 let mut pm = PixelMap::<bool, u32>::new(&UVec2::splat(2), false, 1);
1122
1123 assert_eq!(
1124 pm.all_in_rect(&URect::new(0, 0, 2, 2), |n, _| !*n.value()),
1125 Some(true)
1126 );
1127 assert_eq!(
1128 pm.all_in_rect(&URect::new(2, 2, 4, 4), |n, _| *n.value()),
1129 None
1130 );
1131
1132 pm.set_pixel((0, 0), true);
1133
1134 assert_eq!(
1135 pm.all_in_rect(&URect::new(0, 0, 2, 2), |n, _| *n.value()),
1136 Some(false)
1137 );
1138 assert_eq!(
1139 pm.all_in_rect(&URect::new(0, 0, 2, 2), |n, _| !*n.value()),
1140 Some(false)
1141 );
1142 assert_eq!(
1143 pm.all_in_rect(&URect::new(0, 0, 1, 1), |n, _| *n.value()),
1144 Some(true)
1145 );
1146 assert_eq!(
1147 pm.all_in_rect(&URect::new(1, 1, 2, 2), |n, _| *n.value()),
1148 Some(false)
1149 );
1150 }
1151
1152 #[test]
1153 #[cfg(feature = "serialize")]
1154 fn test_serialization() {
1155 let mut pm: PixelMap<bool, u32> = PixelMap::new(&UVec2::splat(2), false, 1);
1156 pm.set_pixel((0, 0), true);
1157
1158 let pmstr = ron::to_string(&pm).unwrap();
1159 let pm2: PixelMap<bool, u32> = ron::from_str(&pmstr).unwrap();
1160
1161 assert_eq!(pm.root, pm2.root);
1162 assert_eq!(pm.pixel_size, pm2.pixel_size);
1163 assert!(pm2.get_pixel((0, 0)).unwrap());
1164 }
1165
1166 #[test]
1167 fn test_next_pow2() {
1168 assert_eq!(next_pow2(0u32), 2);
1169 assert_eq!(next_pow2(1u32), 2);
1170 assert_eq!(next_pow2(2u32), 2);
1171 assert_eq!(next_pow2(3u32), 4);
1172 assert_eq!(next_pow2(4u32), 4);
1173 assert_eq!(next_pow2(5u32), 8);
1174 assert_eq!(next_pow2(17u32), 32);
1175 assert_eq!(next_pow2(32u32), 32);
1176 assert_eq!(next_pow2(33u32), 64);
1177 }
1178
1179 #[test]
1180 fn test_contour_segments_unique() {
1181 let mut pm: PixelMap<bool, u32> = PixelMap::new(&UVec2::splat(1024), false, 1);
1182 pm.draw_circle(&ICircle::new(IVec2::splat(512), 50), true);
1183
1184 let mut segments: HashSet<ILine> = HashSet::with_capacity(256);
1185 pm.contour_segments(
1186 &pm.region().as_urect(),
1187 |a, _| *a.value(),
1188 |seg| {
1189 assert!(segments.insert(*seg));
1190 assert!(segments.insert(seg.flip()));
1191 },
1192 );
1193
1194 assert_eq!(segments.len(), 728);
1195 }
1196}