parry2d/shape/voxels/
voxels_edition.rs1use crate::bounding_volume::Aabb;
2use crate::math::{Point, Real, Vector, DIM};
3use crate::shape::voxels::voxels_chunk::{VoxelsChunk, VoxelsChunkHeader};
4use crate::shape::{VoxelState, Voxels};
5use crate::utils::hashmap::Entry;
6use alloc::vec;
7
8impl Voxels {
9 pub fn set_voxel_size(&mut self, new_size: Vector<Real>) {
14 let scale = new_size.component_div(&self.voxel_size);
15 self.chunk_bvh.scale(&scale);
16 self.voxel_size = new_size;
17 }
18
19 pub fn set_voxel(&mut self, key: Point<i32>, is_filled: bool) -> VoxelState {
23 let (chunk_key, id_in_chunk) = Self::chunk_key_and_id_in_chunk(key);
24 let header_entry = self.chunk_headers.entry(chunk_key);
25
26 if !is_filled && matches!(header_entry, Entry::Vacant(_)) {
27 return VoxelState::EMPTY;
30 }
31
32 let chunk_header = header_entry.or_insert_with(|| {
33 let id = self.free_chunks.pop().unwrap_or_else(|| {
34 self.chunks.push(VoxelsChunk::default());
35 self.chunk_keys.push(chunk_key);
36 self.chunks.len() - 1
37 });
38
39 self.chunk_keys[id] = chunk_key;
40 self.chunk_bvh
41 .insert(VoxelsChunk::aabb(&chunk_key, &self.voxel_size), id as u32);
42 VoxelsChunkHeader { id, len: 0 }
43 });
44 let chunk_id = chunk_header.id;
45
46 let prev = self.chunks[chunk_id].states[id_in_chunk];
47 let new_is_empty = !is_filled;
48
49 if prev.is_empty() ^ new_is_empty {
50 let can_remove_chunk = if new_is_empty {
51 chunk_header.len -= 1;
52 chunk_header.len == 0
53 } else {
54 chunk_header.len += 1;
55 false
56 };
57
58 self.chunks[chunk_id].states[id_in_chunk] =
59 self.update_neighbors_state(key, new_is_empty);
60
61 if can_remove_chunk {
62 self.chunk_bvh.remove(chunk_id as u32);
63
64 #[cfg(feature = "enhanced-determinism")]
65 let _ = self.chunk_headers.swap_remove(&chunk_key);
66 #[cfg(not(feature = "enhanced-determinism"))]
67 let _ = self.chunk_headers.remove(&chunk_key);
68
69 self.free_chunks.push(chunk_id);
70 self.chunk_keys[chunk_id] = VoxelsChunk::INVALID_CHUNK_KEY;
71 }
72 }
73
74 prev
75 }
76
77 pub fn crop(&mut self, domain_mins: Point<i32>, domain_maxs: Point<i32>) {
81 if let Some(new_shape) = self.cropped(domain_mins, domain_maxs) {
83 *self = new_shape;
84 }
85 }
86
87 pub fn cropped(&self, domain_mins: Point<i32>, domain_maxs: Point<i32>) -> Option<Self> {
91 let mut in_box = vec![];
93 for vox in self.voxels() {
94 if !vox.state.is_empty()
95 && grid_aabb_contains_point(&domain_mins, &domain_maxs, &vox.grid_coords)
96 {
97 in_box.push(vox.grid_coords);
98 }
99 }
100
101 if !in_box.is_empty() {
102 Some(Voxels::new(self.voxel_size, &in_box))
103 } else {
104 None
105 }
106 }
107
108 pub fn split_with_box(&self, aabb: &Aabb) -> (Option<Self>, Option<Self>) {
113 let mut in_box = vec![];
115 let mut rest = vec![];
116 for vox in self.voxels() {
117 if !vox.state.is_empty() {
118 if aabb.contains_local_point(&vox.center) {
119 in_box.push(vox.grid_coords);
120 } else {
121 rest.push(vox.grid_coords);
122 }
123 }
124 }
125
126 let in_box = if !in_box.is_empty() {
127 Some(Voxels::new(self.voxel_size, &in_box))
128 } else {
129 None
130 };
131
132 let rest = if !rest.is_empty() {
133 Some(Voxels::new(self.voxel_size, &rest))
134 } else {
135 None
136 };
137
138 (in_box, rest)
139 }
140}
141
142fn grid_aabb_contains_point(mins: &Point<i32>, maxs: &Point<i32>, point: &Point<i32>) -> bool {
143 for i in 0..DIM {
144 if point[i] < mins[i] || point[i] > maxs[i] {
145 return false;
146 }
147 }
148
149 true
150}