bentopy 0.2.0a10__cp313-cp313-manylinux_2_34_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bentopy-0.2.0a10.data/scripts/bentopy-init +0 -0
- bentopy-0.2.0a10.data/scripts/bentopy-pack +0 -0
- bentopy-0.2.0a10.data/scripts/bentopy-render +0 -0
- bentopy-0.2.0a10.data/scripts/bentopy-solvate +0 -0
- bentopy-0.2.0a10.dist-info/METADATA +358 -0
- bentopy-0.2.0a10.dist-info/RECORD +58 -0
- bentopy-0.2.0a10.dist-info/WHEEL +5 -0
- bentopy-0.2.0a10.dist-info/entry_points.txt +4 -0
- bentopy-0.2.0a10.dist-info/licenses/LICENSE.txt +13 -0
- bentopy-0.2.0a10.dist-info/top_level.txt +8 -0
- check/check.py +128 -0
- core/config/bent/lexer.rs +338 -0
- core/config/bent/parser.rs +1180 -0
- core/config/bent/writer.rs +205 -0
- core/config/bent.rs +149 -0
- core/config/compartment_combinations.rs +300 -0
- core/config/legacy.rs +768 -0
- core/config.rs +362 -0
- core/mod.rs +4 -0
- core/placement.rs +100 -0
- core/utilities.rs +1 -0
- core/version.rs +32 -0
- init/example.bent +74 -0
- init/main.rs +235 -0
- mask/config.py +153 -0
- mask/mask.py +308 -0
- mask/utilities.py +38 -0
- merge/merge.py +175 -0
- pack/args.rs +77 -0
- pack/main.rs +121 -0
- pack/mask.rs +940 -0
- pack/session.rs +176 -0
- pack/state/combinations.rs +31 -0
- pack/state/compartment.rs +44 -0
- pack/state/mask.rs +196 -0
- pack/state/pack.rs +187 -0
- pack/state/segment.rs +72 -0
- pack/state/space.rs +98 -0
- pack/state.rs +440 -0
- pack/structure.rs +185 -0
- pack/voxelize.rs +85 -0
- render/args.rs +109 -0
- render/limits.rs +73 -0
- render/main.rs +12 -0
- render/render.rs +393 -0
- render/structure.rs +264 -0
- solvate/args.rs +324 -0
- solvate/convert.rs +25 -0
- solvate/cookies.rs +185 -0
- solvate/main.rs +177 -0
- solvate/placement.rs +380 -0
- solvate/solvate.rs +244 -0
- solvate/structure.rs +160 -0
- solvate/substitute.rs +113 -0
- solvate/water/martini.rs +409 -0
- solvate/water/models.rs +150 -0
- solvate/water/tip3p.rs +658 -0
- solvate/water.rs +115 -0
pack/mask.rs
ADDED
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
use std::ops::{BitAndAssign, BitOrAssign, Not};
|
|
2
|
+
|
|
3
|
+
use glam::{I64Vec3, IVec3, U64Vec3};
|
|
4
|
+
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
|
5
|
+
|
|
6
|
+
pub type Dimensions = [u64; 3]; // TODO: Make into usize? Rather awkward in places right now.
|
|
7
|
+
pub type Position = Dimensions;
|
|
8
|
+
|
|
9
|
+
type Backing = u8;
|
|
10
|
+
const BACKING_BITS: usize = Backing::BITS as usize;
|
|
11
|
+
|
|
12
|
+
/// A 3-dimensional bitmap-backed `Mask` type that is used to represent voxelized spaces.
|
|
13
|
+
///
|
|
14
|
+
/// Invariant: A `Mask` has non-zero dimensions.
|
|
15
|
+
#[derive(Debug, Clone)]
|
|
16
|
+
pub struct Mask {
|
|
17
|
+
dimensions: [usize; 3],
|
|
18
|
+
pub(crate) backings: Box<[Backing]>,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl Mask {
|
|
22
|
+
pub fn new(dimensions: Dimensions) -> Self {
|
|
23
|
+
Self::fill::<false>(dimensions)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn fill<const VALUE: bool>(dimensions: Dimensions) -> Self {
|
|
27
|
+
let dimensions = dimensions.map(|v| v as usize);
|
|
28
|
+
// Invariant: A `Mask` has non-zero dimensions.
|
|
29
|
+
assert!(
|
|
30
|
+
dimensions.iter().all(|&v| v > 0),
|
|
31
|
+
"a mask must have non-zero dimensions"
|
|
32
|
+
);
|
|
33
|
+
let n: usize = dimensions.iter().product();
|
|
34
|
+
let n_backings = n.div_ceil(BACKING_BITS);
|
|
35
|
+
let value = if VALUE { Backing::MAX } else { Backing::MIN };
|
|
36
|
+
Self {
|
|
37
|
+
dimensions,
|
|
38
|
+
backings: vec![value; n_backings].into_boxed_slice(),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// # Panics
|
|
43
|
+
///
|
|
44
|
+
/// If the provided `dimensions` are not compatible with the number of `cells`, this function
|
|
45
|
+
/// will panic.
|
|
46
|
+
pub fn from_cells(dimensions: Dimensions, cells: &[bool]) -> Self {
|
|
47
|
+
assert_eq!(
|
|
48
|
+
dimensions.iter().product::<u64>() as usize,
|
|
49
|
+
cells.len(),
|
|
50
|
+
"the number of cells must match the dimensions"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
Self::from_cells_iter(dimensions, cells.iter().copied())
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// An internal function for setting up a [`Mask`] from an iterator of `bool`s.
|
|
57
|
+
///
|
|
58
|
+
/// Providing an iterator that is not exactly the correct length is not acceptable behavior but
|
|
59
|
+
/// is not unsafe.
|
|
60
|
+
fn from_cells_iter(dimensions: Dimensions, cells: impl Iterator<Item = bool>) -> Self {
|
|
61
|
+
let mut mask = Self::new(dimensions);
|
|
62
|
+
|
|
63
|
+
for lin_idx in cells
|
|
64
|
+
.enumerate()
|
|
65
|
+
.filter_map(|(i, v)| if v { Some(i) } else { None })
|
|
66
|
+
{
|
|
67
|
+
mask.set_linear_unchecked::<true>(lin_idx)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
mask
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pub const fn dimensions(&self) -> Dimensions {
|
|
74
|
+
let [w, h, d] = self.dimensions;
|
|
75
|
+
[w as u64, h as u64, d as u64]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Return the number of cells that are equal to `VALUE`.
|
|
79
|
+
pub fn count<const VALUE: bool>(&self) -> usize {
|
|
80
|
+
// If we're counting the `false` cells, we may overcount when there are trailing bits in
|
|
81
|
+
// the last backing.
|
|
82
|
+
let overshoot = if VALUE {
|
|
83
|
+
0
|
|
84
|
+
} else {
|
|
85
|
+
self.n_backings() * BACKING_BITS - self.n_cells()
|
|
86
|
+
};
|
|
87
|
+
let uncorrected: usize = self
|
|
88
|
+
.backings
|
|
89
|
+
.iter()
|
|
90
|
+
.map(|backing| {
|
|
91
|
+
if VALUE {
|
|
92
|
+
backing.count_ones() as usize
|
|
93
|
+
} else {
|
|
94
|
+
backing.count_zeros() as usize
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.sum();
|
|
98
|
+
uncorrected - overshoot
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const fn n_cells(&self) -> usize {
|
|
102
|
+
let [w, h, d] = self.dimensions;
|
|
103
|
+
w * h * d
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pub(crate) const fn n_backings(&self) -> usize {
|
|
107
|
+
self.backings.len()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const fn backing_idx(&self, lin_idx: usize) -> (usize, usize) {
|
|
111
|
+
(lin_idx / BACKING_BITS, lin_idx % BACKING_BITS)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const fn contains(&self, idx: Position) -> bool {
|
|
115
|
+
let [x, y, z] = idx;
|
|
116
|
+
let [w, h, d] = self.dimensions();
|
|
117
|
+
x < w && y < h && z < d
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pub const fn spatial_idx(&self, mut lin_idx: usize) -> Option<Position> {
|
|
121
|
+
if lin_idx >= self.n_cells() {
|
|
122
|
+
return None;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let [w, h, _] = self.dimensions;
|
|
126
|
+
let x = lin_idx % w;
|
|
127
|
+
lin_idx /= w;
|
|
128
|
+
let y = lin_idx % h;
|
|
129
|
+
lin_idx /= h;
|
|
130
|
+
let z = lin_idx;
|
|
131
|
+
|
|
132
|
+
Some([x as u64, y as u64, z as u64])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const fn linear_idx(&self, idx: Position) -> usize {
|
|
136
|
+
let [x, y, z] = idx;
|
|
137
|
+
let [w, h, _] = self.dimensions;
|
|
138
|
+
let lin_idx = x as usize + y as usize * w + z as usize * w * h;
|
|
139
|
+
debug_assert!(lin_idx < self.n_cells());
|
|
140
|
+
lin_idx
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const fn get_linear_unchecked(&self, lin_idx: usize) -> bool {
|
|
144
|
+
debug_assert!(lin_idx < self.n_cells());
|
|
145
|
+
let (backing_idx, bit_idx) = self.backing_idx(lin_idx);
|
|
146
|
+
self.backings[backing_idx] >> bit_idx & 1 != 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Check whether the cell at the specified `lin_idx` is `VALUE`.
|
|
150
|
+
const fn query_linear_unchecked<const VALUE: bool>(&self, lin_idx: usize) -> bool {
|
|
151
|
+
debug_assert!(lin_idx < self.n_cells());
|
|
152
|
+
let (backing_idx, bit_idx) = self.backing_idx(lin_idx);
|
|
153
|
+
let backing = self.backings[backing_idx];
|
|
154
|
+
// FIXME: This seems convoluted for what the truth table actually looks like.
|
|
155
|
+
if VALUE {
|
|
156
|
+
match backing {
|
|
157
|
+
0 => false,
|
|
158
|
+
Backing::MAX => true,
|
|
159
|
+
_ => backing >> bit_idx & 1 != 0,
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
match backing {
|
|
163
|
+
0 => true,
|
|
164
|
+
Backing::MAX => false,
|
|
165
|
+
_ => backing >> bit_idx & 1 == 0,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn set_linear_unchecked<const VALUE: bool>(&mut self, lin_idx: usize) {
|
|
171
|
+
debug_assert!(lin_idx < self.n_cells());
|
|
172
|
+
let (backing_idx, bit_idx) = self.backing_idx(lin_idx);
|
|
173
|
+
if VALUE {
|
|
174
|
+
self.backings[backing_idx] |= 1 << bit_idx;
|
|
175
|
+
} else {
|
|
176
|
+
self.backings[backing_idx] &= !(1 << bit_idx);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[allow(dead_code)]
|
|
181
|
+
pub const fn get(&self, idx: Position) -> Option<bool> {
|
|
182
|
+
if self.contains(idx) {
|
|
183
|
+
let lin_idx = self.linear_idx(idx);
|
|
184
|
+
Some(self.get_linear_unchecked(lin_idx))
|
|
185
|
+
} else {
|
|
186
|
+
None
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
pub const fn get_periodic(&self, idx: Position) -> bool {
|
|
191
|
+
let lin_idx = self.linear_idx(normalize_periodic(idx, self.dimensions()));
|
|
192
|
+
self.get_linear_unchecked(lin_idx)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// # Panics
|
|
196
|
+
///
|
|
197
|
+
/// If `idx` is not within the dimensions of this `Mask`, this function will panic.
|
|
198
|
+
pub fn set(&mut self, idx: Position, value: bool) {
|
|
199
|
+
assert!(
|
|
200
|
+
self.contains(idx),
|
|
201
|
+
"the provided `idx` must be within the dimensions of this mask"
|
|
202
|
+
);
|
|
203
|
+
let lin_idx = self.linear_idx(idx);
|
|
204
|
+
if value {
|
|
205
|
+
self.set_linear_unchecked::<true>(lin_idx)
|
|
206
|
+
} else {
|
|
207
|
+
self.set_linear_unchecked::<false>(lin_idx)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pub fn set_periodic(&mut self, idx: Position, value: bool) {
|
|
212
|
+
let lin_idx = self.linear_idx(normalize_periodic(idx, self.dimensions()));
|
|
213
|
+
if value {
|
|
214
|
+
self.set_linear_unchecked::<true>(lin_idx)
|
|
215
|
+
} else {
|
|
216
|
+
self.set_linear_unchecked::<false>(lin_idx)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Return an [`Iterator`] over all linear indices where the cell is equal to `VALUE`.
|
|
221
|
+
pub fn linear_indices_where<const VALUE: bool>(&self) -> impl Iterator<Item = usize> + '_ {
|
|
222
|
+
(0..self.n_cells()).filter(|&lin_idx| self.query_linear_unchecked::<VALUE>(lin_idx))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Return an [`Iterator`] over all indices where the cell is equal to `VALUE`.
|
|
226
|
+
pub fn indices_where<const VALUE: bool>(&self) -> impl Iterator<Item = Position> + '_ {
|
|
227
|
+
let [w, h, d] = self.dimensions();
|
|
228
|
+
(0..d).flat_map(move |z| {
|
|
229
|
+
(0..h).flat_map(move |y| {
|
|
230
|
+
(0..w).filter_map(move |x| {
|
|
231
|
+
let idx = [x, y, z];
|
|
232
|
+
let lin_idx = self.linear_idx(idx);
|
|
233
|
+
if self.query_linear_unchecked::<VALUE>(lin_idx) {
|
|
234
|
+
Some(idx)
|
|
235
|
+
} else {
|
|
236
|
+
None
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Apply some `mask` onto this [`Mask`].
|
|
244
|
+
///
|
|
245
|
+
/// This is essentially an `or-assign` operation between the `self` and the provided `mask`.
|
|
246
|
+
///
|
|
247
|
+
/// # Panics
|
|
248
|
+
///
|
|
249
|
+
/// If the dimensions of the stamped and stamping mask are different, this function will panic.
|
|
250
|
+
pub fn apply_mask(&mut self, mask: &Mask) {
|
|
251
|
+
assert_eq!(
|
|
252
|
+
self.dimensions(),
|
|
253
|
+
mask.dimensions(),
|
|
254
|
+
"the dimensions of both masks must be identical"
|
|
255
|
+
);
|
|
256
|
+
// For good measure, so the compiler gets it.
|
|
257
|
+
assert_eq!(self.n_backings(), mask.n_backings()); // FIXME: Is this one necessary?
|
|
258
|
+
|
|
259
|
+
self.backings
|
|
260
|
+
.iter_mut()
|
|
261
|
+
.zip(mask.backings.iter())
|
|
262
|
+
.for_each(|(s, &m)| *s |= m);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
pub fn merge_mask(&mut self, mask: &Mask) {
|
|
266
|
+
assert_eq!(
|
|
267
|
+
self.dimensions(),
|
|
268
|
+
mask.dimensions(),
|
|
269
|
+
"the dimensions of both masks must be identical"
|
|
270
|
+
);
|
|
271
|
+
// For good measure, so the compiler gets it.
|
|
272
|
+
assert_eq!(self.n_backings(), mask.n_backings()); // FIXME: Is this one necessary?
|
|
273
|
+
|
|
274
|
+
self.backings
|
|
275
|
+
.iter_mut()
|
|
276
|
+
.zip(mask.backings.iter())
|
|
277
|
+
.for_each(|(s, &m)| *s &= m);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
pub fn apply_function(&mut self, f: impl Fn(Position) -> bool) {
|
|
281
|
+
let [w, h, d] = self.dimensions();
|
|
282
|
+
let mut lin_idx = 0;
|
|
283
|
+
for z in 0..d {
|
|
284
|
+
for y in 0..h {
|
|
285
|
+
for x in 0..w {
|
|
286
|
+
if f([x, y, z]) {
|
|
287
|
+
self.set_linear_unchecked::<true>(lin_idx);
|
|
288
|
+
} else {
|
|
289
|
+
self.set_linear_unchecked::<false>(lin_idx);
|
|
290
|
+
}
|
|
291
|
+
lin_idx += 1;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/// Return whether any of the [`Mask`] cells have the provided `VALUE`.
|
|
298
|
+
pub fn any<const VALUE: bool>(&self) -> bool {
|
|
299
|
+
(0..self.n_cells()).any(|lin_idx| self.query_linear_unchecked::<VALUE>(lin_idx))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// TODO: Periodicity?
|
|
303
|
+
/// Grow the voxels in the [`Mask`] in _x_, _y_, and _z_ directions.
|
|
304
|
+
pub fn grow_approx(&mut self) {
|
|
305
|
+
const OFFSETS: [I64Vec3; 6] = [
|
|
306
|
+
I64Vec3::X,
|
|
307
|
+
I64Vec3::NEG_X,
|
|
308
|
+
I64Vec3::Y,
|
|
309
|
+
I64Vec3::NEG_Y,
|
|
310
|
+
I64Vec3::Z,
|
|
311
|
+
I64Vec3::NEG_Z,
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
let old = self.clone();
|
|
315
|
+
let dimensions = self.dimensions();
|
|
316
|
+
let [w, h, d] = dimensions;
|
|
317
|
+
let offset_masks = OFFSETS.into_par_iter().map(|offset| {
|
|
318
|
+
let mut mask = Mask::new(dimensions);
|
|
319
|
+
for z in 0..d {
|
|
320
|
+
let oz = z as i64 + offset.z;
|
|
321
|
+
if oz < 0 || oz >= d as i64 {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
let oz = oz as u64;
|
|
325
|
+
|
|
326
|
+
for y in 0..h {
|
|
327
|
+
let oy = y as i64 + offset.y;
|
|
328
|
+
if oy < 0 || oy >= h as i64 {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
let oy = oy as u64;
|
|
332
|
+
|
|
333
|
+
for x in 0..w {
|
|
334
|
+
let ox = x as i64 + offset.x;
|
|
335
|
+
if ox < 0 || ox >= w as i64 {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
let ox = ox as u64;
|
|
339
|
+
|
|
340
|
+
let offset_lin_idx = ox + oy * w + oz * w * h;
|
|
341
|
+
if old.query_linear_unchecked::<true>(offset_lin_idx as usize) {
|
|
342
|
+
let lin_idx = x + y * w + z * w * h;
|
|
343
|
+
mask.set_linear_unchecked::<true>(lin_idx as usize)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
mask
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
*self |= offset_masks.reduce(
|
|
353
|
+
|| Mask::new(dimensions),
|
|
354
|
+
|mut acc, mask| {
|
|
355
|
+
acc |= mask;
|
|
356
|
+
acc
|
|
357
|
+
},
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/// Take an `idx` that may fall outside of the `dimensions` and return a normalized [`Position`].
|
|
363
|
+
// NOTE: It turns out that when compiled with '-C target-cpu=native', this unrolled operation
|
|
364
|
+
// behaved very poorly. That led to poor performance, as this function is very hot.
|
|
365
|
+
// Inlining this function helped.
|
|
366
|
+
#[inline(always)]
|
|
367
|
+
const fn normalize_periodic(idx: Position, dimensions: Dimensions) -> Position {
|
|
368
|
+
[
|
|
369
|
+
idx[0] % dimensions[0],
|
|
370
|
+
idx[1] % dimensions[1],
|
|
371
|
+
idx[2] % dimensions[2],
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
mod blocks {
|
|
376
|
+
use super::{Dimensions, IVec3, U64Vec3};
|
|
377
|
+
|
|
378
|
+
type Block = Vec<U64Vec3>;
|
|
379
|
+
|
|
380
|
+
pub struct Blocks {
|
|
381
|
+
radius: u64,
|
|
382
|
+
size: [usize; 3],
|
|
383
|
+
blocks: Vec<Block>,
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
impl Blocks {
|
|
387
|
+
pub fn from_iter(
|
|
388
|
+
radius: u64,
|
|
389
|
+
dimensions: Dimensions,
|
|
390
|
+
positions: impl Iterator<Item = U64Vec3>,
|
|
391
|
+
) -> Self {
|
|
392
|
+
let size = dimensions.map(|v| (v / radius) as usize + 1);
|
|
393
|
+
let [w, h, _] = size;
|
|
394
|
+
let mut blocks = vec![Vec::new(); size.iter().product()];
|
|
395
|
+
for pos in positions {
|
|
396
|
+
let idx = pos / radius;
|
|
397
|
+
let [x, y, z] = idx.to_array().map(|v| v as usize);
|
|
398
|
+
let lin_idx = x + y * w + z * w * h;
|
|
399
|
+
blocks[lin_idx].push(pos)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
Self {
|
|
403
|
+
radius,
|
|
404
|
+
size,
|
|
405
|
+
blocks,
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
pub fn nearby(&self, position: U64Vec3) -> impl Iterator<Item = U64Vec3> + '_ {
|
|
410
|
+
let [w, h, d] = self.size;
|
|
411
|
+
let block_idx = (position / self.radius).as_ivec3();
|
|
412
|
+
[
|
|
413
|
+
IVec3::new(0, 0, 0),
|
|
414
|
+
IVec3::new(-1, 0, 0),
|
|
415
|
+
IVec3::new(1, 0, 0),
|
|
416
|
+
IVec3::new(0, -1, 0),
|
|
417
|
+
IVec3::new(0, 1, 0),
|
|
418
|
+
IVec3::new(0, 0, -1),
|
|
419
|
+
IVec3::new(0, 0, 1),
|
|
420
|
+
IVec3::new(-1, -1, -1),
|
|
421
|
+
IVec3::new(-1, -1, 0),
|
|
422
|
+
IVec3::new(-1, -1, 1),
|
|
423
|
+
IVec3::new(-1, 0, -1),
|
|
424
|
+
IVec3::new(-1, 0, 1),
|
|
425
|
+
IVec3::new(-1, 1, -1),
|
|
426
|
+
IVec3::new(-1, 1, 0),
|
|
427
|
+
IVec3::new(-1, 1, 1),
|
|
428
|
+
IVec3::new(0, -1, -1),
|
|
429
|
+
IVec3::new(0, -1, 1),
|
|
430
|
+
IVec3::new(0, 1, -1),
|
|
431
|
+
IVec3::new(0, 1, 1),
|
|
432
|
+
IVec3::new(1, -1, -1),
|
|
433
|
+
IVec3::new(1, -1, 0),
|
|
434
|
+
IVec3::new(1, -1, 1),
|
|
435
|
+
IVec3::new(1, 0, -1),
|
|
436
|
+
IVec3::new(1, 0, 1),
|
|
437
|
+
IVec3::new(1, 1, -1),
|
|
438
|
+
IVec3::new(1, 1, 0),
|
|
439
|
+
IVec3::new(1, 1, 1),
|
|
440
|
+
]
|
|
441
|
+
.into_iter()
|
|
442
|
+
.map(move |offset| block_idx + offset)
|
|
443
|
+
.filter(move |block_idx| {
|
|
444
|
+
(0..w as i32).contains(&block_idx.x)
|
|
445
|
+
&& (0..h as i32).contains(&block_idx.y)
|
|
446
|
+
&& (0..d as i32).contains(&block_idx.z)
|
|
447
|
+
})
|
|
448
|
+
.flat_map(move |block_idx| {
|
|
449
|
+
let [x, y, z] = block_idx.to_array().map(|v| v as usize);
|
|
450
|
+
let lin_idx = x + y * w + z * w * h;
|
|
451
|
+
&self.blocks[lin_idx]
|
|
452
|
+
})
|
|
453
|
+
.copied()
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// TODO: Reconsider whether we want to use this function or `distance_mask_grow`.
|
|
459
|
+
// TODO: Add a periodic counterpart or setting.
|
|
460
|
+
#[allow(dead_code)]
|
|
461
|
+
pub fn distance_mask(thing: &Mask, radius: f32) -> Mask {
|
|
462
|
+
let dimensions = thing.dimensions();
|
|
463
|
+
|
|
464
|
+
assert!(radius >= 0.0);
|
|
465
|
+
let positions = thing.indices_where::<false>().map(U64Vec3::from_array);
|
|
466
|
+
let thing_blocks = blocks::Blocks::from_iter(radius as u64, dimensions, positions);
|
|
467
|
+
|
|
468
|
+
let radius_squared = radius.powi(2) as i64;
|
|
469
|
+
let [w, h, d] = dimensions;
|
|
470
|
+
let start = std::time::Instant::now();
|
|
471
|
+
let slices = (0..d).into_par_iter().map(|z| {
|
|
472
|
+
let mut slice = Mask::new(dimensions);
|
|
473
|
+
for y in 0..h {
|
|
474
|
+
for x in 0..w {
|
|
475
|
+
let pos = U64Vec3::from_array([x, y, z]);
|
|
476
|
+
let posi = pos.as_i64vec3();
|
|
477
|
+
let mut nearby = thing_blocks.nearby(pos);
|
|
478
|
+
if nearby.any(|t| {
|
|
479
|
+
let distance_squared = t.as_i64vec3().distance_squared(posi);
|
|
480
|
+
distance_squared < radius_squared
|
|
481
|
+
}) {
|
|
482
|
+
let lin_idx = x + y * w + z * w * h;
|
|
483
|
+
slice.set_linear_unchecked::<true>(lin_idx as usize)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
slice
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
let mask = slices.reduce(
|
|
492
|
+
|| Mask::new(dimensions),
|
|
493
|
+
|mut acc, slice| {
|
|
494
|
+
acc |= slice;
|
|
495
|
+
acc
|
|
496
|
+
},
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
eprintln!("DISTANCE MASK took {:.3} s", start.elapsed().as_secs_f32());
|
|
500
|
+
|
|
501
|
+
mask
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// TODO: Add a periodic counterpart or setting.
|
|
505
|
+
/// Create a distance mask by growing the starting point by `radius` voxel steps.
|
|
506
|
+
pub fn distance_mask_grow(mask: &Mask, radius: u64) -> Mask {
|
|
507
|
+
let mut mask = !mask.clone();
|
|
508
|
+
|
|
509
|
+
for _ in 0..radius {
|
|
510
|
+
mask.grow_approx()
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
!mask
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
impl BitOrAssign for Mask {
|
|
517
|
+
fn bitor_assign(&mut self, rhs: Self) {
|
|
518
|
+
assert_eq!(
|
|
519
|
+
self.dimensions(),
|
|
520
|
+
rhs.dimensions(),
|
|
521
|
+
"the dimensions of both masks must be identical"
|
|
522
|
+
);
|
|
523
|
+
// For good measure, so the compiler gets it.
|
|
524
|
+
assert_eq!(self.n_backings(), rhs.n_backings()); // FIXME: Is this one necessary?
|
|
525
|
+
|
|
526
|
+
self.backings
|
|
527
|
+
.iter_mut()
|
|
528
|
+
.zip(rhs.backings.iter())
|
|
529
|
+
.for_each(|(s, &m)| *s |= m);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
impl BitAndAssign for Mask {
|
|
534
|
+
// TODO: Perhaps introduce a macro to set up functions like this?
|
|
535
|
+
fn bitand_assign(&mut self, rhs: Self) {
|
|
536
|
+
assert_eq!(
|
|
537
|
+
self.dimensions(),
|
|
538
|
+
rhs.dimensions(),
|
|
539
|
+
"the dimensions of both masks must be identical"
|
|
540
|
+
);
|
|
541
|
+
// For good measure, so the compiler gets it.
|
|
542
|
+
assert_eq!(self.n_backings(), rhs.n_backings()); // FIXME: Is this one necessary?
|
|
543
|
+
|
|
544
|
+
self.backings
|
|
545
|
+
.iter_mut()
|
|
546
|
+
.zip(rhs.backings.iter())
|
|
547
|
+
.for_each(|(s, &m)| *s &= m);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
impl Not for Mask {
|
|
552
|
+
type Output = Self;
|
|
553
|
+
|
|
554
|
+
fn not(mut self) -> Self::Output {
|
|
555
|
+
self.backings.iter_mut().for_each(|b| *b = !*b);
|
|
556
|
+
self
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
#[cfg(test)]
|
|
561
|
+
mod tests {
|
|
562
|
+
use super::*;
|
|
563
|
+
|
|
564
|
+
#[test]
|
|
565
|
+
fn create() {
|
|
566
|
+
let _ = Mask::new([10, 20, 30]);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
#[test]
|
|
570
|
+
#[should_panic]
|
|
571
|
+
fn create_zero() {
|
|
572
|
+
let _ = Mask::new([0, 0, 0]);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
#[test]
|
|
576
|
+
fn from_cells() {
|
|
577
|
+
let cells = [
|
|
578
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
579
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
580
|
+
true,
|
|
581
|
+
];
|
|
582
|
+
let _ = Mask::from_cells([3, 3, 3], &cells);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
#[test]
|
|
586
|
+
#[should_panic]
|
|
587
|
+
fn from_cells_under() {
|
|
588
|
+
let cells = [
|
|
589
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
590
|
+
true, true, false, false, true, false, false, false, true, true, true,
|
|
591
|
+
];
|
|
592
|
+
let _ = Mask::from_cells([3, 3, 3], &cells);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
#[test]
|
|
596
|
+
#[should_panic]
|
|
597
|
+
fn from_cells_over() {
|
|
598
|
+
let cells = [
|
|
599
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
600
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
601
|
+
true, true, true, false, true,
|
|
602
|
+
];
|
|
603
|
+
let _ = Mask::from_cells([3, 3, 3], &cells);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
#[test]
|
|
607
|
+
fn dimensions() {
|
|
608
|
+
let dimensions = [5172, 1312, 161];
|
|
609
|
+
let mask = Mask::new(dimensions);
|
|
610
|
+
assert_eq!(mask.dimensions(), dimensions);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
#[test]
|
|
614
|
+
fn count() {
|
|
615
|
+
let mut mask = Mask::new([172, 1312, 161]);
|
|
616
|
+
|
|
617
|
+
mask.set([123, 456, 78], true);
|
|
618
|
+
mask.set([123, 784, 56], true);
|
|
619
|
+
mask.set([78, 1236, 45], true);
|
|
620
|
+
|
|
621
|
+
assert_eq!(mask.count::<true>(), 3);
|
|
622
|
+
assert_eq!(mask.count::<false>(), mask.n_cells() - mask.count::<true>());
|
|
623
|
+
|
|
624
|
+
mask.set([78, 1236, 45], false);
|
|
625
|
+
assert_eq!(mask.count::<true>(), 2);
|
|
626
|
+
assert_eq!(mask.count::<false>(), mask.n_cells() - mask.count::<true>());
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
#[test]
|
|
630
|
+
fn n_cells() {
|
|
631
|
+
let mask = Mask::new([123, 456, 789]);
|
|
632
|
+
assert_eq!(mask.n_cells(), 123 * 456 * 789);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
#[test]
|
|
636
|
+
fn n_backings() {
|
|
637
|
+
let mask = Mask::new([123, 456, 789]);
|
|
638
|
+
assert_eq!(mask.n_backings(), mask.n_cells().div_ceil(BACKING_BITS));
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
#[test]
|
|
642
|
+
fn backing_idx() {
|
|
643
|
+
let mask = Mask::new([123, 456, 789]);
|
|
644
|
+
assert_eq!(mask.backing_idx(0), (0, 0));
|
|
645
|
+
assert_eq!(mask.backing_idx(1), (0, 1));
|
|
646
|
+
assert_eq!(mask.backing_idx(2), (0, 2));
|
|
647
|
+
assert_eq!(mask.backing_idx(8), (8 / BACKING_BITS, 8 % BACKING_BITS));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
#[test]
|
|
651
|
+
fn contains() {
|
|
652
|
+
let mask = Mask::new([123, 456, 789]);
|
|
653
|
+
assert!(!mask.contains([123, 456, 789]));
|
|
654
|
+
assert!(!mask.contains([122, 456, 789]));
|
|
655
|
+
assert!(!mask.contains([122, 455, 789]));
|
|
656
|
+
assert!(mask.contains([122, 455, 788]));
|
|
657
|
+
assert!(mask.contains([0, 0, 0]));
|
|
658
|
+
assert!(!mask.contains([12345, 0, 0]));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
#[test]
|
|
662
|
+
fn spatial_idx() {
|
|
663
|
+
let mask = Mask::new([100, 100, 100]);
|
|
664
|
+
assert_eq!(mask.spatial_idx(0), Some([0, 0, 0]));
|
|
665
|
+
assert_eq!(mask.spatial_idx(99), Some([99, 0, 0]));
|
|
666
|
+
assert_eq!(mask.spatial_idx(100), Some([0, 1, 0]));
|
|
667
|
+
assert_eq!(mask.spatial_idx(101), Some([1, 1, 0]));
|
|
668
|
+
assert_eq!(mask.spatial_idx(1000000 - 1), Some([99, 99, 99]));
|
|
669
|
+
assert!(mask.spatial_idx(1000000).is_none());
|
|
670
|
+
assert!(mask.spatial_idx(12345678).is_none());
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
#[test]
|
|
674
|
+
fn linear_idx() {
|
|
675
|
+
let mask = Mask::new([100, 100, 100]);
|
|
676
|
+
assert_eq!(mask.linear_idx([0, 0, 0]), 0);
|
|
677
|
+
assert_eq!(mask.linear_idx([99, 0, 0]), 99);
|
|
678
|
+
assert_eq!(mask.linear_idx([0, 1, 0]), 100);
|
|
679
|
+
assert_eq!(mask.linear_idx([1, 1, 0]), 101);
|
|
680
|
+
assert_eq!(mask.linear_idx([99, 99, 99]), 1000000 - 1);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
#[test]
|
|
684
|
+
#[should_panic]
|
|
685
|
+
fn linear_idx_panic() {
|
|
686
|
+
let mask = Mask::new([100, 100, 100]);
|
|
687
|
+
assert_eq!(mask.linear_idx([100, 100, 100]), 1000000); // FIXME: Reconsider this behavior.
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
#[test]
|
|
691
|
+
fn query_linear_unchecked() {
|
|
692
|
+
let cells = [
|
|
693
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
694
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
695
|
+
true,
|
|
696
|
+
];
|
|
697
|
+
let mask = Mask::from_cells([3, 3, 3], &cells);
|
|
698
|
+
|
|
699
|
+
assert!(mask.query_linear_unchecked::<false>(0));
|
|
700
|
+
assert!(mask.query_linear_unchecked::<true>(13));
|
|
701
|
+
assert!(mask.query_linear_unchecked::<false>(7));
|
|
702
|
+
assert!(mask.query_linear_unchecked::<true>(12));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
#[test]
|
|
706
|
+
fn set_linear_unchecked() {
|
|
707
|
+
let cells = [
|
|
708
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
709
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
710
|
+
true,
|
|
711
|
+
];
|
|
712
|
+
let mut mask = Mask::from_cells([3, 3, 3], &cells);
|
|
713
|
+
|
|
714
|
+
// Was false, set false.
|
|
715
|
+
assert!(mask.query_linear_unchecked::<false>(0));
|
|
716
|
+
mask.set_linear_unchecked::<false>(0);
|
|
717
|
+
assert!(mask.query_linear_unchecked::<false>(0));
|
|
718
|
+
|
|
719
|
+
// Was true, set false.
|
|
720
|
+
assert!(mask.query_linear_unchecked::<true>(13));
|
|
721
|
+
mask.set_linear_unchecked::<false>(13);
|
|
722
|
+
assert!(mask.query_linear_unchecked::<false>(13));
|
|
723
|
+
|
|
724
|
+
// Was false, set true.
|
|
725
|
+
assert!(mask.query_linear_unchecked::<false>(8));
|
|
726
|
+
mask.set_linear_unchecked::<true>(8);
|
|
727
|
+
assert!(mask.query_linear_unchecked::<true>(8));
|
|
728
|
+
|
|
729
|
+
// Was true, set true.
|
|
730
|
+
assert!(mask.query_linear_unchecked::<true>(12));
|
|
731
|
+
mask.set_linear_unchecked::<true>(12);
|
|
732
|
+
assert!(mask.query_linear_unchecked::<true>(12));
|
|
733
|
+
|
|
734
|
+
// Was true, set false.
|
|
735
|
+
assert!(mask.query_linear_unchecked::<true>(17));
|
|
736
|
+
mask.set_linear_unchecked::<false>(17);
|
|
737
|
+
assert!(mask.query_linear_unchecked::<false>(17));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
#[test]
|
|
741
|
+
fn get() {
|
|
742
|
+
let cells = [
|
|
743
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
744
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
745
|
+
true,
|
|
746
|
+
];
|
|
747
|
+
let mut mask = Mask::from_cells([3, 3, 3], &cells);
|
|
748
|
+
|
|
749
|
+
assert_eq!(mask.get([0, 0, 0]), Some(false));
|
|
750
|
+
assert_eq!(mask.get([1, 1, 1]), Some(true));
|
|
751
|
+
assert_eq!(mask.get([2, 2, 2]), Some(true));
|
|
752
|
+
assert_eq!(mask.get([2, 2, 3]), None);
|
|
753
|
+
assert_eq!(mask.get([3, 3, 3]), None);
|
|
754
|
+
assert_eq!(mask.get([2, 4, 2]), None);
|
|
755
|
+
|
|
756
|
+
mask.set([1, 1, 1], true);
|
|
757
|
+
assert_eq!(mask.get([1, 1, 1]), Some(true));
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
#[test]
|
|
761
|
+
fn set() {
|
|
762
|
+
let cells = [
|
|
763
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
764
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
765
|
+
true,
|
|
766
|
+
];
|
|
767
|
+
let mut mask = Mask::from_cells([3, 3, 3], &cells);
|
|
768
|
+
|
|
769
|
+
// Was false, set false.
|
|
770
|
+
assert_eq!(mask.get([0, 0, 0]), Some(false));
|
|
771
|
+
mask.set([0, 0, 0], false);
|
|
772
|
+
assert_eq!(mask.get([0, 0, 0]), Some(false));
|
|
773
|
+
|
|
774
|
+
// Was true, set true.
|
|
775
|
+
assert_eq!(mask.get([0, 1, 0]), Some(true));
|
|
776
|
+
mask.set([0, 1, 0], true);
|
|
777
|
+
assert_eq!(mask.get([0, 1, 0]), Some(true));
|
|
778
|
+
|
|
779
|
+
// Was false, set true.
|
|
780
|
+
assert_eq!(mask.get([1, 2, 0]), Some(false));
|
|
781
|
+
mask.set([1, 2, 0], true);
|
|
782
|
+
assert_eq!(mask.get([1, 2, 0]), Some(true));
|
|
783
|
+
|
|
784
|
+
// Was true, set false.
|
|
785
|
+
assert_eq!(mask.get([2, 2, 2]), Some(true));
|
|
786
|
+
mask.set([2, 2, 2], false);
|
|
787
|
+
assert_eq!(mask.get([2, 2, 2]), Some(false));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
#[test]
|
|
791
|
+
fn indices_where() {
|
|
792
|
+
let cells = [
|
|
793
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
794
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
795
|
+
true,
|
|
796
|
+
];
|
|
797
|
+
let mask = Mask::from_cells([3, 3, 3], &cells);
|
|
798
|
+
|
|
799
|
+
let where_false = [
|
|
800
|
+
[0, 0, 0],
|
|
801
|
+
[1, 0, 0],
|
|
802
|
+
[2, 0, 0],
|
|
803
|
+
[0, 2, 0],
|
|
804
|
+
[1, 2, 0],
|
|
805
|
+
[2, 2, 0],
|
|
806
|
+
[0, 0, 1],
|
|
807
|
+
[2, 0, 1],
|
|
808
|
+
[0, 2, 1],
|
|
809
|
+
[1, 2, 1],
|
|
810
|
+
[0, 0, 2],
|
|
811
|
+
[1, 0, 2],
|
|
812
|
+
[2, 0, 2],
|
|
813
|
+
];
|
|
814
|
+
|
|
815
|
+
assert_eq!(
|
|
816
|
+
mask.indices_where::<false>().collect::<Vec<_>>(),
|
|
817
|
+
where_false
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
let where_true = [
|
|
821
|
+
[0, 1, 0],
|
|
822
|
+
[1, 1, 0],
|
|
823
|
+
[2, 1, 0],
|
|
824
|
+
[1, 0, 1],
|
|
825
|
+
[0, 1, 1],
|
|
826
|
+
[1, 1, 1],
|
|
827
|
+
[2, 1, 1],
|
|
828
|
+
[2, 2, 1],
|
|
829
|
+
[0, 1, 2],
|
|
830
|
+
[1, 1, 2],
|
|
831
|
+
[2, 1, 2],
|
|
832
|
+
[0, 2, 2],
|
|
833
|
+
[1, 2, 2],
|
|
834
|
+
[2, 2, 2],
|
|
835
|
+
];
|
|
836
|
+
|
|
837
|
+
assert_eq!(mask.indices_where::<true>().collect::<Vec<_>>(), where_true);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
#[test]
|
|
841
|
+
fn linear_indices_where() {
|
|
842
|
+
let cells = [
|
|
843
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
844
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
845
|
+
true,
|
|
846
|
+
];
|
|
847
|
+
let mask = Mask::from_cells([3, 3, 3], &cells);
|
|
848
|
+
|
|
849
|
+
let where_false = [0, 1, 2, 6, 7, 8, 9, 11, 15, 16, 18, 19, 20];
|
|
850
|
+
|
|
851
|
+
assert_eq!(
|
|
852
|
+
mask.linear_indices_where::<false>().collect::<Vec<_>>(),
|
|
853
|
+
where_false
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
let where_true = [3, 4, 5, 10, 12, 13, 14, 17, 21, 22, 23, 24, 25, 26];
|
|
857
|
+
|
|
858
|
+
assert_eq!(
|
|
859
|
+
mask.linear_indices_where::<true>().collect::<Vec<_>>(),
|
|
860
|
+
where_true
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
#[test]
|
|
865
|
+
fn apply_mask() {
|
|
866
|
+
let cells = [
|
|
867
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
868
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
869
|
+
true,
|
|
870
|
+
];
|
|
871
|
+
let mut base = Mask::from_cells([3, 3, 3], &cells);
|
|
872
|
+
assert!(base.query_linear_unchecked::<false>(0));
|
|
873
|
+
assert!(base.query_linear_unchecked::<false>(1));
|
|
874
|
+
|
|
875
|
+
let mut mask = base.clone();
|
|
876
|
+
mask.set([1, 0, 0], true);
|
|
877
|
+
assert!(mask.query_linear_unchecked::<false>(0));
|
|
878
|
+
assert!(mask.query_linear_unchecked::<true>(1));
|
|
879
|
+
|
|
880
|
+
// Mask the base.
|
|
881
|
+
base.apply_mask(&mask);
|
|
882
|
+
// And now base should have that same bit set and be identical aside from that.
|
|
883
|
+
assert!(base.query_linear_unchecked::<false>(0));
|
|
884
|
+
assert!(base.query_linear_unchecked::<true>(1));
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
#[test]
|
|
888
|
+
fn merge_mask() {
|
|
889
|
+
let cells = [
|
|
890
|
+
false, false, false, true, true, true, false, false, false, false, true, false, true,
|
|
891
|
+
true, true, false, false, true, false, false, false, true, true, true, true, true,
|
|
892
|
+
true,
|
|
893
|
+
];
|
|
894
|
+
let mut base = Mask::from_cells([3, 3, 3], &cells);
|
|
895
|
+
assert!(base.query_linear_unchecked::<false>(0));
|
|
896
|
+
assert!(base.query_linear_unchecked::<false>(1));
|
|
897
|
+
|
|
898
|
+
let mut mask = base.clone();
|
|
899
|
+
mask.set([1, 0, 0], true);
|
|
900
|
+
assert!(mask.query_linear_unchecked::<false>(0));
|
|
901
|
+
assert!(mask.query_linear_unchecked::<true>(1));
|
|
902
|
+
|
|
903
|
+
// Mask the base.
|
|
904
|
+
base.merge_mask(&mask);
|
|
905
|
+
// And now base and mask should be identical again.
|
|
906
|
+
assert!(base.query_linear_unchecked::<false>(0));
|
|
907
|
+
assert!(base.query_linear_unchecked::<false>(1));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
#[test]
|
|
911
|
+
fn grow() {
|
|
912
|
+
let mut cells = [false; 27];
|
|
913
|
+
cells[cells.len() / 2] = true; // Set the center cell.
|
|
914
|
+
let mut mask = Mask::from_cells([3, 3, 3], &cells);
|
|
915
|
+
|
|
916
|
+
assert_eq!(mask.count::<true>(), 1);
|
|
917
|
+
|
|
918
|
+
// After this, we should have a 3d cross.
|
|
919
|
+
mask.grow_approx();
|
|
920
|
+
assert_eq!(mask.count::<true>(), 1 + 6);
|
|
921
|
+
assert_eq!(mask.get([0, 0, 0]), Some(false));
|
|
922
|
+
assert_eq!(mask.get([1, 0, 0]), Some(false));
|
|
923
|
+
assert_eq!(mask.get([2, 0, 0]), Some(false));
|
|
924
|
+
assert_eq!(mask.get([0, 1, 0]), Some(false));
|
|
925
|
+
assert_eq!(mask.get([1, 1, 0]), Some(true));
|
|
926
|
+
assert_eq!(mask.get([2, 1, 0]), Some(false));
|
|
927
|
+
assert_eq!(mask.get([0, 1, 1]), Some(true));
|
|
928
|
+
assert_eq!(mask.get([1, 1, 1]), Some(true));
|
|
929
|
+
assert_eq!(mask.get([2, 1, 1]), Some(true));
|
|
930
|
+
assert_eq!(mask.get([2, 2, 2]), Some(false));
|
|
931
|
+
|
|
932
|
+
// Now we have a cube where only the corners are not occupied.
|
|
933
|
+
mask.grow_approx();
|
|
934
|
+
assert_eq!(mask.count::<true>(), mask.n_cells() - 8);
|
|
935
|
+
|
|
936
|
+
// Finally, this will fill up the whole mask.
|
|
937
|
+
mask.grow_approx();
|
|
938
|
+
assert_eq!(mask.count::<true>(), mask.n_cells());
|
|
939
|
+
}
|
|
940
|
+
}
|