Skip to content

Commit bd1d1db

Browse files
committed
Unit testing rotation code (success)
1 parent f50e125 commit bd1d1db

File tree

8 files changed

+320
-57
lines changed

8 files changed

+320
-57
lines changed

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ dependencies {
106106
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
107107
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
108108

109+
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
110+
109111
// JEI
110112
// compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}:api")
111113
// runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
@@ -147,4 +149,8 @@ publishing {
147149
url "file:///${project.projectDir}/mcmodsrepo"
148150
}
149151
}
152+
}
153+
154+
test {
155+
useJUnitPlatform()
150156
}

src/main/java/com/robotgryphon/compactcrafting/blocks/FieldProjectorTile.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.robotgryphon.compactcrafting.crafting.CraftingHelper;
55
import com.robotgryphon.compactcrafting.field.FieldProjection;
66
import com.robotgryphon.compactcrafting.field.FieldProjectionSize;
7+
import com.robotgryphon.compactcrafting.field.MiniaturizationFieldBlockData;
78
import com.robotgryphon.compactcrafting.field.ProjectorHelper;
89
import com.robotgryphon.compactcrafting.recipes.MiniaturizationRecipe;
910
import com.robotgryphon.compactcrafting.util.BlockSpaceUtil;
@@ -190,12 +191,12 @@ public void doRecipeScan() {
190191
return;
191192

192193
AxisAlignedBB fieldBounds = field.getBounds();
193-
BlockPos[] nonAirPositions = BlockPos.getAllInBox(fieldBounds)
194-
.filter(p -> !world.isAirBlock(p))
195-
.map(BlockPos::toImmutable)
196-
.toArray(BlockPos[]::new);
197194

198-
AxisAlignedBB filledBounds = BlockSpaceUtil.getBoundsForBlocks(nonAirPositions);
195+
MiniaturizationFieldBlockData fieldBlocks = MiniaturizationFieldBlockData.getFromField(world, fieldBounds);
196+
197+
// If no positions filled, exit early
198+
if(fieldBlocks.getNumberFilledBlocks() == 0)
199+
return;
199200

200201
// ===========================================================================================================
201202
// RECIPE BEGIN
@@ -204,26 +205,18 @@ public void doRecipeScan() {
204205
.stream()
205206
.map(RegistryObject::get)
206207
.filter(recipe -> recipe.fitsInFieldSize(size))
207-
.filter(recipe -> BlockSpaceUtil.boundsFitsInside(filledBounds, recipe.getDimensions()))
208+
.filter(recipe -> BlockSpaceUtil.boundsFitsInside(fieldBlocks.getFilledBounds(), recipe.getDimensions()))
208209
.collect(Collectors.toSet());
209210

210211
// All the recipes we have registered won't fit in the filled bounds -
211212
// blocks were placed in a larger space than the max recipe size
212213
if(recipesBoundFitted.size() == 0)
213214
return;
214215

215-
// BlockPos[] testLocations = new BlockPos[] {
216-
// new BlockPos(5, 0, 5),
217-
// new BlockPos(7, 0, 5),
218-
// new BlockPos(5, 0, 7)
219-
// };
220-
//
221-
// BlockPos[] rotatedCW = BlockSpaceUtil.rotateLayerPositions(testLocations, new Vector3i(5, 0, 5));
222-
223216
// Begin recipe dry run - loop, check bottom layer for matches
224217
MiniaturizationRecipe matchedRecipe = null;
225218
for(MiniaturizationRecipe recipe : recipesBoundFitted) {
226-
boolean recipeMatches = recipe.matches(world, field.getFieldSize(), fieldBounds, filledBounds);
219+
boolean recipeMatches = recipe.matches(world, field.getFieldSize(), fieldBlocks);
227220
if(!recipeMatches)
228221
continue;
229222

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.robotgryphon.compactcrafting.field;
2+
3+
import com.robotgryphon.compactcrafting.recipes.RecipeHelper;
4+
import com.robotgryphon.compactcrafting.util.BlockSpaceUtil;
5+
import net.minecraft.util.math.AxisAlignedBB;
6+
import net.minecraft.util.math.BlockPos;
7+
import net.minecraft.world.IWorldReader;
8+
9+
import java.util.stream.Stream;
10+
11+
public class MiniaturizationFieldBlockData {
12+
13+
private AxisAlignedBB fieldBounds;
14+
private AxisAlignedBB filledBounds;
15+
16+
private BlockPos[] filledPositions;
17+
private BlockPos[] relativeFilledPositions;
18+
19+
private MiniaturizationFieldBlockData(AxisAlignedBB fieldBounds) {
20+
this.fieldBounds = fieldBounds;
21+
}
22+
23+
public static MiniaturizationFieldBlockData getFromField(IWorldReader world, AxisAlignedBB field) {
24+
MiniaturizationFieldBlockData fb = new MiniaturizationFieldBlockData(field);
25+
26+
BlockPos[] nonAirPositions = BlockPos.getAllInBox(field)
27+
.filter(p -> !world.isAirBlock(p))
28+
.map(BlockPos::toImmutable)
29+
.toArray(BlockPos[]::new);
30+
31+
fb.filledPositions = nonAirPositions;
32+
33+
fb.filledBounds = BlockSpaceUtil.getBoundsForBlocks(nonAirPositions);
34+
35+
fb.relativeFilledPositions = RecipeHelper.normalizeFieldPositions(fb);
36+
37+
return fb;
38+
}
39+
40+
public AxisAlignedBB getFilledBounds() {
41+
return this.filledBounds;
42+
}
43+
44+
public int getNumberFilledBlocks() {
45+
return filledPositions.length;
46+
}
47+
48+
public BlockPos[] getFilledBlocks() {
49+
return this.filledPositions;
50+
}
51+
52+
public Stream<BlockPos> getRelativeFilledBlocks() {
53+
return Stream.of(relativeFilledPositions);
54+
}
55+
}

src/main/java/com/robotgryphon/compactcrafting/recipes/MiniaturizationRecipe.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.robotgryphon.compactcrafting.CompactCrafting;
44
import com.robotgryphon.compactcrafting.field.FieldProjectionSize;
5+
import com.robotgryphon.compactcrafting.field.MiniaturizationFieldBlockData;
56
import com.robotgryphon.compactcrafting.util.BlockSpaceUtil;
67
import net.minecraft.block.BlockState;
78
import net.minecraft.item.Item;
@@ -90,11 +91,12 @@ private boolean hasMatchingBottomLayer(IWorldReader world, FieldProjectionSize f
9091
return true;
9192
}
9293

93-
public boolean matches(IWorldReader world, FieldProjectionSize fieldSize, AxisAlignedBB field, AxisAlignedBB filledBounds) {
94+
public boolean matches(IWorldReader world, FieldProjectionSize fieldSize, MiniaturizationFieldBlockData fieldBlocks) {
9495
if (!fitsInFieldSize(fieldSize))
9596
return false;
9697

9798
// We know that the recipe will at least fit inside the current projection field
99+
AxisAlignedBB filledBounds = fieldBlocks.getFilledBounds();
98100

99101
// Check rest of the recipe layers
100102
int maxY = (int) dimensions.getYSize();
@@ -170,11 +172,11 @@ public boolean doLayerBlocksMatch(IWorldReader world, AxisAlignedBB fieldFilledB
170172
if(totalFilled != requiredFilled)
171173
return false;
172174

173-
BlockPos[] fieldNormalizedPositions = RecipeHelper.normalizeLayerPositions(fieldFilledBounds, filledPositions);
175+
BlockPos[] fieldNormalizedPositions = BlockSpaceUtil.normalizeLayerPositions(fieldFilledBounds, filledPositions);
174176
int extraYOffset = fieldNormalizedPositions[0].getY();
175177

176178
for(BlockPos fieldFilledPosition : fieldNormalizedPositions) {
177-
BlockPos realPos = RecipeHelper.denormalizeLayerPosition(fieldFilledBounds, fieldFilledPosition);
179+
BlockPos realPos = BlockSpaceUtil.denormalizeLayerPosition(fieldFilledBounds, fieldFilledPosition);
178180
BlockState state = world.getBlockState(realPos);
179181

180182
// If we require a block at a position and it's air...
Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,22 @@
11
package com.robotgryphon.compactcrafting.recipes;
22

3+
import com.robotgryphon.compactcrafting.field.MiniaturizationFieldBlockData;
4+
import com.robotgryphon.compactcrafting.util.BlockSpaceUtil;
35
import net.minecraft.util.math.AxisAlignedBB;
46
import net.minecraft.util.math.BlockPos;
57

68
import java.util.stream.Stream;
79

810
public abstract class RecipeHelper {
911

10-
/**
11-
* Normalizes world coordinates to relative field coordinates.
12-
*
13-
* @param fieldBounds The bounds of the field itself.
14-
* @param pos The position to normalize.
15-
* @return
16-
*/
17-
public static BlockPos normalizeLayerPosition(AxisAlignedBB fieldBounds, BlockPos pos) {
18-
return new BlockPos(
19-
pos.getX() - fieldBounds.minX,
20-
pos.getY() - fieldBounds.minY,
21-
pos.getZ() - fieldBounds.minZ
22-
);
23-
}
24-
/**
25-
* Converts world-coordinate positions into relative field positions.
26-
*
27-
* @param fieldBounds The boundaries of the crafting field.
28-
* @param fieldPositions The non-air block positions in the field (world coordinates).
29-
* @return
30-
*/
31-
public static BlockPos[] normalizeLayerPositions(AxisAlignedBB fieldBounds, BlockPos[] fieldPositions) {
32-
// Normalize the block positions so the recipe can match easier
33-
return Stream.of(fieldPositions)
12+
public static BlockPos[] normalizeFieldPositions(MiniaturizationFieldBlockData fieldBlocks) {
13+
BlockPos[] filledBlocks = fieldBlocks.getFilledBlocks();
14+
AxisAlignedBB filledBounds = fieldBlocks.getFilledBounds();
15+
16+
return Stream.of(filledBlocks)
3417
.parallel()
35-
.map(p -> normalizeLayerPosition(fieldBounds, p))
18+
.map(p -> BlockSpaceUtil.normalizeLayerPosition(filledBounds, p))
3619
.map(BlockPos::toImmutable)
3720
.toArray(BlockPos[]::new);
3821
}
39-
40-
public static BlockPos denormalizeLayerPosition(AxisAlignedBB realBounds, BlockPos normPos) {
41-
return new BlockPos(
42-
realBounds.minX + normPos.getX(),
43-
realBounds.minY + normPos.getY(),
44-
realBounds.minZ + normPos.getZ()
45-
);
46-
}
4722
}

src/main/java/com/robotgryphon/compactcrafting/util/BlockSpaceUtil.java

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.robotgryphon.compactcrafting.util;
22

3+
import com.robotgryphon.compactcrafting.field.MiniaturizationFieldBlockData;
34
import net.minecraft.util.Rotation;
45
import net.minecraft.util.math.AxisAlignedBB;
56
import net.minecraft.util.math.BlockPos;
67
import net.minecraft.util.math.MutableBoundingBox;
78
import net.minecraft.util.math.vector.Vector3d;
8-
import net.minecraft.util.math.vector.Vector3i;
99
import net.minecraft.world.IWorldReader;
1010

1111
import java.util.Arrays;
@@ -34,11 +34,29 @@ public static AxisAlignedBB getLayerBoundsByYOffset(AxisAlignedBB fullBounds, in
3434
);
3535
}
3636

37-
public static BlockPos[] rotateLayerPositions(BlockPos[] positions, Vector3i fieldCenter) {
38-
return Stream.of(positions)
39-
.map(p -> p.subtract(fieldCenter))
40-
.map(p -> p.rotate(Rotation.CLOCKWISE_90))
41-
.map(p -> p.add(fieldCenter))
37+
public static BlockPos[] rotatePositionsInPlace(BlockPos[] positions) {
38+
return rotatePositionsInPlace(positions, Rotation.CLOCKWISE_90);
39+
}
40+
41+
public static BlockPos[] rotatePositionsInPlace(BlockPos[] positions, Rotation rot) {
42+
AxisAlignedBB bounds = getBoundsForBlocks(positions);
43+
44+
// Rotation around a normalized world offset (smallest position is 0,0)
45+
BlockPos[] rotatedPreNormalize = Stream.of(positions)
46+
.map(p -> normalizeLayerPosition(bounds, p))
47+
.map(p -> p.rotate(rot))
48+
.map(p -> denormalizeLayerPosition(bounds, p))
49+
.map(BlockPos::toImmutable)
50+
.toArray(BlockPos[]::new);
51+
52+
AxisAlignedBB rotatedBounds = BlockSpaceUtil.getBoundsForBlocks(rotatedPreNormalize);
53+
54+
// Re-Normalize the positions to fix the offsetting that rotation does (we're rotating in place)
55+
BlockPos[] reNormalized = normalizeLayerPositions(rotatedBounds, rotatedPreNormalize);
56+
57+
// Rotated positions were normalized before to fix the offsetting that rotation does - denormalize again
58+
return Stream.of(reNormalized)
59+
.map(p -> denormalizeLayerPosition(bounds, p))
4260
.map(BlockPos::toImmutable)
4361
.toArray(BlockPos[]::new);
4462
}
@@ -78,4 +96,43 @@ public static AxisAlignedBB getBoundsForBlocks(Collection<BlockPos> filled) {
7896

7997
return AxisAlignedBB.toImmutable(trimmedBounds);
8098
}
99+
100+
/**
101+
* Normalizes world coordinates to relative field coordinates.
102+
*
103+
* @param fieldBounds The bounds of the field itself.
104+
* @param pos The position to normalize.
105+
* @return
106+
*/
107+
public static BlockPos normalizeLayerPosition(AxisAlignedBB fieldBounds, BlockPos pos) {
108+
return new BlockPos(
109+
pos.getX() - fieldBounds.minX,
110+
pos.getY() - fieldBounds.minY,
111+
pos.getZ() - fieldBounds.minZ
112+
);
113+
}
114+
115+
/**
116+
* Converts world-coordinate positions into relative field positions.
117+
*
118+
* @param fieldBounds The boundaries of the crafting field.
119+
* @param fieldPositions The non-air block positions in the field (world coordinates).
120+
* @return
121+
*/
122+
public static BlockPos[] normalizeLayerPositions(AxisAlignedBB fieldBounds, BlockPos[] fieldPositions) {
123+
// Normalize the block positions so the recipe can match easier
124+
return Stream.of(fieldPositions)
125+
.parallel()
126+
.map(p -> normalizeLayerPosition(fieldBounds, p))
127+
.map(BlockPos::toImmutable)
128+
.toArray(BlockPos[]::new);
129+
}
130+
131+
public static BlockPos denormalizeLayerPosition(AxisAlignedBB realBounds, BlockPos normPos) {
132+
return new BlockPos(
133+
realBounds.minX + normPos.getX(),
134+
realBounds.minY + normPos.getY(),
135+
realBounds.minZ + normPos.getZ()
136+
);
137+
}
81138
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.robotgryphon.compactcrafting;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.*;
6+
7+
class MiniaturizationFieldBlockDataTest {
8+
9+
@Test
10+
void getFilledBlocks() {
11+
}
12+
}

0 commit comments

Comments
 (0)