Skip to content

Commit c3b220d

Browse files
committed
Add GridAab::shrink(); make GridAab::expand() non-panicking.
1 parent 8f5a16f commit c3b220d

File tree

15 files changed

+143
-89
lines changed

15 files changed

+143
-89
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
- `block::EvalBlockError` is now a `struct` with an inner `ErrorKind` enum, instead of an enum, and contains more information.
2020
- `block::Move`’s means of construction have been changed to be more systematic and orthogonal. In particular, paired moves are constructed from unpaired ones.
2121

22+
- `math::GridAab::expand()` now takes unsigned values; use `GridAab::shrink()` instead of negative ones. This allows both versions to never panic.
23+
2224
- `all-is-cubes-gpu` library:
2325
- `in_wgpu::SurfaceRenderer::new()` requires `wgpu::Adapter` instead of `&wgpu::Adapter`.
2426

all-is-cubes-base/src/math/grid_aab.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -772,10 +772,9 @@ impl GridAab {
772772
Self::from_lower_upper(self.lower_bounds * scale, self.upper_bounds * scale)
773773
}
774774

775-
/// Moves all bounds outward or inward by the specified distances.
775+
/// Moves all bounds outward by the specified distances.
776776
///
777-
/// TODO: Currently this will panic if the result is empty. Make it return Option
778-
/// instead.
777+
/// If the result’s coordinates would overflow, they are as large as possible instead.
779778
///
780779
/// ```
781780
/// # extern crate all_is_cubes_base as all_is_cubes;
@@ -791,16 +790,59 @@ impl GridAab {
791790
/// );
792791
/// ```
793792
#[inline]
794-
#[track_caller] // TODO: better error reporting
795793
#[must_use]
796-
pub fn expand(self, deltas: FaceMap<GridCoordinate>) -> Self {
797-
use Face6::*;
794+
pub fn expand(self, deltas: FaceMap<GridSizeCoord>) -> Self {
798795
let l = self.lower_bounds();
799796
let u = self.upper_bounds();
800797
Self::from_lower_upper(
801-
[l.x - deltas[NX], l.y - deltas[NY], l.z - deltas[NZ]],
802-
[u.x + deltas[PX], u.y + deltas[PY], u.z + deltas[PZ]],
798+
[
799+
l.x.saturating_sub_unsigned(deltas.nx),
800+
l.y.saturating_sub_unsigned(deltas.ny),
801+
l.z.saturating_sub_unsigned(deltas.nz),
802+
],
803+
[
804+
u.x.saturating_add_unsigned(deltas.px),
805+
u.y.saturating_add_unsigned(deltas.py),
806+
u.z.saturating_add_unsigned(deltas.pz),
807+
],
808+
)
809+
}
810+
811+
/// Moves all bounds inward by the specified distances.
812+
///
813+
/// Returns `None` if the result would have less than zero size.
814+
///
815+
/// ```
816+
/// # extern crate all_is_cubes_base as all_is_cubes;
817+
/// use all_is_cubes::math::{GridAab, FaceMap};
818+
///
819+
/// assert_eq!(
820+
/// GridAab::from_lower_upper([10, 10, 10], [20, 20, 20])
821+
/// .shrink(FaceMap {
822+
/// nx: 1, ny: 2, nz: 3,
823+
/// px: 4, py: 5, pz: 6,
824+
/// }),
825+
/// Some(GridAab::from_lower_upper([11, 12, 13], [16, 15, 14])),
826+
/// );
827+
/// ```
828+
#[inline]
829+
#[must_use]
830+
pub fn shrink(self, deltas: FaceMap<GridSizeCoord>) -> Option<Self> {
831+
let l = self.lower_bounds();
832+
let u = self.upper_bounds();
833+
Self::checked_from_lower_upper(
834+
[
835+
l.x.checked_add_unsigned(deltas.nx)?,
836+
l.y.checked_add_unsigned(deltas.ny)?,
837+
l.z.checked_add_unsigned(deltas.nz)?,
838+
],
839+
[
840+
u.x.checked_sub_unsigned(deltas.px)?,
841+
u.y.checked_sub_unsigned(deltas.py)?,
842+
u.z.checked_sub_unsigned(deltas.pz)?,
843+
],
803844
)
845+
.ok()
804846
}
805847

806848
/// Returns a [`GridAab`] which includes the volume between the given `face` rectangle

all-is-cubes-content/src/alg.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use all_is_cubes::block::{Atom, Block, Primitive, Resolution, AIR};
1111
use all_is_cubes::euclid::vec3;
1212
use all_is_cubes::math::{
1313
Cube, CubeFace, Face6, FaceMap, FreeCoordinate, FreePoint, GridAab, GridCoordinate, GridPoint,
14-
GridVector, Gridgid, NotNan, Vol,
14+
GridSizeCoord, GridVector, Gridgid, NotNan, Vol,
1515
};
1616
use all_is_cubes::space::{CubeTransaction, SetCubeError, Space, SpaceTransaction};
1717

@@ -102,12 +102,12 @@ pub(crate) fn voronoi_pattern<'a>(
102102
/// TODO: Change the callback value to a struct
103103
pub(crate) fn four_walls<F, E>(bounding_box: GridAab, mut f: F) -> Result<(), E>
104104
where
105-
F: FnMut(GridPoint, Face6, GridCoordinate, GridAab) -> Result<(), E>,
105+
F: FnMut(GridPoint, Face6, GridSizeCoord, GridAab) -> Result<(), E>,
106106
{
107-
let interior = bounding_box.expand(FaceMap::symmetric([-1, 0, -1]));
107+
let interior = bounding_box.shrink(FaceMap::symmetric([1, 0, 1])).unwrap();
108108
let low = bounding_box.lower_bounds();
109109
let high = bounding_box.upper_bounds() - GridVector::new(1, 1, 1);
110-
let size = bounding_box.size().to_i32();
110+
let size = bounding_box.size();
111111
f(
112112
low,
113113
Face6::PZ,

all-is-cubes-content/src/atrium.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use all_is_cubes::euclid::Point3D;
1515
use all_is_cubes::linking::{BlockModule, BlockProvider, InGenError};
1616
use all_is_cubes::math::{
1717
rgb_const, Axis, Cube, Face6, FaceMap, FreeCoordinate, GridAab, GridCoordinate, GridPoint,
18-
GridRotation, GridVector, Gridgid, Rgb, Rgba, Vol,
18+
GridRotation, GridSizeCoord, GridVector, Gridgid, Rgb, Rgba, Vol,
1919
};
2020
use all_is_cubes::space::{SetCubeError, Space, SpacePhysics, SpaceTransaction};
2121
use all_is_cubes::transaction::{self, Transaction as _};
@@ -26,7 +26,8 @@ use crate::alg::{array_of_noise, four_walls, scale_color};
2626
use crate::Fire;
2727

2828
/// A special name for "the thickness of a 1-block-thick wall/floor/pillar", for readability.
29-
const WALL: GridCoordinate = 1;
29+
const IWALL: GridCoordinate = 1;
30+
const UWALL: GridSizeCoord = 1;
3031

3132
pub(crate) async fn atrium(
3233
universe: &mut Universe,
@@ -40,31 +41,31 @@ pub(crate) async fn atrium(
4041
blocks
4142
};
4243

43-
let ceiling_height = 6;
44-
let between_small_arches = 3;
44+
let ceiling_height = 6u32;
45+
let between_small_arches = 3u32;
4546
let between_large_arches = between_small_arches * 2 + 1;
4647
let balcony_radius = 4;
4748
let large_arch_count_x = 1;
4849
let large_arch_count_z = 5;
4950
let floor_count = 4;
5051
let sun_height = 10;
5152

52-
let origin = GridAab::from_lower_size([0, 0, 0], [1, 1, 1]);
53+
let origin = GridAab::ORIGIN_CUBE;
5354
let atrium_footprint = origin.expand(FaceMap::symmetric([
54-
((between_large_arches + WALL) * large_arch_count_x) / 2 - WALL,
55+
((between_large_arches + UWALL) * large_arch_count_x) / 2 - UWALL,
5556
0,
56-
((between_large_arches + WALL) * large_arch_count_z) / 2 - WALL,
57+
((between_large_arches + UWALL) * large_arch_count_z) / 2 - UWALL,
5758
]));
58-
let arches_footprint = atrium_footprint.expand(FaceMap::symmetric([WALL, 0, WALL]));
59+
let arches_footprint = atrium_footprint.expand(FaceMap::symmetric([UWALL, 0, UWALL]));
5960
let balconies_footprint =
6061
arches_footprint.expand(FaceMap::symmetric([balcony_radius, 0, balcony_radius]));
61-
let outer_walls_footprint = balconies_footprint.expand(FaceMap::symmetric([WALL, 0, WALL]));
62+
let outer_walls_footprint = balconies_footprint.expand(FaceMap::symmetric([UWALL, 0, UWALL]));
6263

63-
let balcony_floor_pos = GridVector::new(0, ceiling_height + WALL, 0);
64-
let top_floor_pos = GridVector::new(0, (ceiling_height + WALL) * 2, 0);
64+
let balcony_floor_pos = GridVector::new(0, (ceiling_height + UWALL) as i32, 0);
65+
let top_floor_pos = GridVector::new(0, (ceiling_height + UWALL) as i32 * 2, 0);
6566

6667
let space_bounds = outer_walls_footprint
67-
.expand(FaceMap::default().with(Face6::PY, ceiling_height * floor_count + sun_height));
68+
.expand(FaceMap::default().with(Face6::PY, sun_height + ceiling_height * floor_count));
6869

6970
let floor_with_cutout = |mut cube: Cube| {
7071
cube.y = 0;
@@ -134,7 +135,7 @@ pub(crate) async fn atrium(
134135
outer_walls_footprint
135136
.translate(top_floor_pos)
136137
.expand(FaceMap::from_fn(|f| {
137-
GridCoordinate::from(f == Face6::PY) * ceiling_height
138+
GridSizeCoord::from(f == Face6::PY) * ceiling_height
138139
})),
139140
floor_with_cutout,
140141
)?;
@@ -206,14 +207,14 @@ pub(crate) async fn atrium(
206207
*br" ",
207208
]]);
208209
four_walls(
209-
arches_footprint.translate([0, WALL, 0]),
210+
arches_footprint.translate([0, IWALL, 0]),
210211
|origin, direction, length, _box| {
211212
arch_row(
212213
&mut space,
213214
&blocks,
214215
origin,
215-
between_large_arches + WALL,
216-
length / (between_large_arches + WALL),
216+
between_large_arches + UWALL,
217+
length / (between_large_arches + UWALL),
217218
direction,
218219
arches_pattern.as_ref(),
219220
)
@@ -331,14 +332,14 @@ fn arch_row(
331332
space: &mut Space,
332333
blocks: &BlockProvider<AtriumBlocks>,
333334
first_column_base: GridPoint,
334-
section_length: GridCoordinate,
335-
section_count: GridCoordinate,
335+
section_length: GridSizeCoord,
336+
section_count: GridSizeCoord,
336337
parallel: Face6,
337338
pattern: Vol<&[u8]>,
338339
) -> Result<(), InGenError> {
339-
let offset = parallel.normal_vector() * section_length;
340+
let offset = parallel.normal_vector() * section_length as GridCoordinate;
340341
let rotation = GridRotation::from_to(Face6::NX, parallel, Face6::PY).unwrap();
341-
for i in 0..section_count {
342+
for i in 0..(section_count as GridCoordinate) {
342343
let column_base = first_column_base + offset * (i + 1);
343344

344345
let banner_color = if parallel.axis() == Axis::Z {

all-is-cubes-content/src/city.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use all_is_cubes::drawing::VoxelBrush;
1515
use all_is_cubes::inv::{Slot, Tool};
1616
use all_is_cubes::linking::{BlockProvider, InGenError};
1717
use all_is_cubes::math::{
18-
rgba_const, Cube, Face6, FaceMap, GridAab, GridCoordinate, GridRotation, GridSize, GridVector,
19-
Gridgid, VectorOps,
18+
rgba_const, Cube, Face6, FaceMap, GridAab, GridCoordinate, GridRotation, GridSize,
19+
GridSizeCoord, GridVector, Gridgid, VectorOps,
2020
};
2121
use all_is_cubes::op::Operation;
2222
use all_is_cubes::space::{LightPhysics, Space, SpaceBuilder, SpacePhysics};
@@ -491,18 +491,16 @@ fn place_one_exhibit<I: Instant>(
491491
let front_face = plot_transform.rotation.transform(Face6::PZ);
492492
// Compute the surface that needs to be clear for walking
493493
let entrance_plane = enclosure_lower
494-
.expand(
495-
FaceMap {
496-
// don't alter y
497-
ny: 0,
498-
py: 0,
499-
..enclosure_thickness
500-
}
501-
.map(|_, thick| -thick),
502-
)
494+
.shrink(FaceMap {
495+
// don't alter y
496+
ny: 0,
497+
py: 0,
498+
..enclosure_thickness
499+
})
500+
.unwrap()
503501
.abut(Face6::PY, 0)
504502
.unwrap()
505-
.abut(front_face, enclosure_thickness.pz) // re-add enclosure bounds
503+
.abut(front_face, enclosure_thickness.pz as GridCoordinate) // re-add enclosure bounds
506504
.unwrap()
507505
.abut(
508506
front_face,
@@ -839,7 +837,7 @@ impl CityPlanner {
839837
/// Distance from the center cube to the line of the front of each placed exhibit.
840838
/// TODO: The units of this isn't being consistent, since it is actually + 1 from the lamps
841839
const PLOT_FRONT_RADIUS: GridCoordinate = Self::LAMP_POSITION_RADIUS;
842-
const GAP_BETWEEN_PLOTS: GridCoordinate = 1;
840+
const GAP_BETWEEN_PLOTS: GridSizeCoord = 1;
843841

844842
const SURFACE_Y: GridCoordinate = 1;
845843
const UNDERGROUND_FLOOR_Y: GridCoordinate = -10;

all-is-cubes-content/src/city/exhibits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,9 +1126,9 @@ fn COLOR_LIGHTS(_: Context<'_>) {
11261126
fn COLORED_BOUNCE(_: Context<'_>) {
11271127
let mut txn = ExhibitTransaction::default();
11281128

1129-
let interior_radius = 3;
1130-
let wall_thickness = 3;
1131-
let total_radius = interior_radius + wall_thickness;
1129+
let interior_radius = 3i32;
1130+
let wall_thickness = 3u32;
1131+
let total_radius = interior_radius.saturating_add_unsigned(wall_thickness);
11321132
let brightness = 50.0;
11331133

11341134
// --- Blocks ---
@@ -1505,7 +1505,7 @@ fn TREES(ctx: Context<'_>) {
15051505
ny: 0,
15061506
nz: 2,
15071507
px: 2,
1508-
py: ix + iz * 2,
1508+
py: u32::try_from(ix + iz * 2).unwrap(),
15091509
pz: 2,
15101510
}),
15111511
)

all-is-cubes-content/src/dungeon/demo_dungeon.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use all_is_cubes::euclid::Size3D;
1616
use all_is_cubes::inv::Tool;
1717
use all_is_cubes::linking::{BlockModule, BlockProvider, GenError, InGenError};
1818
use all_is_cubes::math::{
19-
Axis, Cube, Face6, FaceMap, GridAab, GridCoordinate, GridRotation, GridSize, GridVector, Rgb,
20-
Rgba, Vol,
19+
Axis, Cube, Face6, FaceMap, GridAab, GridCoordinate, GridRotation, GridSize, GridSizeCoord,
20+
GridVector, Rgb, Rgba, Vol,
2121
};
2222
use all_is_cubes::space::{LightPhysics, Space};
2323
use all_is_cubes::time;
@@ -313,7 +313,7 @@ impl Theme<Option<DemoRoom>> for DemoTheme {
313313
|origin, along_wall, length, wall_excluding_corners_box| {
314314
let wall = GridRotation::CLOCKWISE.transform(along_wall); // TODO: make four_walls provide this in a nice name
315315
if let WallFeature::Window = room_data.wall_features[wall] {
316-
let midpoint = length / 2;
316+
let midpoint = (length / 2) as GridCoordinate;
317317
for step in WINDOW_PATTERN {
318318
let mut window_pos =
319319
origin + along_wall.normal_vector() * (midpoint + step);
@@ -414,11 +414,11 @@ pub(crate) async fn demo_dungeon(
414414
room_wall_thickness: FaceMap::repeat(1),
415415
gap_between_walls: Size3D::new(1, 1, 1),
416416
};
417-
let perimeter_margin: GridCoordinate = 30;
417+
let perimeter_margin: GridSizeCoord = 30;
418418

419419
let mut requested_rooms = Size3D::from(
420420
(requested_size.unwrap_or(Size3D::new(135, 40, 135))
421-
- Size3D::new(perimeter_margin, 0, perimeter_margin).to_u32())
421+
- Size3D::new(perimeter_margin, 0, perimeter_margin))
422422
.to_vector()
423423
.component_div(dungeon_grid.room_spacing().to_vector()),
424424
);

all-is-cubes-content/src/dungeon/generic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl DungeonGrid {
4949

5050
pub fn room_box_including_walls(&self) -> GridAab {
5151
self.room_box
52-
.expand(self.room_wall_thickness.map(|_, c| GridCoordinate::from(c)))
52+
.expand(self.room_wall_thickness.map(|_, c| c.into()))
5353
}
5454

5555
pub fn room_box_at(&self, room_position: Cube) -> GridAab {

all-is-cubes-content/src/template.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,9 @@ async fn islands(
369369
&& cell_bounds.size().height >= margin + 25
370370
&& cell_bounds.size().depth >= margin * 2
371371
{
372-
let occupied_bounds = cell_bounds.expand(FaceMap::repeat(-10).with(Face6::PY, -25));
372+
let occupied_bounds = cell_bounds
373+
.shrink(FaceMap::repeat(10).with(Face6::PY, 25))
374+
.unwrap();
373375
wavy_landscape(occupied_bounds, &mut space, &landscape_blocks, 0.5)?;
374376
}
375377
p.progress(i as f32 / island_grid.volume_f64() as f32).await;

all-is-cubes-mesh/examples/visualize-block-mesh.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ fn make_transparent_block(universe: &mut Universe) -> Block {
9191
.light_emission(Rgb::new(0.0, 1.0, 1.0))
9292
.build();
9393
let resolution = Resolution::R16;
94-
let solid_box = GridAab::for_block(resolution).expand(FaceMap::repeat(-2));
94+
let solid_box = GridAab::for_block(resolution)
95+
.shrink(FaceMap::repeat(2))
96+
.unwrap();
9597
let transparent_box = GridAab::for_block(resolution).abut(Face6::PX, -4).unwrap();
9698
let emissive_box = GridAab::for_block(resolution).abut(Face6::NX, -4).unwrap();
9799
Block::builder()

0 commit comments

Comments
 (0)