diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 312685377..d4b86779c 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -536,7 +536,9 @@
-
+
+
+
@@ -974,14 +976,19 @@
-
+
+
+
+
-
+
+
+
@@ -991,7 +998,11 @@
-
+
+
+
+
+
@@ -2211,7 +2222,10 @@
-
+
+
+
+
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/BasicTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/BasicTerrainGenerator.java
index 9c2fc7f02..603b826ea 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/BasicTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/BasicTerrainGenerator.java
@@ -3,12 +3,16 @@
import com.faforever.neroxis.brushes.Brushes;
import com.faforever.neroxis.generator.GeneratorParameters;
import com.faforever.neroxis.map.SCMap;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.map.SymmetrySettings;
import com.faforever.neroxis.mask.BooleanMask;
import com.faforever.neroxis.mask.FloatMask;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
import com.faforever.neroxis.util.vector.Vector3;
+import java.util.List;
+
public class BasicTerrainGenerator extends TerrainGenerator {
protected BooleanMask spawnLandMask;
protected BooleanMask spawnPlateauMask;
@@ -143,9 +147,18 @@ protected void teamConnectionsSetup() {
int numTeammateConnections = 1;
connections.setSize(map.getSize() + 1);
- MapMaskMethods.connectTeamsAroundCenter(map, random.nextLong(), connections, minMiddlePoints, maxMiddlePoints,
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.connectTeamsAroundCenter(team0SpawnLocations, random.nextLong(), connections, minMiddlePoints,
+ maxMiddlePoints,
numTeamConnections, maxStepSize, 32);
- MapMaskMethods.connectTeammates(map, random.nextLong(), connections, maxMiddlePoints, numTeammateConnections,
+ MapMaskMethods.connectTeammates(team0SpawnLocations, random.nextLong(), connections, maxMiddlePoints,
+ numTeammateConnections,
maxStepSize);
}
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/BigIslandsTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/BigIslandsTerrainGenerator.java
index e5fedf95d..f2a4fd183 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/BigIslandsTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/BigIslandsTerrainGenerator.java
@@ -1,8 +1,12 @@
package com.faforever.neroxis.generator.terrain;
import com.faforever.neroxis.generator.ParameterConstraints;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.mask.BooleanMask;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.List;
public class BigIslandsTerrainGenerator extends PathedTerrainGenerator {
@@ -24,7 +28,16 @@ protected void landSetup() {
BooleanMask islands = new BooleanMask(mapSize / 4, random.nextLong(), symmetrySettings, "islands", true);
land.setSize(mapSize + 1);
- MapMaskMethods.pathAroundSpawns(map, random.nextLong(), land, maxStepSize, numPaths, maxMiddlePoints, bound,
+
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.pathAroundSpawns(team0SpawnLocations, random.nextLong(), land, maxStepSize, numPaths,
+ maxMiddlePoints, bound,
(float) StrictMath.PI / 2);
land.inflate(maxStepSize).setSize(mapSize / 4);
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/DropPlateauTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/DropPlateauTerrainGenerator.java
index 04d40b99f..cf5d6e6dc 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/DropPlateauTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/DropPlateauTerrainGenerator.java
@@ -2,8 +2,12 @@
import com.faforever.neroxis.generator.GeneratorParameters;
import com.faforever.neroxis.map.SCMap;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.map.SymmetrySettings;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.List;
public class DropPlateauTerrainGenerator extends PathedTerrainGenerator {
@@ -31,9 +35,18 @@ protected void teamConnectionsSetup() {
connections.setSize(mapSize + 1);
- MapMaskMethods.connectTeamsAroundCenter(map, random.nextLong(), connections, minMiddlePoints, maxMiddlePoints,
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.connectTeamsAroundCenter(team0SpawnLocations, random.nextLong(), connections, minMiddlePoints,
+ maxMiddlePoints,
numTeamConnections, maxStepSize, 32);
- MapMaskMethods.connectTeammates(map, random.nextLong(), connections, maxMiddlePoints, numTeammateConnections,
+ MapMaskMethods.connectTeammates(team0SpawnLocations, random.nextLong(), connections, maxMiddlePoints,
+ numTeammateConnections,
maxStepSize);
}
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/LandBridgeTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/LandBridgeTerrainGenerator.java
index 272ad01c9..c600e8a0b 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/LandBridgeTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/LandBridgeTerrainGenerator.java
@@ -1,7 +1,11 @@
package com.faforever.neroxis.generator.terrain;
import com.faforever.neroxis.generator.ParameterConstraints;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.List;
public class LandBridgeTerrainGenerator extends PathedTerrainGenerator {
@@ -20,9 +24,18 @@ protected void landSetup() {
int numPaths = 32 / generatorParameters.spawnCount();
land.setSize(mapSize + 1);
- MapMaskMethods.connectTeammates(map, random.nextLong(), land, 8, 2, maxStepSize);
- MapMaskMethods.connectTeams(map, random.nextLong(), land, 0, 2, 1, maxStepSize);
- MapMaskMethods.pathAroundSpawns(map, random.nextLong(), land, maxStepSize, numPaths, 4, mapSize / 6,
+
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.connectTeammates(team0SpawnLocations, random.nextLong(), land, 8, 2, maxStepSize);
+ MapMaskMethods.connectTeams(team0SpawnLocations, random.nextLong(), land, 0, 2, 1, maxStepSize);
+ MapMaskMethods.pathAroundSpawns(team0SpawnLocations, random.nextLong(), land, maxStepSize, numPaths, 4,
+ mapSize / 6,
(float) (StrictMath.PI / 2f));
land.inflate(maxStepSize);
land.setSize(mapSize / 8);
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/OneIslandTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/OneIslandTerrainGenerator.java
index ada029558..d77398fe1 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/OneIslandTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/OneIslandTerrainGenerator.java
@@ -3,8 +3,12 @@
import com.faforever.neroxis.generator.GeneratorParameters;
import com.faforever.neroxis.generator.ParameterConstraints;
import com.faforever.neroxis.map.SCMap;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.map.SymmetrySettings;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.List;
public class OneIslandTerrainGenerator extends PathedTerrainGenerator {
@@ -31,9 +35,18 @@ protected void teamConnectionsSetup() {
int numTeammateConnections = 1;
connections.setSize(map.getSize() + 1);
- MapMaskMethods.connectTeams(map, random.nextLong(), connections, minMiddlePoints, maxMiddlePoints,
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.connectTeams(team0SpawnLocations, random.nextLong(), connections, minMiddlePoints,
+ maxMiddlePoints,
numTeamConnections, maxStepSize);
- MapMaskMethods.connectTeammates(map, random.nextLong(), connections, maxMiddlePoints, numTeammateConnections,
+ MapMaskMethods.connectTeammates(team0SpawnLocations, random.nextLong(), connections, maxMiddlePoints,
+ numTeammateConnections,
maxStepSize);
}
@@ -43,9 +56,9 @@ protected void landSetup() {
int minMiddlePoints = 2;
int maxMiddlePoints = 4;
int numTeamConnections = (int) (4 * landDensity + 4) / symmetrySettings.spawnSymmetry()
- .getNumSymPoints();
+ .getNumSymPoints();
int numTeammateConnections = (int) (2 * landDensity + 2) / symmetrySettings.spawnSymmetry()
- .getNumSymPoints();
+ .getNumSymPoints();
int numWalkers = (int) (8 * landDensity + 8) / symmetrySettings.spawnSymmetry().getNumSymPoints();
int bound = (int) (mapSize / 64 * (16 * (random.nextFloat() * .25f + (1 - landDensity) * .75f)))
+ mapSize / 8;
@@ -58,9 +71,19 @@ protected void landSetup() {
.fillEdge((int) (mapSize / 8 * (1 - landDensity) + mapSize / 8), false)
.inflate(mapSize / 64f)
.blur(12, .125f));
- MapMaskMethods.connectTeamsAroundCenter(map, random.nextLong(), land, minMiddlePoints, maxMiddlePoints,
+
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.connectTeamsAroundCenter(team0SpawnLocations, random.nextLong(), land, minMiddlePoints,
+ maxMiddlePoints,
numTeamConnections, maxStepSize, 32);
- MapMaskMethods.connectTeammates(map, random.nextLong(), land, maxMiddlePoints, numTeammateConnections,
+ MapMaskMethods.connectTeammates(team0SpawnLocations, random.nextLong(), land, maxMiddlePoints,
+ numTeammateConnections,
maxStepSize);
land.inflate(mapSize / 128f).setSize(mapSize / 8);
land.dilute(.5f, 8).erode(.5f, 6);
diff --git a/generator/src/main/java/com/faforever/neroxis/generator/terrain/SmallIslandsTerrainGenerator.java b/generator/src/main/java/com/faforever/neroxis/generator/terrain/SmallIslandsTerrainGenerator.java
index 21f5841da..5697675a4 100644
--- a/generator/src/main/java/com/faforever/neroxis/generator/terrain/SmallIslandsTerrainGenerator.java
+++ b/generator/src/main/java/com/faforever/neroxis/generator/terrain/SmallIslandsTerrainGenerator.java
@@ -3,9 +3,13 @@
import com.faforever.neroxis.generator.GeneratorParameters;
import com.faforever.neroxis.generator.ParameterConstraints;
import com.faforever.neroxis.map.SCMap;
+import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.map.SymmetrySettings;
import com.faforever.neroxis.mask.BooleanMask;
import com.faforever.neroxis.mask.MapMaskMethods;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.List;
public class SmallIslandsTerrainGenerator extends PathedTerrainGenerator {
@Override
@@ -28,13 +32,22 @@ protected void landSetup() {
int maxMiddlePoints = 4;
int numPaths = (int) (4 * landDensity + 4) / symmetrySettings.spawnSymmetry().getNumSymPoints();
- int bound = ((int) (mapSize / 16 * (random.nextFloat() * .25f + landDensity * .75f)) + mapSize / 16);
+ int bound = ((int) (mapSize / 16f * (random.nextFloat() * .25f + landDensity * .75f)) + mapSize / 16);
float maxStepSize = mapSize / 128f;
BooleanMask islands = new BooleanMask(mapSize / 4, random.nextLong(), symmetrySettings, "islands", true);
land.setSize(mapSize + 1);
- MapMaskMethods.pathAroundSpawns(map, random.nextLong(), land, maxStepSize, numPaths, maxMiddlePoints, bound,
+
+ List team0SpawnLocations = map.getSpawns()
+ .stream()
+ .filter(spawn -> spawn.getTeamID() == 0)
+ .map(Spawn::getPosition)
+ .map(Vector2::new)
+ .toList();
+
+ MapMaskMethods.pathAroundSpawns(team0SpawnLocations, random.nextLong(), land, maxStepSize, numPaths,
+ maxMiddlePoints, bound,
(float) StrictMath.PI / 2);
land.inflate(maxStepSize).setSize(mapSize / 4);
diff --git a/settings.gradle b/settings.gradle
index 9fea44e3c..096572879 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,7 +2,5 @@ rootProject.name = 'NeroxisGen'
include 'shared'
include 'generator'
include 'toolsuite'
-include 'mapgeneditor'
include 'utilities'
-include 'ngraph'
diff --git a/shared/src/main/java/com/faforever/neroxis/map/SymmetrySettings.java b/shared/src/main/java/com/faforever/neroxis/map/SymmetrySettings.java
index 6d19b5c41..77a4e3be6 100644
--- a/shared/src/main/java/com/faforever/neroxis/map/SymmetrySettings.java
+++ b/shared/src/main/java/com/faforever/neroxis/map/SymmetrySettings.java
@@ -5,6 +5,21 @@ public record SymmetrySettings(
Symmetry teamSymmetry,
Symmetry spawnSymmetry
) {
+
+ public SymmetrySettings {
+ if (terrainSymmetry.getNumSymPoints() % teamSymmetry.getNumSymPoints() != 0) {
+ throw new IllegalArgumentException("Team symmetry not a multiple of terrain symmetry");
+ }
+
+ if (terrainSymmetry.getNumSymPoints() % spawnSymmetry.getNumSymPoints() != 0) {
+ throw new IllegalArgumentException("Spawn symmetry not a multiple of terrain symmetry");
+ }
+
+ if (spawnSymmetry.getNumSymPoints() % teamSymmetry.getNumSymPoints() != 0) {
+ throw new IllegalArgumentException("Spawn symmetry not a multiple of team symmetry");
+ }
+ }
+
public SymmetrySettings(Symmetry symmetry) {
this(symmetry, symmetry, symmetry);
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java b/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java
index 666180fa2..7120eb522 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java
@@ -4,7 +4,9 @@
import com.faforever.neroxis.map.SymmetrySettings;
import com.faforever.neroxis.map.SymmetryType;
import com.faforever.neroxis.util.BezierCurve;
+import com.faforever.neroxis.util.SymmetryUtil;
import com.faforever.neroxis.util.functional.BiIntBooleanConsumer;
+import com.faforever.neroxis.util.functional.SymmetryRegionBoundsChecker;
import com.faforever.neroxis.util.functional.ToBooleanBiIntFunction;
import com.faforever.neroxis.util.vector.Vector2;
@@ -20,12 +22,15 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
+import java.util.function.IntUnaryOperator;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import static com.faforever.neroxis.brushes.Brushes.loadBrush;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public final class BooleanMask extends PrimitiveMask {
+public class BooleanMask extends PrimitiveMask {
private static final int BOOLEANS_PER_LONG = 64;
private static final long SINGLE_BIT_VALUE = 1;
private long[] mask;
@@ -52,12 +57,8 @@ public BooleanMask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
this(size, seed, symmetrySettings, name, false);
}
- BooleanMask(BooleanMask other) {
- this(other, (String) null);
- }
-
- BooleanMask(BooleanMask other, String name) {
- super(other, name);
+ protected BooleanMask(BooleanMask other, String name, boolean immutable) {
+ super(other, name, immutable);
}
private , U extends Comparable> BooleanMask(T other, U minValue) {
@@ -122,6 +123,11 @@ private void setPrimitive(int x, int y, boolean value) {
setBit(x, y, value, getSize(), mask);
}
+ @Override
+ protected void copyValue(int sourceX, int sourceY, int destX, int destY) {
+ setPrimitive(destX, destY, getPrimitive(sourceX, sourceY));
+ }
+
@Override
public BooleanMask blur(int radius) {
return blur(radius, .5f);
@@ -183,7 +189,7 @@ public BufferedImage toImage() {
public String toHash() throws NoSuchAlgorithmException {
int size = getSize();
ByteBuffer bytes = ByteBuffer.allocate(size * size);
- loopWithSymmetry(SymmetryType.SPAWN, (x, y) -> bytes.put(getPrimitive(x, y) ? (byte) 1 : 0));
+ loopInSymmetryRegion(SymmetryType.SPAWN, (x, y) -> bytes.put(getPrimitive(x, y) ? (byte) 1 : 0));
byte[] data = MessageDigest.getInstance("MD5").digest(bytes.array());
StringBuilder stringBuilder = new StringBuilder();
for (byte datum : data) {
@@ -230,9 +236,9 @@ protected BooleanMask setSizeInternal(int newSize) {
long[] oldMask = mask;
initializeMask(newSize);
Map coordinateMap = getSymmetricScalingCoordinateMap(oldSize, newSize);
- applyWithSymmetry(SymmetryType.SPAWN, (x, y) -> {
+ apply((x, y) -> {
boolean value = getBit(coordinateMap.get(x), coordinateMap.get(y), oldSize, oldMask);
- applyAtSymmetryPoints(x, y, SymmetryType.SPAWN, (sx, sy) -> setPrimitive(sx, sy, value));
+ setPrimitive(x, y, value);
});
}
});
@@ -597,16 +603,21 @@ public , U extends ComparableMask> BooleanMask ini
public BooleanMask randomWalk(int numWalkers, int numSteps) {
return enqueue(() -> {
int size = getSize();
+ Symmetry symmetry = symmetrySettings.getSymmetry(SymmetryType.TERRAIN);
+ int minXBound = 0;
+ int maxXBound = SymmetryUtil.getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ SymmetryRegionBoundsChecker symmetryRegionBoundsChecker = SymmetryUtil.getSymmetryRegionBoundsChecker(
+ symmetry, size);
for (int i = 0; i < numWalkers; i++) {
- int maxXBound = getMaxXBound(SymmetryType.TERRAIN);
- int minXBound = getMinXBound(SymmetryType.TERRAIN);
int x = random.nextInt(maxXBound - minXBound) + minXBound;
- int maxYBound = getMaxYBound(x, SymmetryType.TERRAIN);
- int minYBound = getMinYBound(x, SymmetryType.TERRAIN);
+ int maxYBound = maxYBoundFunction.applyAsInt(x);
+ int minYBound = minYBoundFunction.applyAsInt(x);
int y = random.nextInt(maxYBound - minYBound + 1) + minYBound;
for (int j = 0; j < numSteps; j++) {
- if (inBounds(x, y, size)) {
- applyAtSymmetryPoints(x, y, SymmetryType.TERRAIN, (sx, sy) -> setPrimitive(sx, sy, true));
+ if (inBounds(x, y, size) && symmetryRegionBoundsChecker.inBounds(x, y)) {
+ setPrimitive(x, y, true);
}
switch (random.nextInt(4)) {
case 0 -> x++;
@@ -616,6 +627,7 @@ public BooleanMask randomWalk(int numWalkers, int numSteps) {
}
}
}
+ applySymmetry(SymmetryType.TERRAIN);
});
}
@@ -682,7 +694,7 @@ public BooleanMask pathBezier(Vector2 start, Vector2 end, int minOrder, int maxO
for (float j = 0; j <= 1; j += 1f / size) {
points.add(bezierCurve.getPoint(j));
}
- fillCoordinates(points.stream().filter(this::inBounds).collect(Collectors.toList()), true);
+ fillCoordinates(points.stream().filter(this::inBounds).toList(), true);
}
return this;
}
@@ -706,6 +718,9 @@ public BooleanMask path(Vector2 start, Vector2 end, float maxStepSize, int numMi
SymmetryType symmetryType) {
return enqueue(() -> {
int size = getSize();
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ SymmetryRegionBoundsChecker symmetryRegionBoundsChecker = SymmetryUtil.getSymmetryRegionBoundsChecker(
+ symmetry, size);
List checkPoints = new ArrayList<>();
checkPoints.add(new Vector2(start));
for (int i = 0; i < numMiddlePoints; i++) {
@@ -713,9 +728,8 @@ public BooleanMask path(Vector2 start, Vector2 end, float maxStepSize, int numMi
float angle = (float) ((random.nextFloat() - .5f) * 2 * StrictMath.PI / 2f) + previousLoc.angleTo(end);
if (symmetrySettings.terrainSymmetry() == Symmetry.POINT4
&& angle % (StrictMath.PI / 2) < StrictMath.PI / 8) {
- angle += (float) (
- (random.nextBoolean() ? -1 : 1) * (random.nextFloat() * .5f + .5f) * 2f * StrictMath.PI
- / 4f);
+ int direction = random.nextBoolean() ? -1 : 1;
+ angle += (float) (direction * (random.nextFloat() * .5f + .5f) * 2f * StrictMath.PI / 4f);
}
float magnitude =
random.nextFloat() * (midPointMaxDistance - midPointMinDistance) + midPointMinDistance;
@@ -730,10 +744,11 @@ public BooleanMask path(Vector2 start, Vector2 end, float maxStepSize, int numMi
Vector2 nextLoc = checkPoints.get(i + 1);
float oldAngle = location.angleTo(nextLoc) + (random.nextFloat() - .5f) * 2f * maxAngleError;
while (location.getDistance(nextLoc) > maxStepSize && numSteps < size * size) {
- List symmetryPoints = getSymmetryPoints(location, symmetryType);
- if (inBounds(location) && symmetryPoints.stream().allMatch(this::inBounds)) {
- applyAtSymmetryPoints((int) location.getX(), (int) location.getY(), SymmetryType.TERRAIN,
- (sx, sy) -> setPrimitive(sx, sy, true));
+ if (inBounds(location, size) && symmetryRegionBoundsChecker.inBounds(location)) {
+ List symmetryPoints = getSymmetryPoints(location, symmetryType);
+ if (symmetryPoints.stream().allMatch(this::inBounds)) {
+ setPrimitive(location, true);
+ }
}
float magnitude = StrictMath.max(1, random.nextFloat() * maxStepSize);
float angle = oldAngle * .5f + location.angleTo(nextLoc) * .5f
@@ -746,6 +761,7 @@ public BooleanMask path(Vector2 start, Vector2 end, float maxStepSize, int numMi
break;
}
}
+ applySymmetry(symmetryType);
});
}
@@ -819,28 +835,38 @@ public BooleanMask deflate(float radius) {
* @return the modified mask
*/
public BooleanMask progressiveWalk(int numWalkers, int numSteps) {
- int size = getSize();
- for (int i = 0; i < numWalkers; i++) {
- int x = random.nextInt(getMaxXBound(SymmetryType.TERRAIN) - getMinXBound(SymmetryType.TERRAIN))
- + getMinXBound(SymmetryType.TERRAIN);
- int y = random.nextInt(getMaxYBound(x, SymmetryType.TERRAIN) - getMinYBound(x, SymmetryType.TERRAIN) + 1)
- + getMinYBound(x, SymmetryType.TERRAIN);
- List directions = new ArrayList<>(Arrays.asList(0, 1, 2, 3));
- int regressiveDir = random.nextInt(directions.size());
- directions.remove(regressiveDir);
- for (int j = 0; j < numSteps; j++) {
- if (inBounds(x, y, size)) {
- applyAtSymmetryPoints(x, y, SymmetryType.TERRAIN, (sx, sy) -> setPrimitive(sx, sy, true));
- }
- switch (directions.get(random.nextInt(directions.size()))) {
- case 0 -> x++;
- case 1 -> x--;
- case 2 -> y++;
- case 3 -> y--;
+ return enqueue(() -> {
+
+ int size = getSize();
+ Symmetry symmetry = symmetrySettings.getSymmetry(SymmetryType.TERRAIN);
+ int minXBound = 0;
+ int maxXBound = SymmetryUtil.getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ SymmetryRegionBoundsChecker symmetryRegionBoundsChecker = SymmetryUtil.getSymmetryRegionBoundsChecker(
+ symmetry, size);
+ for (int i = 0; i < numWalkers; i++) {
+ int x = random.nextInt(maxXBound - minXBound) + minXBound;
+ int maxYBound = maxYBoundFunction.applyAsInt(x);
+ int minYBound = minYBoundFunction.applyAsInt(x);
+ int y = random.nextInt(maxYBound - minYBound + 1) + minYBound;
+ List directions = new ArrayList<>(Arrays.asList(0, 1, 2, 3));
+ int regressiveDir = random.nextInt(directions.size());
+ directions.remove(regressiveDir);
+ for (int j = 0; j < numSteps; j++) {
+ if (inBounds(x, y, size) && symmetryRegionBoundsChecker.inBounds(x, y)) {
+ setPrimitive(x, y, true);
+ }
+ switch (directions.get(random.nextInt(directions.size()))) {
+ case 0 -> x++;
+ case 1 -> x--;
+ case 2 -> y++;
+ case 3 -> y--;
+ }
}
}
- }
- return this;
+ applySymmetry(SymmetryType.TERRAIN);
+ });
}
/**
@@ -920,7 +946,7 @@ public BooleanMask erode(float strength) {
* @return the modified mask
*/
public BooleanMask acid(float strength, float size) {
- BooleanMask holes = new BooleanMask(this, getName() + "holes");
+ BooleanMask holes = new BooleanMask(this, getName() + "holes", false);
holes.randomize(strength, SymmetryType.SPAWN).inflate(size);
return enqueue(dependencies -> {
BooleanMask source = (BooleanMask) dependencies.getFirst();
@@ -935,7 +961,7 @@ public BooleanMask acid(float strength, float size) {
* @return the modified mask
*/
public BooleanMask splat(float strength, float size) {
- BooleanMask holes = new BooleanMask(this, getName() + "splat");
+ BooleanMask holes = new BooleanMask(this, getName() + "splat", false);
holes.randomize(strength, SymmetryType.SPAWN).inflate(size);
return enqueue(dependencies -> {
BooleanMask source = (BooleanMask) dependencies.getFirst();
@@ -975,6 +1001,15 @@ public boolean isEdge(int x, int y) {
x < size - 1 && getPrimitive(x + 1, y) != value) || (y < size - 1 && getPrimitive(x, y + 1) != value));
}
+ public boolean isEdge(int x, int y, int size, long[] mask) {
+ boolean value = getBit(x, y, size, mask);
+ return ((x > 0 && getBit(x - 1, y, size, mask) != value) || (y > 0 && getBit(x, y - 1, size, mask) != value)
+ || (
+ x < size - 1 && getBit(x + 1, y, size, mask) != value) || (y < size - 1
+ && getBit(x, y + 1, size, mask)
+ != value));
+ }
+
/**
* Set false pixels with non-like neighbors to true
* with a probability of {@code strength} {@code count} times
@@ -990,11 +1025,11 @@ public BooleanMask dilute(float strength, int count) {
for (int i = 0; i < count; i++) {
long[] maskCopy = getMaskCopy();
applyWithSymmetry(symmetryType, (x, y) -> {
- if (!getPrimitive(x, y) && random.nextFloat() < strength && isEdge(x, y)) {
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> setBit(sx, sy, true, size, maskCopy));
+ if (!getBit(x, y, getSize(), maskCopy) && random.nextFloat() < strength && isEdge(x, y, size,
+ maskCopy)) {
+ setBit(x, y, true, size, mask);
}
});
- mask = maskCopy;
}
});
}
@@ -1012,11 +1047,11 @@ public BooleanMask erode(float strength, int count) {
for (int i = 0; i < count; i++) {
long[] maskCopy = getMaskCopy();
applyWithSymmetry(symmetryType, (x, y) -> {
- if (getPrimitive(x, y) && random.nextFloat() < strength && isEdge(x, y)) {
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> setBit(sx, sy, false, size, maskCopy));
+ if (getBit(x, y, getSize(), maskCopy) && random.nextFloat() < strength && isEdge(x, y, size,
+ maskCopy)) {
+ setBit(x, y, false, size, mask);
}
});
- mask = maskCopy;
}
});
}
@@ -1091,13 +1126,23 @@ public BooleanMask limitToSymmetryRegion() {
}
public BooleanMask limitToSymmetryRegion(SymmetryType symmetryType) {
- int minXBound = getMinXBound(symmetryType);
- int maxXBound = getMaxXBound(symmetryType);
- return apply((x, y) -> {
- setPrimitive(x, y,
- getPrimitive(x, y) && !(x < minXBound || x >= maxXBound || y < getMinYBound(x, symmetryType)
- || y >= getMaxYBound(x, symmetryType)));
- });
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ int size = getSize();
+ int minXBound = 0;
+ int maxXBound = SymmetryUtil.getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ Map minYBoundMap = IntStream.range(minXBound, maxXBound)
+ .boxed()
+ .collect(Collectors.toMap(Function.identity(),
+ minYBoundFunction::applyAsInt));
+ Map maxYBoundMap = IntStream.range(minXBound, maxXBound)
+ .boxed()
+ .collect(Collectors.toMap(Function.identity(),
+ maxYBoundFunction::applyAsInt));
+ return apply((x, y) -> setPrimitive(x, y, getPrimitive(x, y) && !(x < minXBound || x >= maxXBound
+ || y < minYBoundMap.get(x)
+ || y >= maxYBoundMap.get(x))));
}
/**
@@ -1107,7 +1152,7 @@ public BooleanMask limitToSymmetryRegion(SymmetryType symmetryType) {
*/
public BooleanMask limitToCenteredCircle(float circleRadius) {
int size = getSize();
- BooleanMask symmetryLimit = new BooleanMask(size, null, symmetrySettings, getName() + "symmetryLimit",
+ BooleanMask symmetryLimit = new BooleanMask(size, null, symmetrySettings, getName() + "SymmetryLimit",
isParallel());
symmetryLimit.fillCircle(size / 2f, size / 2f, circleRadius, true);
return multiply(symmetryLimit);
@@ -1156,7 +1201,7 @@ public BooleanMask removeAreasSmallerThan(int maxArea) {
Set coordinates = getShapeCoordinates(location, maxArea);
seen.addAll(coordinates);
if (coordinates.size() < maxArea) {
- fillCoordinates(coordinates, !value);
+ coordinates.forEach(loc -> set((int) loc.getX(), (int) loc.getY(), !value));
}
}
});
@@ -1329,7 +1374,7 @@ public List getAllCoordinatesEqualTo(boolean value, int spacing) {
public List getRandomCoordinates(float minSpacing, float maxSpacing, SymmetryType symmetryType) {
List coordinateList;
if (symmetryType != null) {
- coordinateList = copy().limitToSymmetryRegion().getAllCoordinatesEqualTo(true);
+ coordinateList = copy().limitToSymmetryRegion(symmetryType).getAllCoordinatesEqualTo(true);
} else {
coordinateList = getAllCoordinatesEqualTo(true);
}
@@ -1384,31 +1429,19 @@ public Vector2 getRandomPosition() {
}
public BooleanMask addPrimitiveWithSymmetry(SymmetryType symmetryType, ToBooleanBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- boolean value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public BooleanMask subtractPrimitiveWithSymmetry(SymmetryType symmetryType, ToBooleanBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- boolean value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> subtractPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public BooleanMask multiplyPrimitiveWithSymmetry(SymmetryType symmetryType, ToBooleanBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- boolean value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> multiplyPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public BooleanMask dividePrimitiveWithSymmetry(SymmetryType symmetryType, ToBooleanBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- boolean value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> dividePrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> dividePrimitiveAt(x, y, valueFunction.apply(x, y)));
}
private BooleanMask applyWithOffset(BooleanMask other, BiIntBooleanConsumer action, int xOffset, int yOffset,
@@ -1419,35 +1452,17 @@ private BooleanMask applyWithOffset(BooleanMask other, BiIntBooleanConsumer acti
int smallerSize = StrictMath.min(size, otherSize);
int biggerSize = StrictMath.max(size, otherSize);
if (smallerSize == otherSize) {
- if (symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
- otherSize, size);
- Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
- otherSize, size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- boolean value = other.getPrimitive(x, y);
- applyAtSymmetryPoints(shiftX, shiftY, SymmetryType.SPAWN,
- (sx, sy) -> action.accept(sx, sy, value));
- }
- });
- } else {
- applyAtSymmetryPointsWithOutOfBounds(xOffset, yOffset, SymmetryType.SPAWN, (sx, sy) -> {
- Map coordinateXMap = getShiftedCoordinateMap(sx, center, wrapEdges, otherSize,
- size);
- Map coordinateYMap = getShiftedCoordinateMap(sy, center, wrapEdges, otherSize,
- size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- action.accept(shiftX, shiftY, other.getPrimitive(x, y));
- }
- });
- });
- }
+ Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
+ otherSize, size);
+ Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
+ otherSize, size);
+ other.apply((x, y) -> {
+ int shiftX = coordinateXMap.get(x);
+ int shiftY = coordinateYMap.get(y);
+ if (inBounds(shiftX, shiftY, size)) {
+ action.accept(shiftX, shiftY, other.getPrimitive(x, y));
+ }
+ });
} else {
Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, size,
otherSize);
@@ -1461,6 +1476,7 @@ private BooleanMask applyWithOffset(BooleanMask other, BiIntBooleanConsumer acti
}
});
}
+ applySymmetry(SymmetryType.SPAWN);
});
}
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/ComparableMask.java b/shared/src/main/java/com/faforever/neroxis/mask/ComparableMask.java
index f5641aa8d..3ad526c13 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/ComparableMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/ComparableMask.java
@@ -3,13 +3,14 @@
import com.faforever.neroxis.map.SymmetrySettings;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public abstract sealed class ComparableMask, U extends ComparableMask> extends OperationsMask permits PrimitiveMask {
+public abstract class ComparableMask, U extends ComparableMask> extends
+ OperationsMask {
protected ComparableMask(int size, Long seed, SymmetrySettings symmetrySettings, String name, boolean parallel) {
super(size, seed, symmetrySettings, name, parallel);
}
- protected ComparableMask(U other, String name) {
- super(other, name);
+ protected ComparableMask(U other, String name, boolean immutable) {
+ super(other, name, immutable);
}
protected boolean valueAtEqualTo(int x, int y, T value) {
@@ -65,15 +66,21 @@ && valueAtGreaterThanEqualTo(x + 1, y,
&&
valueAtGreaterThanEqualTo(
x,
- y -
+ y
+ -
1,
- value)) &&
- (y <
- getSize() -
- 1 &&
+ value))
+ &&
+ (y
+ <
+ getSize()
+ -
+ 1
+ &&
valueAtGreaterThanEqualTo(
x,
- y +
+ y
+ +
1,
value))));
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/FloatMask.java b/shared/src/main/java/com/faforever/neroxis/mask/FloatMask.java
index 357e32f5f..81525b892 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/FloatMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/FloatMask.java
@@ -25,7 +25,7 @@
import static com.faforever.neroxis.brushes.Brushes.loadBrush;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public final class FloatMask extends PrimitiveMask {
+public class FloatMask extends PrimitiveMask {
private float[][] mask;
public FloatMask(int size, Long seed, SymmetrySettings symmetrySettings) {
@@ -70,19 +70,11 @@ public FloatMask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetry
this(sourceImage, seed, symmetrySettings, scaleFactor, name, false);
}
- FloatMask(FloatMask other) {
- this(other, null);
- }
-
- FloatMask(FloatMask other, String name) {
- super(other, name);
- }
-
- FloatMask(BooleanMask other, float low, float high) {
+ protected FloatMask(BooleanMask other, float low, float high) {
this(other, low, high, null);
}
- FloatMask(BooleanMask other, float low, float high, String name) {
+ protected FloatMask(BooleanMask other, float low, float high, String name) {
this(other.getSize(), other.getNextSeed(), other.getSymmetrySettings(), name, other.isParallel());
enqueue(dependencies -> {
BooleanMask source = (BooleanMask) dependencies.getFirst();
@@ -90,6 +82,10 @@ public FloatMask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetry
}, other);
}
+ protected FloatMask(FloatMask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
private , U extends VectorMask> FloatMask(VectorMask other1,
VectorMask other2) {
this(other1, other2, null);
@@ -137,6 +133,11 @@ void setPrimitive(int x, int y, float value) {
mask[x][y] = value;
}
+ @Override
+ protected void copyValue(int sourceX, int sourceY, int destX, int destY) {
+ setPrimitive(destX, destY, getPrimitive(sourceX, sourceY));
+ }
+
/**
* Add perlin noise to the mask with the given resolution and noise scale
*
@@ -148,10 +149,9 @@ public FloatMask addPerlinNoise(int resolution, float scale) {
int size = getSize();
int gradientSize = size / resolution;
float gradientScale = (float) size / gradientSize;
- Vector2Mask gradientVectors = new Vector2Mask(gradientSize +
- 1, random.nextLong(), new SymmetrySettings(Symmetry.NONE),
- getName() +
- "PerlinVectors", isParallel());
+ Vector2Mask gradientVectors = new Vector2Mask(gradientSize + 1, random.nextLong(),
+ new SymmetrySettings(Symmetry.NONE), getName() + "PerlinVectors",
+ isParallel());
gradientVectors.randomize(-1f, 1f).normalize();
FloatMask noise = new FloatMask(size, null, symmetrySettings, getName() + "PerlinNoise", isParallel());
noise.enqueue(dependencies -> {
@@ -275,7 +275,8 @@ private void waterDrop(int maxIterations, float x, float y, float friction, floa
for (int i = 0; i < maxIterations; ++i) {
int sampleX = (int) (x + xOffset);
int sampleY = (int) (y + yOffset);
- if (!inBounds(sampleX, sampleY) || !inBounds((int) xPrev, (int) yPrev)) {
+ if (!inBounds(sampleX, sampleY, getSize()) || !inBounds((int) xPrev, (int) yPrev,
+ getSize())) {
return;
}
Vector3 surfaceNormal = calculateNormalAt(sampleX, sampleY, 1f);
@@ -307,8 +308,8 @@ private void waterDrop(int maxIterations, float x, float y, float friction, floa
public FloatMask removeAreasOfSpecifiedSizeWithLocalMaximums(int minSize, int maxSize, int levelOfPrecision,
float floatMax) {
for (int x = 0; x < levelOfPrecision; x++) {
- removeAreasInIntensityAndSize(minSize, maxSize, ((1f - (float) x / (float) levelOfPrecision) *
- floatMax), floatMax);
+ removeAreasInIntensityAndSize(minSize, maxSize, ((1f - (float) x / (float) levelOfPrecision) * floatMax),
+ floatMax);
}
removeAreasInIntensityAndSize(minSize, maxSize, 0.0000001f, floatMax);
return this;
@@ -395,9 +396,8 @@ public FloatMask useBrushWithinAreaWithDensity(BooleanMask other, String brushNa
float intensity, boolean wrapEdges) {
enqueue(dependencies -> {
BooleanMask source = (BooleanMask) dependencies.getFirst();
- int frequency = (int) (density * (float) source.getCount() /
- 26.21f /
- symmetrySettings.spawnSymmetry().getNumSymPoints());
+ int frequency = (int) (density * (float) source.getCount() / 26.21f / symmetrySettings.spawnSymmetry()
+ .getNumSymPoints());
useBrushWithinArea(source, brushName, size, frequency, intensity, wrapEdges);
}, other);
return this;
@@ -423,8 +423,7 @@ public BooleanMask copyAsShadowMask(Vector3 lightDirection) {
float angle = (float) ((lightDirection.getAzimuth() - StrictMath.PI) % (StrictMath.PI * 2));
float slope = (float) StrictMath.tan(lightDirection.getElevation());
BooleanMask shadowMask = new BooleanMask(getSize(), getNextSeed(), new SymmetrySettings(Symmetry.NONE),
- getName() +
- "Shadow", isParallel());
+ getName() + "Shadow", isParallel());
return shadowMask.enqueue(dependencies -> shadowMask.apply((x, y) -> {
FloatMask source = (FloatMask) dependencies.getFirst();
Vector2 location = new Vector2(x, y);
@@ -515,15 +514,15 @@ private void addCalculatedParabolicDistance(boolean useColumns) {
}
Vector2 current = new Vector2(j, value);
Vector2 vertex = vertices.get(index);
- float xIntersect = ((current.getY() + current.getX() * current.getX()) -
- (vertex.getY() + vertex.getX() * vertex.getX())) /
- (2 * current.getX() - 2 * vertex.getX());
+ float xIntersect = ((current.getY() + current.getX() * current.getX()) - (vertex.getY() + vertex.getX()
+ * vertex.getX()))
+ / (2 * current.getX() - 2 * vertex.getX());
while (xIntersect <= intersections.get(index).getX()) {
index -= 1;
vertex = vertices.get(index);
- xIntersect = ((current.getY() + current.getX() * current.getX()) -
- (vertex.getY() + vertex.getX() * vertex.getX())) /
- (2 * current.getX() - 2 * vertex.getX());
+ xIntersect = ((current.getY() + current.getX() * current.getX()) - (vertex.getY() + vertex.getX()
+ * vertex.getX()))
+ / (2 * current.getX() - 2 * vertex.getX());
}
index += 1;
if (index < vertices.size()) {
@@ -619,7 +618,7 @@ public BufferedImage toImage() {
public String toHash() throws NoSuchAlgorithmException {
int size = getSize();
ByteBuffer bytes = ByteBuffer.allocate(size * size * 4);
- loopWithSymmetry(SymmetryType.SPAWN, (x, y) -> bytes.putFloat(getPrimitive(x, y)));
+ loopInSymmetryRegion(SymmetryType.SPAWN, (x, y) -> bytes.putFloat(getPrimitive(x, y)));
byte[] data = MessageDigest.getInstance("MD5").digest(bytes.array());
StringBuilder stringBuilder = new StringBuilder();
for (byte datum : data) {
@@ -669,9 +668,9 @@ protected FloatMask setSizeInternal(int newSize) {
float[][] oldMask = mask;
initializeMask(newSize);
Map coordinateMap = getSymmetricScalingCoordinateMap(oldSize, newSize);
- applyWithSymmetry(SymmetryType.SPAWN, (x, y) -> {
+ apply((x, y) -> {
float value = oldMask[coordinateMap.get(x)][coordinateMap.get(y)];
- applyAtSymmetryPoints(x, y, SymmetryType.SPAWN, (sx, sy) -> setPrimitive(sx, sy, value));
+ setPrimitive(x, y, value);
});
}
});
@@ -954,38 +953,23 @@ public FloatMask divideWithOffset(FloatMask other, int xOffset, int yOffset, boo
}
public FloatMask setPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> setPrimitive(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> setPrimitive(x, y, valueFunction.apply(x, y)));
}
public FloatMask addPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public FloatMask subtractPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> subtractPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public FloatMask multiplyPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> multiplyPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public FloatMask dividePrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> dividePrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> dividePrimitiveAt(x, y, valueFunction.apply(x, y)));
}
private FloatMask applyWithOffset(FloatMask other, BiIntFloatConsumer action, int xOffset, int yOffset,
@@ -996,35 +980,17 @@ private FloatMask applyWithOffset(FloatMask other, BiIntFloatConsumer action, in
int smallerSize = StrictMath.min(size, otherSize);
int biggerSize = StrictMath.max(size, otherSize);
if (smallerSize == otherSize) {
- if (symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
- otherSize, size);
- Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
- otherSize, size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- float value = other.getPrimitive(x, y);
- applyAtSymmetryPoints(shiftX, shiftY, SymmetryType.SPAWN,
- (sx, sy) -> action.accept(sx, sy, value));
- }
- });
- } else {
- applyAtSymmetryPointsWithOutOfBounds(xOffset, yOffset, SymmetryType.SPAWN, (sx, sy) -> {
- Map coordinateXMap = getShiftedCoordinateMap(sx, center, wrapEdges, otherSize,
- size);
- Map coordinateYMap = getShiftedCoordinateMap(sy, center, wrapEdges, otherSize,
- size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- action.accept(shiftX, shiftY, other.getPrimitive(x, y));
- }
- });
- });
- }
+ Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
+ otherSize, size);
+ Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
+ otherSize, size);
+ other.apply((x, y) -> {
+ int shiftX = coordinateXMap.get(x);
+ int shiftY = coordinateYMap.get(y);
+ if (inBounds(shiftX, shiftY, size)) {
+ action.accept(shiftX, shiftY, other.getPrimitive(x, y));
+ }
+ });
} else {
Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, size,
otherSize);
@@ -1038,6 +1004,7 @@ private FloatMask applyWithOffset(FloatMask other, BiIntFloatConsumer action, in
}
});
}
+ applySymmetry(SymmetryType.SPAWN);
});
}
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/IntegerMask.java b/shared/src/main/java/com/faforever/neroxis/mask/IntegerMask.java
index 8ff376e58..2de35ac92 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/IntegerMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/IntegerMask.java
@@ -15,7 +15,7 @@
import java.util.Map;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public final class IntegerMask extends PrimitiveMask {
+public class IntegerMask extends PrimitiveMask {
private int[][] mask;
public IntegerMask(int size, Long seed, SymmetrySettings symmetrySettings) {
@@ -39,14 +39,6 @@ public IntegerMask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
this(size, seed, symmetrySettings, name, false);
}
- IntegerMask(IntegerMask other) {
- this(other, null);
- }
-
- IntegerMask(IntegerMask other, String name) {
- super(other, name);
- }
-
IntegerMask(BooleanMask other, int low, int high) {
this(other, low, high, null);
}
@@ -54,7 +46,7 @@ public IntegerMask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
public IntegerMask(BooleanMask other, int low, int high, String name) {
this(other.getSize(), other.getNextSeed(), other.getSymmetrySettings(), name, other.isParallel());
enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
apply((x, y) -> setPrimitive(x, y, source.getPrimitive(x, y) ? high : low));
}, other);
}
@@ -75,10 +67,19 @@ public IntegerMask(BufferedImage sourceImage, Long seed, SymmetrySettings symmet
this(sourceImage, seed, symmetrySettings, null, false);
}
+ protected IntegerMask(IntegerMask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
private void setPrimitive(int x, int y, int value) {
mask[x][y] = value;
}
+ @Override
+ protected void copyValue(int sourceX, int sourceY, int destX, int destY) {
+ setPrimitive(destX, destY, getPrimitive(sourceX, sourceY));
+ }
+
public int getPrimitive(Vector2 location) {
return getPrimitive(StrictMath.round(location.getX()), StrictMath.round(location.getY()));
}
@@ -118,7 +119,7 @@ public IntegerMask blur(int radius) {
public IntegerMask blur(int radius, BooleanMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- BooleanMask limiter = (BooleanMask) dependencies.get(0);
+ BooleanMask limiter = (BooleanMask) dependencies.getFirst();
int[][] innerCount = getInnerCount();
apply((x, y) -> {
if (limiter.get(x, y)) {
@@ -130,7 +131,7 @@ public IntegerMask blur(int radius, BooleanMask other) {
@Override
protected IntegerMask copyFrom(IntegerMask other) {
- return enqueue(dependencies -> fill(((IntegerMask) dependencies.get(0)).mask), other);
+ return enqueue(dependencies -> fill(((IntegerMask) dependencies.getFirst()).mask), other);
}
@Override
@@ -159,7 +160,7 @@ public BufferedImage toImage() {
@Override
public String toHash() throws NoSuchAlgorithmException {
ByteBuffer bytes = ByteBuffer.allocate(getSize() * getSize() * 4);
- loopWithSymmetry(SymmetryType.SPAWN, (x, y) -> bytes.putInt(getPrimitive(x, y)));
+ loopInSymmetryRegion(SymmetryType.SPAWN, (x, y) -> bytes.putInt(getPrimitive(x, y)));
byte[] data = MessageDigest.getInstance("MD5").digest(bytes.array());
StringBuilder stringBuilder = new StringBuilder();
for (byte datum : data) {
@@ -209,9 +210,9 @@ protected IntegerMask setSizeInternal(int newSize) {
int[][] oldMask = mask;
initializeMask(newSize);
Map coordinateMap = getSymmetricScalingCoordinateMap(oldSize, newSize);
- applyWithSymmetry(SymmetryType.SPAWN, (x, y) -> {
+ apply((x, y) -> {
int value = oldMask[coordinateMap.get(x)][coordinateMap.get(y)];
- applyAtSymmetryPoints(x, y, SymmetryType.SPAWN, (sx, sy) -> setPrimitive(sx, sy, value));
+ setPrimitive(x, y, value);
});
}
});
@@ -247,18 +248,22 @@ private IntegerMask divide(ToIntBiIntFunction valueFunction) {
return apply((x, y) -> dividePrimitiveAt(x, y, valueFunction.apply(x, y)));
}
- private void addPrimitiveAt(int x, int y, float value) {
+ private void addPrimitiveAt(int x, int y, int value) {
mask[x][y] += value;
}
- private void subtractPrimitiveAt(int x, int y, float value) {
+ private void subtractPrimitiveAt(int x, int y, int value) {
mask[x][y] -= value;
}
- private void multiplyPrimitiveAt(int x, int y, float value) {
+ private void multiplyPrimitiveAt(int x, int y, int value) {
mask[x][y] *= value;
}
+ private void dividePrimitiveAt(int x, int y, int value) {
+ mask[x][y] /= value;
+ }
+
public BufferedImage writeToImage(BufferedImage image, float scaleFactor) {
assertSize(image.getHeight());
int size = getSize();
@@ -267,10 +272,6 @@ public BufferedImage writeToImage(BufferedImage image, float scaleFactor) {
return image;
}
- private void dividePrimitiveAt(int x, int y, float value) {
- mask[x][y] /= value;
- }
-
@Override
protected int[][] getInnerCount() {
int[][] innerCount = new int[getSize()][getSize()];
@@ -287,7 +288,7 @@ public Integer getSum() {
public IntegerMask add(IntegerMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
apply((x, y) -> mask[x][y] += source.mask[x][y]);
}, other);
}
@@ -302,7 +303,7 @@ public IntegerMask add(BooleanMask other, Integer value) {
assertCompatibleMask(other);
int val = value;
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
apply((x, y) -> {
if (source.getPrimitive(x, y)) {
addPrimitiveAt(x, y, val);
@@ -328,7 +329,7 @@ public IntegerMask add(BooleanMask other, IntegerMask values) {
@Override
public IntegerMask addWithOffset(IntegerMask other, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
applyWithOffset(source, (TriIntConsumer) this::addPrimitiveAt, xOffset, yOffset, center, wrapEdges);
}, other);
}
@@ -349,7 +350,7 @@ public Integer getAvg() {
public IntegerMask subtract(IntegerMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
apply((x, y) -> mask[x][y] -= source.mask[x][y]);
}, other);
}
@@ -359,7 +360,7 @@ public IntegerMask subtract(BooleanMask other, Integer values) {
assertCompatibleMask(other);
int val = values;
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
apply((x, y) -> {
if (source.getPrimitive(x, y)) {
subtractPrimitiveAt(x, y, val);
@@ -386,7 +387,7 @@ public IntegerMask subtract(BooleanMask other, IntegerMask values) {
public IntegerMask subtractWithOffset(IntegerMask other, int xOffset, int yOffset, boolean center,
boolean wrapEdges) {
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
applyWithOffset(source, (TriIntConsumer) this::subtractPrimitiveAt, xOffset, yOffset, center, wrapEdges);
}, other);
}
@@ -395,7 +396,7 @@ public IntegerMask subtractWithOffset(IntegerMask other, int xOffset, int yOffse
public IntegerMask multiply(IntegerMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
apply((x, y) -> mask[x][y] *= source.mask[x][y]);
}, other);
}
@@ -410,7 +411,7 @@ public IntegerMask multiply(BooleanMask other, Integer value) {
assertCompatibleMask(other);
int val = value;
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
apply((x, y) -> {
if (source.getPrimitive(x, y)) {
multiplyPrimitiveAt(x, y, val);
@@ -437,7 +438,7 @@ public IntegerMask multiply(BooleanMask other, IntegerMask value) {
public IntegerMask multiplyWithOffset(IntegerMask other, int xOffset, int yOffset, boolean center,
boolean wrapEdges) {
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
applyWithOffset(source, (TriIntConsumer) this::multiplyPrimitiveAt, xOffset, yOffset, center, wrapEdges);
}, other);
}
@@ -446,7 +447,7 @@ public IntegerMask multiplyWithOffset(IntegerMask other, int xOffset, int yOffse
public IntegerMask divide(IntegerMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
apply((x, y) -> mask[x][y] /= source.mask[x][y]);
}, other);
}
@@ -461,7 +462,7 @@ public IntegerMask divide(BooleanMask other, Integer value) {
assertCompatibleMask(other);
int val = value;
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
apply((x, y) -> {
if (source.getPrimitive(x, y)) {
dividePrimitiveAt(x, y, val);
@@ -488,37 +489,25 @@ public IntegerMask divide(BooleanMask other, IntegerMask value) {
public IntegerMask divideWithOffset(IntegerMask other, int xOffset, int yOffset, boolean center,
boolean wrapEdges) {
return enqueue(dependencies -> {
- IntegerMask source = (IntegerMask) dependencies.get(0);
+ IntegerMask source = (IntegerMask) dependencies.getFirst();
applyWithOffset(source, (TriIntConsumer) this::dividePrimitiveAt, xOffset, yOffset, center, wrapEdges);
}, other);
}
public IntegerMask addPrimitiveWithSymmetry(SymmetryType symmetryType, ToIntBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- int value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public IntegerMask subtractPrimitiveWithSymmetry(SymmetryType symmetryType, ToIntBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- int value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> subtractPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public IntegerMask multiplyPrimitiveWithSymmetry(SymmetryType symmetryType, ToIntBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- int value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyPrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> multiplyPrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public IntegerMask dividePrimitiveWithSymmetry(SymmetryType symmetryType, ToIntBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- int value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> dividePrimitiveAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> dividePrimitiveAt(x, y, valueFunction.apply(x, y)));
}
public IntegerMask applyWithOffset(IntegerMask other, TriIntConsumer action, int xOffset, int yOffset,
@@ -529,35 +518,17 @@ public IntegerMask applyWithOffset(IntegerMask other, TriIntConsumer action, int
int smallerSize = StrictMath.min(size, otherSize);
int biggerSize = StrictMath.max(size, otherSize);
if (smallerSize == otherSize) {
- if (symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
- otherSize, size);
- Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
- otherSize, size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- int value = other.getPrimitive(x, y);
- applyAtSymmetryPoints(shiftX, shiftY, SymmetryType.SPAWN,
- (sx, sy) -> action.accept(x, y, value));
- }
- });
- } else {
- applyAtSymmetryPointsWithOutOfBounds(xOffset, yOffset, SymmetryType.SPAWN, (sx, sy) -> {
- Map coordinateXMap = getShiftedCoordinateMap(sx, center, wrapEdges, otherSize,
- size);
- Map coordinateYMap = getShiftedCoordinateMap(sy, center, wrapEdges, otherSize,
- size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- action.accept(shiftX, shiftY, other.getPrimitive(x, y));
- }
- });
- });
- }
+ Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
+ otherSize, size);
+ Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
+ otherSize, size);
+ other.apply((x, y) -> {
+ int shiftX = coordinateXMap.get(x);
+ int shiftY = coordinateYMap.get(y);
+ if (inBounds(shiftX, shiftY, size)) {
+ action.accept(shiftX, shiftY, other.getPrimitive(x, y));
+ }
+ });
} else {
Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, size,
otherSize);
@@ -571,6 +542,7 @@ public IntegerMask applyWithOffset(IntegerMask other, TriIntConsumer action, int
}
});
}
+ applySymmetry(SymmetryType.SPAWN);
});
}
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/MapMaskMethods.java b/shared/src/main/java/com/faforever/neroxis/mask/MapMaskMethods.java
index 1772a8af3..8ed13ad5b 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/MapMaskMethods.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/MapMaskMethods.java
@@ -1,63 +1,51 @@
package com.faforever.neroxis.mask;
-import com.faforever.neroxis.map.SCMap;
-import com.faforever.neroxis.map.Spawn;
import com.faforever.neroxis.map.SymmetryType;
import com.faforever.neroxis.util.vector.Vector2;
-import com.faforever.neroxis.util.vector.Vector3;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-import java.util.stream.Collectors;
public class MapMaskMethods {
private MapMaskMethods() {
}
- public static BooleanMask connectTeams(SCMap map, long seed, BooleanMask exec, int minMiddlePoints,
+ public static BooleanMask connectTeams(List team0SpawnLocations, long seed, BooleanMask exec,
+ int minMiddlePoints,
int maxMiddlePoints, int numConnections, float maxStepSize) {
Random random = new Random(seed);
- List startTeamSpawns = map.getSpawns()
- .stream()
- .filter(spawn -> spawn.getTeamID() == 0)
- .collect(Collectors.toList());
for (int i = 0; i < numConnections; ++i) {
- Spawn startSpawn = startTeamSpawns.get(random.nextInt(startTeamSpawns.size()));
int numMiddlePoints;
if (maxMiddlePoints > minMiddlePoints) {
numMiddlePoints = random.nextInt(maxMiddlePoints - minMiddlePoints) + minMiddlePoints;
} else {
numMiddlePoints = maxMiddlePoints;
}
- Vector2 start = new Vector2(startSpawn.getPosition());
- Vector2 end = new Vector2(start);
+ Vector2 start = team0SpawnLocations.get(random.nextInt(team0SpawnLocations.size())).copy();
+ Vector2 end = start.copy();
float maxMiddleDistance = start.getDistance(end);
exec.connect(start, end, maxStepSize, numMiddlePoints, maxMiddleDistance, maxMiddleDistance / 2,
- (float) (StrictMath.PI / 2), SymmetryType.SPAWN);
+ (float) (StrictMath.PI / 2), SymmetryType.TERRAIN);
}
return exec;
}
- public static BooleanMask connectTeamsAroundCenter(SCMap map, long seed, BooleanMask exec, int minMiddlePoints,
+ public static BooleanMask connectTeamsAroundCenter(List team0SpawnLocations, long seed, BooleanMask exec,
+ int minMiddlePoints,
int maxMiddlePoints, int numConnections, float maxStepSize,
int bound) {
- List startTeamSpawns = map.getSpawns()
- .stream()
- .filter(spawn -> spawn.getTeamID() == 0)
- .collect(Collectors.toList());
return exec.enqueue(() -> {
Random random = new Random(seed);
for (int i = 0; i < numConnections; ++i) {
- Spawn startSpawn = startTeamSpawns.get(random.nextInt(startTeamSpawns.size()));
int numMiddlePoints;
if (maxMiddlePoints > minMiddlePoints) {
numMiddlePoints = random.nextInt(maxMiddlePoints - minMiddlePoints) + minMiddlePoints;
} else {
numMiddlePoints = maxMiddlePoints;
}
- Vector2 start = new Vector2(startSpawn.getPosition());
- Vector2 end = new Vector2(start);
+ Vector2 start = team0SpawnLocations.get(random.nextInt(team0SpawnLocations.size())).copy();
+ Vector2 end = start.copy();
float offCenterAngle = (float) (StrictMath.PI * (1f / 3f + random.nextFloat() / 3f));
offCenterAngle *= random.nextBoolean() ? 1 : -1;
offCenterAngle += start.angleTo(new Vector2(exec.getSize() / 2f, exec.getSize() / 2f));
@@ -65,28 +53,25 @@ public static BooleanMask connectTeamsAroundCenter(SCMap map, long seed, Boolean
end.clampMax(exec.getSize() - bound).clampMin(bound);
float maxMiddleDistance = start.getDistance(end);
exec.connect(start, end, maxStepSize, numMiddlePoints, maxMiddleDistance, maxMiddleDistance / 2,
- (float) (StrictMath.PI / 2), SymmetryType.SPAWN);
+ (float) (StrictMath.PI / 2), SymmetryType.TERRAIN);
}
});
}
- public static BooleanMask connectTeammates(SCMap map, long seed, BooleanMask exec, int maxMiddlePoints,
+ public static BooleanMask connectTeammates(List team0SpawnLocations, long seed, BooleanMask exec,
+ int maxMiddlePoints,
int numConnections, float maxStepSize) {
- List startTeamSpawns = map.getSpawns()
- .stream()
- .filter(spawn -> spawn.getTeamID() == 0)
- .collect(Collectors.toList());
return exec.enqueue(() -> {
Random random = new Random(seed);
- if (startTeamSpawns.size() > 1) {
- startTeamSpawns.forEach(startSpawn -> {
+ if (team0SpawnLocations.size() > 1) {
+ team0SpawnLocations.forEach(startSpawn -> {
for (int i = 0; i < numConnections; ++i) {
- ArrayList otherSpawns = new ArrayList<>(startTeamSpawns);
+ ArrayList otherSpawns = new ArrayList<>(team0SpawnLocations);
otherSpawns.remove(startSpawn);
- Spawn endSpawn = otherSpawns.get(random.nextInt(otherSpawns.size()));
+ Vector2 endSpawn = otherSpawns.get(random.nextInt(otherSpawns.size()));
int numMiddlePoints = random.nextInt(maxMiddlePoints);
- Vector2 start = new Vector2(startSpawn.getPosition());
- Vector2 end = new Vector2(endSpawn.getPosition());
+ Vector2 start = startSpawn.copy();
+ Vector2 end = endSpawn.copy();
float maxMiddleDistance = start.getDistance(end) / numMiddlePoints * 2;
exec.path(start, end, maxStepSize, numMiddlePoints, maxMiddleDistance, 0,
(float) (StrictMath.PI / 2), SymmetryType.TERRAIN);
@@ -132,12 +117,13 @@ public static BooleanMask pathInEdgeBounds(long seed, BooleanMask exec, float ma
});
}
- public static BooleanMask pathAroundSpawns(SCMap map, long seed, BooleanMask exec, float maxStepSize, int numPaths,
+ public static BooleanMask pathAroundSpawns(List team0SpawnLocations, long seed, BooleanMask exec,
+ float maxStepSize, int numPaths,
int maxMiddlePoints, int bound, float maxAngleError) {
return exec.enqueue(() -> {
Random random = new Random(seed);
- map.getSpawns().forEach(spawn -> {
- Vector2 start = new Vector2(spawn.getPosition());
+ team0SpawnLocations.forEach(spawn -> {
+ Vector2 start = spawn.copy();
for (int i = 0; i < numPaths; i++) {
int endX = (int) (random.nextFloat() * bound + start.getX());
int endY = (int) (random.nextFloat() * bound + start.getY());
@@ -145,31 +131,9 @@ public static BooleanMask pathAroundSpawns(SCMap map, long seed, BooleanMask exe
int numMiddlePoints = random.nextInt(maxMiddlePoints);
float maxMiddleDistance = start.getDistance(end) / numMiddlePoints * 2;
exec.path(start, end, maxStepSize, numMiddlePoints, maxMiddleDistance, 0, maxAngleError,
- SymmetryType.TERRAIN);
+ SymmetryType.SPAWN);
}
});
});
}
-
- public static BooleanMask fillSpawnCircle(SCMap map, BooleanMask exec, float radius) {
- return exec.enqueue(() -> {
- map.getSpawns().forEach(spawn -> {
- Vector3 location = spawn.getPosition();
- exec.fillCircle(location, radius, true);
- });
- });
- }
-
- public static BooleanMask fillSpawnCircleWithProbability(SCMap map, long seed, BooleanMask exec, float spawnSize,
- float probability) {
- return exec.enqueue(() -> {
- Random random = new Random(seed);
- if (random.nextFloat() < probability) {
- map.getSpawns().forEach(spawn -> {
- Vector3 location = spawn.getPosition();
- exec.fillCircle(location, spawnSize, true);
- });
- }
- });
- }
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/Mask.java b/shared/src/main/java/com/faforever/neroxis/mask/Mask.java
index cb96d6207..845bb9af1 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/Mask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/Mask.java
@@ -5,6 +5,7 @@
import com.faforever.neroxis.map.SymmetryType;
import com.faforever.neroxis.util.DebugUtil;
import com.faforever.neroxis.util.Pipeline;
+import com.faforever.neroxis.util.SymmetryUtil;
import com.faforever.neroxis.util.functional.BiIntConsumer;
import com.faforever.neroxis.util.functional.BiIntFunction;
import com.faforever.neroxis.util.functional.BiIntObjConsumer;
@@ -18,25 +19,29 @@
import java.awt.image.BufferedImage;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.IntUnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public abstract sealed class Mask> permits OperationsMask {
- private static final String MOCK_NAME = "Mock";
+public abstract class Mask> {
private static final String COPY_NAME = "Copy";
protected final Random random;
@Getter
private final String name;
@Getter
protected final SymmetrySettings symmetrySettings;
- private boolean immutable;
+ @Getter
+ private final boolean immutable;
private int plannedSize;
@Getter
@Setter
@@ -45,14 +50,22 @@ public abstract sealed class Mask> permits OperationsMas
@Setter
private boolean visualDebug;
private boolean visible;
- private boolean mock;
@Setter
private String visualName;
+ private int copyCount;
- protected Mask(U other, String name) {
- this(other.getSize(), (name != null && name.endsWith(MOCK_NAME)) ? null : other.getNextSeed(),
- other.getSymmetrySettings(), name, other.isParallel());
+ protected Mask(U other, String name, boolean immutable) {
+ int size = other.getSize();
+ this.symmetrySettings = other.getSymmetrySettings();
+ this.name = name == null ? String.valueOf(hashCode()) : name;
+ this.plannedSize = size;
+ this.parallel = other.isParallel();
+ Long seed = immutable ? null : other.getNextSeed();
+ random = seed != null ? new Random(seed) : null;
+ visible = !immutable;
+ initializeMask(size);
init(other);
+ this.immutable = immutable;
}
protected Mask(int size, Long seed, SymmetrySettings symmetrySettings, String name, boolean parallel) {
@@ -60,6 +73,7 @@ protected Mask(int size, Long seed, SymmetrySettings symmetrySettings, String na
this.name = name == null ? String.valueOf(hashCode()) : name;
this.plannedSize = size;
this.parallel = parallel;
+ this.immutable = false;
random = seed != null ? new Random(seed) : null;
visible = true;
initializeMask(size);
@@ -108,10 +122,6 @@ public U init(U other) {
protected abstract U copyFrom(U other);
- public boolean isMock() {
- return (name != null && name.endsWith(MOCK_NAME)) || mock;
- }
-
public int getSize() {
if (parallel && !Pipeline.isRunning()) {
return plannedSize;
@@ -180,10 +190,10 @@ protected void set(Vector2 location, T value) {
set(StrictMath.round(location.getX()), StrictMath.round(location.getY()), value);
}
- @SneakyThrows
- public U immutableCopy() {
- Mask, U> copy = copy(getName() + MOCK_NAME);
- return copy.enqueue(copy::makeImmutable);
+ public static boolean inBounds(Vector2 location, int size) {
+ int x = StrictMath.round(location.getX());
+ int y = StrictMath.round(location.getY());
+ return inBounds(x, y, size);
}
protected abstract U fill(T value);
@@ -202,11 +212,6 @@ protected U enqueue(Runnable function) {
return enqueue(ignored -> function.run());
}
- private void makeImmutable() {
- immutable = true;
- mock = true;
- }
-
/**
* Set the mask to all zeros
*
@@ -217,8 +222,10 @@ public U clear() {
}
protected U enqueue(Consumer>> function, Mask, ?>... usedMasks) {
- assertMutable();
- List> dependencies = List.of(usedMasks);
+ if (immutable) {
+ throw new IllegalStateException("Mask is immutable and cannot be modified");
+ }
+ List> dependencies = Arrays.asList(usedMasks);
if (parallel && !Pipeline.isRunning()) {
if (dependencies.stream().anyMatch(dep -> !dep.parallel)) {
throw new IllegalArgumentException("Non parallel masks used as dependents");
@@ -229,8 +236,8 @@ protected U enqueue(Consumer>> function, Mask, ?>... usedMasks
visible = false;
function.accept(dependencies);
visible = visibleState;
- if (((DebugUtil.DEBUG && isVisualDebug()) || (DebugUtil.VISUALIZE && !isMock() && !isParallel())) &&
- visible) {
+ if (((DebugUtil.DEBUG && isVisualDebug()) || (DebugUtil.VISUALIZE && !isImmutable() && !isParallel()))
+ && visible) {
String callingMethod = DebugUtil.getLastStackTraceMethodInPackage("com.faforever.neroxis.mask");
String callingLine = DebugUtil.getLastStackTraceLineAfterPackage("com.faforever.neroxis.mask");
VisualDebugger.visualizeMask(this, callingMethod, callingLine);
@@ -239,10 +246,20 @@ protected U enqueue(Consumer>> function, Mask, ?>... usedMasks
return (U) this;
}
- protected void assertMutable() {
- if (immutable) {
- throw new IllegalStateException("Mask is a mock and cannot be modified");
- }
+ private void copyValue(Vector2 source, Vector2 dest) {
+ copyValue((int) source.getX(), (int) source.getY(), (int) dest.getX(), (int) dest.getY());
+ }
+
+ private void copyValue(Vector2 source, int destX, int destY) {
+ copyValue((int) source.getX(), (int) source.getY(), destX, destY);
+ }
+
+ private void copyValue(int sourceX, int sourceY, Vector2 dest) {
+ copyValue(sourceX, sourceY, (int) dest.getX(), (int) dest.getY());
+ }
+
+ protected void copyValue(int sourceX, int sourceY, int destX, int destY) {
+ set(destX, destY, get(sourceX, sourceY));
}
protected abstract U setSizeInternal(int newSize);
@@ -256,16 +273,6 @@ public U init(BooleanMask other, T falseValue, T trueValue) {
}, other);
}
- protected void loop(BiIntConsumer maskAction) {
- assertNotPipelined();
- int size = getSize();
- for (int x = 0; x < size; x++) {
- for (int y = 0; y < size; y++) {
- maskAction.accept(x, y);
- }
- }
- }
-
protected void assertNotPipelined() {
if (parallel && !Pipeline.isRunning()) {
throw new IllegalStateException("Mask is pipelined and cannot return an immediate result");
@@ -287,36 +294,19 @@ protected static boolean inBounds(int x, int y, int size) {
return x >= 0 && x < size && y >= 0 && y < size;
}
- public boolean inTeam(int x, int y, boolean reverse) {
- return (x >= getMinXBound(SymmetryType.TEAM) &&
- x < getMaxXBound(SymmetryType.TEAM) &&
- y >= getMinYBound(x, SymmetryType.TEAM) &&
- y < getMaxYBound(x, SymmetryType.TEAM)) ^ reverse && inBounds(x, y);
- }
-
- protected int getMinXBound(SymmetryType symmetryType) {
- return 0;
+ protected int getMaxXBound(SymmetryType symmetryType) {
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ return SymmetryUtil.getMaxXBound(symmetry, getSize());
}
- protected int getMaxXBound(SymmetryType symmetryType) {
+ protected IntUnaryOperator getMinYBoundFunction(SymmetryType symmetryType) {
Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
- int size = getSize();
- return switch (symmetry) {
- case POINT3, POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13, POINT14, POINT15,
- POINT16 -> StrictMath.max(getMaxXFromAngle(360f / symmetry.getNumSymPoints()), size / 2 + 1);
- case POINT4, X, QUAD, DIAG -> size / 2;
- case POINT2, XZ, ZX, Z, NONE -> size;
- };
+ return SymmetryUtil.getMinYBoundFunction(symmetry, getSize());
}
- protected int getMinYBound(int x, SymmetryType symmetryType) {
+ protected IntUnaryOperator getMaxYBoundFunction(SymmetryType symmetryType) {
Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
- return switch (symmetry) {
- case POINT2, POINT3, POINT4, POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13,
- POINT14, POINT15, POINT16 -> getMinYFromXOnArc(x, 360f / symmetry.getNumSymPoints());
- case DIAG, XZ -> x;
- case ZX, X, Z, QUAD, NONE -> 0;
- };
+ return SymmetryUtil.getMaxYBoundFunction(symmetry, getSize());
}
/**
@@ -359,23 +349,6 @@ public U setToValue(BooleanMask area, U value) {
}, area, value);
}
- protected int getMaxYBound(int x, SymmetryType symmetryType) {
- Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
- final int size = getSize();
- return switch (symmetry) {
- case POINT3, POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13, POINT14, POINT15,
- POINT16 -> getMaxYFromXOnArc(x, 360f / symmetry.getNumSymPoints());
- case ZX, DIAG -> size - x;
- case Z, POINT2, POINT4, QUAD -> size / 2 + size % 2;
- case X, NONE, XZ -> size;
- };
- }
-
- public boolean inBounds(int x, int y) {
- int size = getSize();
- return inBounds(x, y, getSize());
- }
-
public boolean onBoundary(Vector2 location) {
return onBoundary((int) location.getX(), (int) location.getY());
}
@@ -393,10 +366,8 @@ public List getSymmetryPoints(Vector2 point, SymmetryType symmetryType)
return getSymmetryPoints(point.getX(), point.getY(), symmetryType);
}
- public List getSymmetryPoints(float x, float y, SymmetryType symmetryType) {
- List symmetryPoints = getSymmetryPointsWithOutOfBounds(x, y, symmetryType);
- symmetryPoints.removeIf(point -> !inBounds(point));
- return symmetryPoints;
+ public boolean inTeam(int x, int y, boolean reverse) {
+ return inTeamNoBounds(x, y, reverse) && inBounds(x, y, getSize());
}
public List getSymmetryPointsWithOutOfBounds(Vector3 point, SymmetryType symmetryType) {
@@ -407,62 +378,9 @@ public List getSymmetryPointsWithOutOfBounds(Vector2 point, SymmetryTyp
return getSymmetryPointsWithOutOfBounds(point.getX(), point.getY(), symmetryType);
}
- public List getSymmetryPointsWithOutOfBounds(float x, float y, SymmetryType symmetryType) {
- Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
- int numSymPoints = symmetry.getNumSymPoints();
- List symmetryPoints = new ArrayList<>(numSymPoints - 1);
- int size = getSize();
- switch (symmetry) {
- case POINT2 -> symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- case POINT4 -> {
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- symmetryPoints.add(new Vector2(y, size - x - 1));
- symmetryPoints.add(new Vector2(size - y - 1, x));
- }
- case POINT6, POINT8, POINT10, POINT12, POINT14, POINT16 -> {
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- for (int i = 1; i < numSymPoints / 2; i++) {
- float angle = (float) (2 * StrictMath.PI * i / numSymPoints);
- Vector2 rotated = getRotatedPoint(x, y, angle);
- symmetryPoints.add(rotated);
- Vector2 antiRotated = getRotatedPoint(x, y, (float) (angle + StrictMath.PI));
- symmetryPoints.add(antiRotated);
- }
- }
- case POINT3, POINT5, POINT7, POINT9, POINT11, POINT13, POINT15 -> {
- for (int i = 1; i < numSymPoints; i++) {
- Vector2 rotated = getRotatedPoint(x, y, (float) (2 * StrictMath.PI * i / numSymPoints));
- symmetryPoints.add(rotated);
- }
- }
- case X -> symmetryPoints.add(new Vector2(size - x - 1, y));
- case Z -> symmetryPoints.add(new Vector2(x, size - y - 1));
- case XZ -> symmetryPoints.add(new Vector2(y, x));
- case ZX -> symmetryPoints.add(new Vector2(size - y - 1, size - x - 1));
- case QUAD -> {
- if (symmetrySettings.teamSymmetry() == Symmetry.Z) {
- symmetryPoints.add(new Vector2(x, size - y - 1));
- symmetryPoints.add(new Vector2(size - x - 1, y));
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- } else {
- symmetryPoints.add(new Vector2(size - x - 1, y));
- symmetryPoints.add(new Vector2(x, size - y - 1));
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- }
- }
- case DIAG -> {
- if (symmetrySettings.teamSymmetry() == Symmetry.ZX) {
- symmetryPoints.add(new Vector2(size - y - 1, size - x - 1));
- symmetryPoints.add(new Vector2(y, x));
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- } else {
- symmetryPoints.add(new Vector2(y, x));
- symmetryPoints.add(new Vector2(size - y - 1, size - x - 1));
- symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
- }
- }
- }
- return symmetryPoints;
+ public List getSymmetryPoints(float x, float y, SymmetryType symmetryType) {
+ List symmetryPoints = getSymmetryPointsWithOutOfBounds(x, y, symmetryType);
+ return symmetryPoints.stream().filter(this::inBounds).toList();
}
public ArrayList getSymmetryRotation(float rot) {
@@ -542,41 +460,10 @@ public U resample(int newSize) {
}
}
- public boolean inTeamNoBounds(int x, int y, boolean reverse) {
- return (x >= getMinXBound(SymmetryType.TEAM) &&
- x < getMaxXBound(SymmetryType.TEAM) &&
- y >= getMinYBound(x, SymmetryType.TEAM) &&
- y < getMaxYBound(x, SymmetryType.TEAM)) ^ reverse;
- }
-
- private int getMaxXFromAngle(float angle) {
- int size = getSize();
- int x = (int) StrictMath.round(StrictMath.cos(((angle + 180) / 180) % 2 * StrictMath.PI) * size + size / 2f);
- return StrictMath.max(StrictMath.min(x, size), 0);
- }
-
- private int getMinYFromXOnArc(int x, float angle) {
- int size = getSize();
- float dx = x - size / 2f;
- int y;
- if (x > getMaxXFromAngle(angle)) {
- y = (int) (size / 2f + StrictMath.tan(((angle + 180) / 180) % 2 * StrictMath.PI) * dx);
- } else {
- y = (int) StrictMath.round(size / 2f - StrictMath.sqrt(size * size - dx * dx));
- }
- return StrictMath.max(StrictMath.min(y, size), 0);
- }
-
- private int getMaxYFromXOnArc(int x, float angle) {
- int size = getSize();
- float dx = x - size / 2f;
- int y;
- if (x > size / 2) {
- y = (int) (size / 2f + StrictMath.tan(((angle + 180) / 180) % 2 * StrictMath.PI) * dx);
- } else {
- y = size / 2 + 1;
- }
- return StrictMath.max(StrictMath.min(y, getSize()), 0);
+ public List getSymmetryPointsWithOutOfBounds(float x, float y, SymmetryType symmetryType) {
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ Symmetry secondarySymmetry = symmetrySettings.getSymmetry(SymmetryType.TEAM);
+ return SymmetryUtil.getSymmetryPoints(x, y, getSize(), symmetry, secondarySymmetry);
}
public boolean inTeam(Vector3 pos, boolean reverse) {
@@ -599,11 +486,24 @@ public boolean inTeamNoBounds(Vector2 pos, boolean reverse) {
return inTeam((int) pos.getX(), (int) pos.getY(), reverse);
}
+ public boolean inTeamNoBounds(int x, int y, boolean reverse) {
+ Symmetry symmetry = symmetrySettings.getSymmetry(SymmetryType.TEAM);
+ int size = getSize();
+ int maxX = SymmetryUtil.getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ return (x >= 0 && x < maxX && y >= minYBoundFunction.applyAsInt(x) && y < maxYBoundFunction.applyAsInt(x))
+ ^ reverse;
+ }
+
+ public boolean inHalf(Vector3 pos, float angle) {
+ return inHalf(new Vector2(pos), angle);
+ }
+
public boolean inHalfNoBounds(Vector2 pos, float angle) {
float halfSize = getSize() / 2f;
- float vectorAngle = (float) ((new Vector2(halfSize, halfSize).angleTo(pos) * 180f / StrictMath.PI) +
- 90f +
- 360f) % 360f;
+ float vectorAngle =
+ (float) ((new Vector2(halfSize, halfSize).angleTo(pos) * 180f / StrictMath.PI) + 90f + 360f) % 360f;
float adjustedAngle = (angle + 180f) % 360f;
if (angle >= 180) {
return (vectorAngle >= angle || vectorAngle < adjustedAngle);
@@ -612,16 +512,17 @@ public boolean inHalfNoBounds(Vector2 pos, float angle) {
}
}
- public boolean inHalf(Vector3 pos, float angle) {
- return inHalf(new Vector2(pos), angle);
+ public T get(Vector2 location) {
+ return get(StrictMath.round(location.getX()), StrictMath.round(location.getY()));
+ }
+
+ public boolean inHalfNoBounds(Vector3 pos, float angle) {
+ return inHalfNoBounds(new Vector2(pos), angle);
}
public U forceSymmetry(SymmetryType symmetryType, boolean reverse) {
if (!reverse) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = get(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> set(sx, sy, value));
- });
+ return applySymmetry(symmetryType);
} else {
if (symmetrySettings.getSymmetry(symmetryType).getNumSymPoints() != 2) {
throw new IllegalArgumentException("Symmetry has more than two symmetry points");
@@ -633,21 +534,12 @@ public U forceSymmetry(SymmetryType symmetryType, boolean reverse) {
}
}
- public T get(Vector2 location) {
- return get(StrictMath.round(location.getX()), StrictMath.round(location.getY()));
- }
-
- public boolean inHalfNoBounds(Vector3 pos, float angle) {
- return inHalfNoBounds(new Vector2(pos), angle);
- }
-
protected U applyWithSymmetry(SymmetryType symmetryType, BiIntConsumer maskAction) {
return enqueue(() -> {
- loopWithSymmetry(symmetryType, maskAction);
- if (!symmetrySettings.getSymmetry(symmetryType).isPerfectSymmetry() &&
- symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- forceSymmetry(SymmetryType.SPAWN);
- }
+ int size = getSize();
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ loopInSymmetryRegion(symmetryType, maskAction);
+ applySymmetry(symmetryType);
});
}
@@ -667,11 +559,48 @@ public boolean inHalf(int x, int y, float angle) {
return inHalf(new Vector2(x, y), angle);
}
+ protected U applySymmetry(SymmetryType symmetryType) {
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ if (symmetry == Symmetry.NONE) {
+ return (U) this;
+ }
+
+ if (symmetry.isPerfectSymmetry()) {
+ return enqueue(() -> {
+ int size = getSize();
+ loopOutsideSymmetryRegion(symmetryType, (x, y) -> {
+ Vector2 sourcePoint = SymmetryUtil.getSourcePoint(x, y, size, symmetry);
+ copyValue(sourcePoint, x, y);
+ });
+ });
+ } else {
+ return enqueue(() -> {
+ Set sourcedPoints = new HashSet<>();
+ int size = getSize();
+ loopOutsideSymmetryRegion(symmetryType, (x, y) -> {
+ Vector2 sourcePoint = SymmetryUtil.getSourcePoint(x, y, size, symmetry);
+ sourcedPoints.add(sourcePoint);
+ if (inBounds(sourcePoint)) {
+ copyValue(sourcePoint, x, y);
+ }
+ });
+ loopInSymmetryRegion(symmetryType, (x, y) -> {
+ if (!sourcedPoints.contains(new Vector2(x, y))) {
+ applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> {
+ if (inBounds(sx, sy, size)) {
+ copyValue(x, y, sx, sy);
+ }
+ });
+ }
+ });
+ });
+ }
+ }
+
public boolean inHalf(Vector2 pos, float angle) {
float halfSize = getSize() / 2f;
- float vectorAngle = (float) ((new Vector2(halfSize, halfSize).angleTo(pos) * 180f / StrictMath.PI) +
- 90f +
- 360f) % 360f;
+ float vectorAngle =
+ (float) ((new Vector2(halfSize, halfSize).angleTo(pos) * 180f / StrictMath.PI) + 90f + 360f) % 360f;
float adjustedAngle = (angle + 180f) % 360f;
if (angle >= 180) {
return (vectorAngle >= angle || vectorAngle < adjustedAngle) && inBounds(pos);
@@ -681,7 +610,9 @@ public boolean inHalf(Vector2 pos, float angle) {
}
public boolean inBounds(Vector2 location) {
- return inBounds(StrictMath.round(location.getX()), StrictMath.round(location.getY()));
+ int x = StrictMath.round(location.getX());
+ int y = StrictMath.round(location.getY());
+ return inBounds(x, y, getSize());
}
public U forceSymmetry(SymmetryType symmetryType) {
@@ -698,13 +629,12 @@ public U forceSymmetry() {
}
protected U setWithSymmetry(SymmetryType symmetryType, BiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> set(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> set(x, y, valueFunction.apply(x, y)));
+
}
- protected U applyAtSymmetryPointsWithOutOfBounds(Vector2 location, SymmetryType symmetryType, BiIntConsumer action) {
+ protected U applyAtSymmetryPointsWithOutOfBounds(Vector2 location, SymmetryType symmetryType,
+ BiIntConsumer action) {
return applyAtSymmetryPointsWithOutOfBounds((int) location.getX(), (int) location.getY(), symmetryType, action);
}
@@ -720,42 +650,25 @@ protected U applyAtSymmetryPoints(int x, int y, SymmetryType symmetryType, BiInt
});
}
- protected U applyWithOffset(U other, BiIntObjConsumer action, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ protected U applyWithOffset(U other, BiIntObjConsumer action, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(() -> {
int size = getSize();
int otherSize = other.getSize();
int smallerSize = StrictMath.min(size, otherSize);
int biggerSize = StrictMath.max(size, otherSize);
if (smallerSize == otherSize) {
- if (symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
- otherSize, size);
- Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
- otherSize, size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- T value = other.get(x, y);
- applyAtSymmetryPoints(shiftX, shiftY, SymmetryType.SPAWN,
- (sx, sy) -> action.accept(sx, sy, value));
- }
- });
- } else {
- applyAtSymmetryPointsWithOutOfBounds(xOffset, yOffset, SymmetryType.SPAWN, (sx, sy) -> {
- Map coordinateXMap = getShiftedCoordinateMap(sx, center, wrapEdges, otherSize,
- size);
- Map coordinateYMap = getShiftedCoordinateMap(sy, center, wrapEdges, otherSize,
- size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- action.accept(shiftX, shiftY, other.get(x, y));
- }
- });
- });
- }
+ Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, otherSize,
+ size);
+ Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges, otherSize,
+ size);
+ other.apply((x, y) -> {
+ int shiftX = coordinateXMap.get(x);
+ int shiftY = coordinateYMap.get(y);
+ if (inBounds(shiftX, shiftY, size)) {
+ action.accept(shiftX, shiftY, other.get(x, y));
+ }
+ });
} else {
Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, size,
otherSize);
@@ -765,11 +678,11 @@ protected U applyWithOffset(U other, BiIntObjConsumer action, int xOffset, in
int shiftX = coordinateXMap.get(x);
int shiftY = coordinateYMap.get(y);
if (inBounds(shiftX, shiftY, otherSize)) {
- T value = other.get(shiftX, shiftY);
- action.accept(x, y, value);
+ action.accept(x, y, other.get(shiftX, shiftY));
}
});
}
+ applySymmetry(SymmetryType.SPAWN);
});
}
@@ -781,7 +694,9 @@ protected U applyAtSymmetryPointsWithOutOfBounds(int x, int y, SymmetryType symm
});
}
- protected void populateCoordinateMaps(int xCoordinate, int yCoordinate, boolean center, boolean wrapEdges, int fromSize, int toSize, Map coordinateXMap, Map coordinateYMap) {
+ protected void populateCoordinateMaps(int xCoordinate, int yCoordinate, boolean center, boolean wrapEdges,
+ int fromSize, int toSize, Map coordinateXMap,
+ Map coordinateYMap) {
int offsetX;
int offsetY;
if (center) {
@@ -797,7 +712,8 @@ protected void populateCoordinateMaps(int xCoordinate, int yCoordinate, boolean
}
}
- protected Map getShiftedCoordinateMap(int offset, boolean center, boolean wrapEdges, int fromSize, int toSize) {
+ protected Map getShiftedCoordinateMap(int offset, boolean center, boolean wrapEdges, int fromSize,
+ int toSize) {
int trueOffset;
if (center) {
trueOffset = offset - fromSize / 2;
@@ -810,19 +726,50 @@ protected Map getShiftedCoordinateMap(int offset, boolean cent
.collect(Collectors.toMap(i -> i, i -> getShiftedValue(i, trueOffset, toSize, wrapEdges)));
}
- protected void loopWithSymmetry(SymmetryType symmetryType, BiIntConsumer maskAction) {
+ protected void loop(BiIntConsumer maskAction) {
+ assertNotPipelined();
+ int size = getSize();
+ for (int x = 0; x < size; x++) {
+ for (int y = 0; y < size; y++) {
+ maskAction.accept(x, y);
+ }
+ }
+ }
+
+ protected void loopInSymmetryRegion(SymmetryType symmetryType, BiIntConsumer maskAction) {
assertNotPipelined();
- int minX = getMinXBound(symmetryType);
- int maxX = getMaxXBound(symmetryType);
- for (int x = minX; x < maxX; x++) {
- int minY = getMinYBound(x, symmetryType);
- int maxY = getMaxYBound(x, symmetryType);
+ Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType);
+ int size = getSize();
+ int maxX = SymmetryUtil.getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ for (int x = 0; x < maxX; x++) {
+ int minY = minYBoundFunction.applyAsInt(x);
+ int maxY = maxYBoundFunction.applyAsInt(x);
for (int y = minY; y < maxY; y++) {
maskAction.accept(x, y);
}
}
}
+ private void loopOutsideSymmetryRegion(SymmetryType symmetryType, BiIntConsumer maskAction) {
+ assertNotPipelined();
+ Symmetry symmetry = symmetrySettings.getSymmetry(SymmetryType.TEAM);
+ int size = getSize();
+ IntUnaryOperator minYBoundFunction = SymmetryUtil.getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = SymmetryUtil.getMaxYBoundFunction(symmetry, size);
+ for (int x = 0; x < size; x++) {
+ int minY = minYBoundFunction.applyAsInt(x);
+ int maxY = maxYBoundFunction.applyAsInt(x);
+ for (int y = 0; y < minY; y++) {
+ maskAction.accept(x, y);
+ }
+ for (int y = maxY; y < size; y++) {
+ maskAction.accept(x, y);
+ }
+ }
+ }
+
protected void assertCompatibleMask(Mask, ?> other) {
int otherSize = other.getSize();
int size = getSize();
@@ -866,8 +813,12 @@ protected void assertSize(int size) {
*
* @return a copy of the mask
*/
+ @SneakyThrows
public U copy() {
- return copy(getName() + COPY_NAME);
+ Class> clazz = getClass();
+ String maskName = getName() + COPY_NAME + copyCount++;
+ return (U) clazz.getDeclaredConstructor(clazz, String.class, boolean.class).newInstance(this,
+ maskName, false);
}
public U getFinalMask() {
@@ -878,9 +829,15 @@ public U getFinalMask() {
}
@SneakyThrows
- public U copy(String maskName) {
+ private U copy(String maskName) {
Class> clazz = getClass();
- return (U) clazz.getDeclaredConstructor(clazz, String.class).newInstance(this, maskName);
+ return (U) clazz.getDeclaredConstructor(clazz, String.class, boolean.class).newInstance(this, maskName, false);
+ }
+
+ @SneakyThrows
+ public U immutableCopy() {
+ Class> clazz = getClass();
+ return (U) clazz.getDeclaredConstructor(clazz, String.class, boolean.class).newInstance(this, name, true);
}
public U startVisualDebugger() {
@@ -1004,10 +961,8 @@ public U fillArc(float x, float y, float startAngle, float endAngle, float radiu
dx = x - cx;
dy = y - cy;
float angle = (float) (StrictMath.atan2(dy, dx) / radiansToDegreeFactor + 360) % 360;
- if (inBounds(cx, cy, size) &&
- dx * dx + dy * dy <= radius2 &&
- angle >= startAngle &&
- angle <= endAngle) {
+ if (inBounds(cx, cy, size) && dx * dx + dy * dy <= radius2 && angle >= startAngle
+ && angle <= endAngle) {
set(cx, cy, value);
}
}
@@ -1092,9 +1047,9 @@ public U fillEdge(int rimWidth, T value) {
}
protected U fillCoordinates(Collection coordinates, T value) {
- coordinates.forEach(
- location -> applyAtSymmetryPoints((int) location.getX(), (int) location.getY(), SymmetryType.SPAWN,
- (x, y) -> set(x, y, value)));
- return (U) this;
+ return enqueue(() -> {
+ coordinates.forEach(location -> set((int) location.getX(), (int) location.getY(), value));
+ applySymmetry(SymmetryType.SPAWN);
+ });
}
}
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/NormalMask.java b/shared/src/main/java/com/faforever/neroxis/mask/NormalMask.java
index 8f1e9a667..5aef5494f 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/NormalMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/NormalMask.java
@@ -9,7 +9,7 @@
import java.awt.image.WritableRaster;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public final class NormalMask extends VectorMask {
+public class NormalMask extends VectorMask {
public NormalMask(int size, Long seed, SymmetrySettings symmetrySettings) {
this(size, seed, null, false);
}
@@ -22,14 +22,6 @@ public NormalMask(int size, Long seed, SymmetrySettings symmetrySettings, String
this(size, seed, name, false);
}
- public NormalMask(NormalMask other) {
- this(other, null);
- }
-
- public NormalMask(NormalMask other, String name) {
- super(other, name);
- }
-
public NormalMask(FloatMask other) {
this(other, 1f, null);
}
@@ -61,6 +53,10 @@ public NormalMask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetr
});
}
+ protected NormalMask(NormalMask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
@Override
protected Vector3 createValue(float scaleFactor, float... components) {
assertMatchingDimension(components.length);
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java b/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java
index 1f5d860bc..20fb84a4c 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java
@@ -6,13 +6,13 @@
import com.faforever.neroxis.util.vector.Vector2;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public abstract sealed class OperationsMask> extends Mask permits ComparableMask, VectorMask {
+public abstract class OperationsMask> extends Mask {
protected OperationsMask(int size, Long seed, SymmetrySettings symmetrySettings, String name, boolean parallel) {
super(size, seed, symmetrySettings, name, parallel);
}
- protected OperationsMask(U other, String name) {
- super(other, name);
+ protected OperationsMask(U other, String name, boolean immutable) {
+ super(other, name, immutable);
}
public abstract T getSum();
@@ -365,31 +365,19 @@ public U divideWithOffset(U other, int xOffset, int yOffset, boolean center, boo
}
public U addWithSymmetry(SymmetryType symmetryType, BiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addValueAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addValueAt(x, y, valueFunction.apply(x, y)));
}
public U subtractWithSymmetry(SymmetryType symmetryType, BiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractValueAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> subtractValueAt(x, y, valueFunction.apply(x, y)));
}
public U multiplyWithSymmetry(SymmetryType symmetryType, BiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyValueAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> multiplyValueAt(x, y, valueFunction.apply(x, y)));
}
public U divideWithSymmetry(SymmetryType symmetryType, BiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- T value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> divideValueAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> divideValueAt(x, y, valueFunction.apply(x, y)));
}
protected void calculateScalarInnerValue(int[][] innerCount, int x, int y, int val) {
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/PrimitiveMask.java b/shared/src/main/java/com/faforever/neroxis/mask/PrimitiveMask.java
index f247b8c7b..322c40c22 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/PrimitiveMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/PrimitiveMask.java
@@ -3,13 +3,14 @@
import com.faforever.neroxis.map.SymmetrySettings;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public abstract sealed class PrimitiveMask, U extends ComparableMask> extends ComparableMask permits BooleanMask, FloatMask, IntegerMask {
+public abstract class PrimitiveMask, U extends ComparableMask> extends
+ ComparableMask {
public PrimitiveMask(int size, Long seed, SymmetrySettings symmetrySettings, String name, boolean parallel) {
super(size, seed, symmetrySettings, name, parallel);
}
- protected PrimitiveMask(U other, String name) {
- super(other, name);
+ protected PrimitiveMask(U other, String name, boolean immutable) {
+ super(other, name, immutable);
}
protected abstract int[][] getInnerCount();
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/Vector2Mask.java b/shared/src/main/java/com/faforever/neroxis/mask/Vector2Mask.java
index 200a7e997..944f899ae 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/Vector2Mask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/Vector2Mask.java
@@ -8,7 +8,7 @@
import java.util.Arrays;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public final class Vector2Mask extends VectorMask {
+public class Vector2Mask extends VectorMask {
public Vector2Mask(int size, Long seed, SymmetrySettings symmetrySettings) {
this(size, seed, symmetrySettings, null, false);
}
@@ -30,14 +30,6 @@ public Vector2Mask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
this(size, seed, symmetrySettings, name, false);
}
- public Vector2Mask(Vector2Mask other) {
- this(other, null);
- }
-
- public Vector2Mask(Vector2Mask other, String name) {
- super(other, name);
- }
-
public Vector2Mask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetrySettings, float scaleFactor) {
this(sourceImage, seed, symmetrySettings, scaleFactor, null, false);
}
@@ -52,6 +44,10 @@ public Vector2Mask(BufferedImage sourceImage, Long seed, SymmetrySettings symmet
super(sourceImage, seed, symmetrySettings, scaleFactor, name, parallel);
}
+ protected Vector2Mask(Vector2Mask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
@Override
protected Vector2 createValue(float scaleFactor, float... components) {
assertMatchingDimension(components.length);
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/Vector3Mask.java b/shared/src/main/java/com/faforever/neroxis/mask/Vector3Mask.java
index 9f0a8cbe8..304d005a7 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/Vector3Mask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/Vector3Mask.java
@@ -7,7 +7,7 @@
import java.awt.image.WritableRaster;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public final class Vector3Mask extends VectorMask {
+public class Vector3Mask extends VectorMask {
public Vector3Mask(int size, Long seed, SymmetrySettings symmetrySettings) {
this(size, seed, symmetrySettings, null, false);
}
@@ -29,14 +29,6 @@ public Vector3Mask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
this(size, seed, symmetrySettings, name, false);
}
- public Vector3Mask(Vector3Mask other) {
- this(other, null);
- }
-
- public Vector3Mask(Vector3Mask other, String name) {
- super(other, name);
- }
-
public Vector3Mask(NormalMask other) {
this(other, null);
}
@@ -63,6 +55,10 @@ public Vector3Mask(BufferedImage sourceImage, Long seed, SymmetrySettings symmet
super(sourceImage, seed, symmetrySettings, scaleFactor, name, parallel);
}
+ protected Vector3Mask(Vector3Mask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
@Override
protected Vector3 createValue(float scaleFactor, float... components) {
assertMatchingDimension(components.length);
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/Vector4Mask.java b/shared/src/main/java/com/faforever/neroxis/mask/Vector4Mask.java
index 8438eb403..5109a0e88 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/Vector4Mask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/Vector4Mask.java
@@ -7,7 +7,7 @@
import java.awt.image.WritableRaster;
@SuppressWarnings({"UnusedReturnValue", "unused"})
-public final class Vector4Mask extends VectorMask {
+public class Vector4Mask extends VectorMask {
public Vector4Mask(int size, Long seed, SymmetrySettings symmetrySettings) {
this(size, seed, symmetrySettings, null, false);
}
@@ -29,14 +29,6 @@ public Vector4Mask(int size, Long seed, SymmetrySettings symmetrySettings, Strin
this(size, seed, symmetrySettings, name, false);
}
- public Vector4Mask(Vector4Mask other) {
- this(other, null);
- }
-
- public Vector4Mask(Vector4Mask other, String name) {
- super(other, name);
- }
-
public Vector4Mask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetrySettings, float scaleFactor) {
this(sourceImage, seed, symmetrySettings, scaleFactor, null, false);
}
@@ -51,6 +43,10 @@ public Vector4Mask(BufferedImage sourceImage, Long seed, SymmetrySettings symmet
super(sourceImage, seed, symmetrySettings, scaleFactor, name, parallel);
}
+ protected Vector4Mask(Vector4Mask other, String name, boolean immutable) {
+ super(other, name, immutable);
+ }
+
@Override
protected Vector4 createValue(float scaleFactor, float... components) {
assertMatchingDimension(components.length);
diff --git a/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java b/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java
index bdd5e8301..40424a70a 100644
--- a/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java
+++ b/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java
@@ -19,7 +19,8 @@
import java.util.Map;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "unused"})
-public abstract sealed class VectorMask, U extends VectorMask> extends OperationsMask permits NormalMask, Vector2Mask, Vector3Mask, Vector4Mask {
+public abstract class VectorMask, U extends VectorMask> extends
+ OperationsMask {
protected T[][] mask;
public VectorMask(BufferedImage sourceImage, Long seed, SymmetrySettings symmetrySettings, float scaleFactor,
@@ -54,8 +55,8 @@ public VectorMask(Long seed, String name, FloatMask... components) {
}, components);
}
- protected VectorMask(U other, String name) {
- super(other, name);
+ protected VectorMask(U other, String name, boolean immutable) {
+ super(other, name, immutable);
}
protected void assertMatchingDimension(int numImageComponents) {
@@ -83,7 +84,7 @@ public U blur(int radius) {
public U blur(int radius, BooleanMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- BooleanMask limiter = (BooleanMask) dependencies.get(0);
+ BooleanMask limiter = (BooleanMask) dependencies.getFirst();
T[][] innerCount = getInnerCount();
set((x, y) -> limiter.get(x, y) ? calculateAreaAverage(radius, x, y, innerCount).round().divide(1000) : get(
x, y));
@@ -92,7 +93,7 @@ public U blur(int radius, BooleanMask other) {
@Override
protected U copyFrom(U other) {
- return enqueue(dependencies -> fill(((U) dependencies.get(0)).mask), other);
+ return enqueue(dependencies -> fill(((U) dependencies.getFirst()).mask), other);
}
@Override
@@ -122,7 +123,7 @@ public String toHash() throws NoSuchAlgorithmException {
int size = getSize();
int dimension = get(0, 0).getDimension();
ByteBuffer bytes = ByteBuffer.allocate(size * size * 4 * dimension);
- loopWithSymmetry(SymmetryType.SPAWN, (x, y) -> {
+ loopInSymmetryRegion(SymmetryType.SPAWN, (x, y) -> {
Vector> value = get(x, y);
for (int i = 0; i < dimension; ++i) {
bytes.putFloat(value.get(i));
@@ -163,8 +164,7 @@ protected U setSizeInternal(int newSize) {
T[][] oldMask = mask;
mask = getNullMask(newSize);
Map coordinateMap = getSymmetricScalingCoordinateMap(oldSize, newSize);
- setWithSymmetry(SymmetryType.SPAWN,
- (x, y) -> oldMask[coordinateMap.get(x)][coordinateMap.get(y)].copy());
+ set((x, y) -> oldMask[coordinateMap.get(x)][coordinateMap.get(y)].copy());
}
});
}
@@ -277,11 +277,11 @@ protected void subtractScalarAt(int x, int y, float value) {
public U blurComponent(int radius, int component, BooleanMask other) {
assertCompatibleMask(other);
return enqueue(dependencies -> {
- BooleanMask limiter = (BooleanMask) dependencies.get(0);
+ BooleanMask limiter = (BooleanMask) dependencies.getFirst();
int[][] innerCount = getComponentInnerCount(component);
- setComponent(
- (x, y) -> limiter.get(x, y) ? calculateComponentAreaAverage(radius, x, y, innerCount) / 1000f : get(
- x, y).get(component), component);
+ setComponent((x, y) -> limiter.get(x, y) ?
+ calculateComponentAreaAverage(radius, x, y, innerCount) / 1000f : get(x, y).get(component),
+ component);
}, other);
}
@@ -478,31 +478,19 @@ public U divideComponent(ToFloatBiIntFunction valueFunction, int component) {
}
public U addScalarWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addScalarAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addScalarAt(x, y, valueFunction.apply(x, y)));
}
public U subtractScalarWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractScalarAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> subtractScalarAt(x, y, valueFunction.apply(x, y)));
}
public U multiplyScalarWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyScalarAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> multiplyScalarAt(x, y, valueFunction.apply(x, y)));
}
public U divideScalarWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> divideScalarAt(sx, sy, value));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> divideScalarAt(x, y, valueFunction.apply(x, y)));
}
public U addComponent(float value, int component) {
@@ -511,35 +499,29 @@ public U addComponent(float value, int component) {
public U setComponent(FloatMask other, int component) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
+ FloatMask source = (FloatMask) dependencies.getFirst();
setComponent(source::getPrimitive, component);
}, other);
}
public U setComponentWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction, int component) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> setComponentAt(sx, sy, value, component));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> setComponentAt(x, y, valueFunction.apply(x, y), component));
}
public U addComponentWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction, int component) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> addComponentAt(sx, sy, value, component));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> addComponentAt(x, y, valueFunction.apply(x, y), component));
}
public U addComponent(BooleanMask other, float value, int component) {
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
addComponent((x, y) -> source.get(x, y) ? value : 0, component);
}, other);
}
public U addComponent(FloatMask other, int component) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
+ FloatMask source = (FloatMask) dependencies.getFirst();
addComponent(source::get, component);
}, other);
}
@@ -550,30 +532,26 @@ public U subtractComponent(float value, int component) {
public U subtractComponentWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction,
int component) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> subtractComponentAt(sx, sy, value, component));
- });
+ return applyWithSymmetry(symmetryType,
+ (x, y) -> subtractComponentAt(x, y, valueFunction.apply(x, y), component));
}
public U multiplyComponentWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction,
int component) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> multiplyComponentAt(sx, sy, value, component));
- });
+ return applyWithSymmetry(symmetryType,
+ (x, y) -> multiplyComponentAt(x, y, valueFunction.apply(x, y), component));
}
public U subtractComponent(BooleanMask other, float value, int component) {
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
subtractComponent((x, y) -> source.get(x, y) ? value : 0, component);
}, other);
}
public U subtractComponent(FloatMask other, int component) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
+ FloatMask source = (FloatMask) dependencies.getFirst();
subtractComponent(source::get, component);
}, other);
}
@@ -584,22 +562,19 @@ public U multiplyComponent(float value, int component) {
protected U divideComponentWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction,
int component) {
- return applyWithSymmetry(symmetryType, (x, y) -> {
- float value = valueFunction.apply(x, y);
- applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> divideComponentAt(sx, sy, value, component));
- });
+ return applyWithSymmetry(symmetryType, (x, y) -> divideComponentAt(x, y, valueFunction.apply(x, y), component));
}
public U multiplyComponent(BooleanMask other, float value, int component) {
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
multiplyComponent((x, y) -> source.get(x, y) ? value : 0, component);
}, other);
}
public U multiplyComponent(FloatMask other, int component) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
+ FloatMask source = (FloatMask) dependencies.getFirst();
multiplyComponent(source::get, component);
}, other);
}
@@ -610,14 +585,14 @@ public U divideComponent(float value, int component) {
public U divideComponent(BooleanMask other, float value, int component) {
return enqueue(dependencies -> {
- BooleanMask source = (BooleanMask) dependencies.get(0);
+ BooleanMask source = (BooleanMask) dependencies.getFirst();
divideComponent((x, y) -> source.get(x, y) ? value : 0, component);
}, other);
}
public U divideComponent(FloatMask other, int component) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
+ FloatMask source = (FloatMask) dependencies.getFirst();
divideComponent(source::get, component);
}, other);
}
@@ -651,82 +626,66 @@ public FloatMask[] splitComponentMasks() {
return components;
}
- public U setComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ public U setComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
- applyComponentWithOffset(source, this::setComponentAt, component, xOffset, yOffset,
- center, wrapEdges);
+ FloatMask source = (FloatMask) dependencies.getFirst();
+ applyComponentWithOffset(source, this::setComponentAt, component, xOffset, yOffset, center, wrapEdges);
}, other);
}
- public U addComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ public U addComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
- applyComponentWithOffset(source, this::addComponentAt, component, xOffset, yOffset,
- center, wrapEdges);
+ FloatMask source = (FloatMask) dependencies.getFirst();
+ applyComponentWithOffset(source, this::addComponentAt, component, xOffset, yOffset, center, wrapEdges);
}, other);
}
- public U subtractComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ public U subtractComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
- applyComponentWithOffset(source, this::subtractComponentAt, component, xOffset,
- yOffset, center, wrapEdges);
+ FloatMask source = (FloatMask) dependencies.getFirst();
+ applyComponentWithOffset(source, this::subtractComponentAt, component, xOffset, yOffset, center, wrapEdges);
}, other);
}
- public U multiplyComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ public U multiplyComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
- applyComponentWithOffset(source, this::multiplyComponentAt, component, xOffset,
- yOffset, center, wrapEdges);
+ FloatMask source = (FloatMask) dependencies.getFirst();
+ applyComponentWithOffset(source, this::multiplyComponentAt, component, xOffset, yOffset, center, wrapEdges);
}, other);
}
- public U divideComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ public U divideComponentWithOffset(FloatMask other, int component, int xOffset, int yOffset, boolean center,
+ boolean wrapEdges) {
return enqueue(dependencies -> {
- FloatMask source = (FloatMask) dependencies.get(0);
- applyComponentWithOffset(source, this::divideComponentAt, component, xOffset,
- yOffset, center, wrapEdges);
+ FloatMask source = (FloatMask) dependencies.getFirst();
+ applyComponentWithOffset(source, this::divideComponentAt, component, xOffset, yOffset, center, wrapEdges);
}, other);
}
- private U applyComponentWithOffset(FloatMask other, BiIntFloatIntConsumer action, int component, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
+ private U applyComponentWithOffset(FloatMask other, BiIntFloatIntConsumer action, int component, int xOffset,
+ int yOffset, boolean center, boolean wrapEdges) {
return enqueue(() -> {
int size = getSize();
int otherSize = other.getSize();
int smallerSize = StrictMath.min(size, otherSize);
int biggerSize = StrictMath.max(size, otherSize);
if (smallerSize == otherSize) {
- if (symmetrySettings.spawnSymmetry().isPerfectSymmetry()) {
- Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges,
- otherSize, size);
- Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges,
- otherSize, size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- float value = other.getPrimitive(x, y);
- applyAtSymmetryPoints(shiftX, shiftY, SymmetryType.SPAWN,
- (sx, sy) -> action.accept(sx, sy, value, component));
- }
- });
- } else {
- applyAtSymmetryPointsWithOutOfBounds(xOffset, yOffset, SymmetryType.SPAWN, (sx, sy) -> {
- Map coordinateXMap = getShiftedCoordinateMap(sx, center, wrapEdges, otherSize,
- size);
- Map coordinateYMap = getShiftedCoordinateMap(sy, center, wrapEdges, otherSize,
- size);
- other.apply((x, y) -> {
- int shiftX = coordinateXMap.get(x);
- int shiftY = coordinateYMap.get(y);
- if (inBounds(shiftX, shiftY, size)) {
- action.accept(shiftX, shiftY, other.getPrimitive(x, y), component);
- }
- });
- });
- }
+ Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, otherSize,
+ size);
+ Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges, otherSize,
+ size);
+ other.apply((x, y) -> {
+ int shiftX = coordinateXMap.get(x);
+ int shiftY = coordinateYMap.get(y);
+ if (inBounds(shiftX, shiftY, size)) {
+ float value = other.getPrimitive(x, y);
+ action.accept(shiftX, shiftY, value, component);
+ }
+ });
} else {
Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, size,
otherSize);
diff --git a/shared/src/main/java/com/faforever/neroxis/util/MathUtil.java b/shared/src/main/java/com/faforever/neroxis/util/MathUtil.java
index c420c73f2..5c4a9f4fb 100644
--- a/shared/src/main/java/com/faforever/neroxis/util/MathUtil.java
+++ b/shared/src/main/java/com/faforever/neroxis/util/MathUtil.java
@@ -36,4 +36,8 @@ public static int binomialCoefficient(int n, int k) {
public static float log2(float val) {
return (float) (StrictMath.log(val) / StrictMath.log(2));
}
+
+ public static int clamp(int value, int min, int max) {
+ return StrictMath.max(StrictMath.min(value, max), min);
+ }
}
diff --git a/shared/src/main/java/com/faforever/neroxis/util/Pipeline.java b/shared/src/main/java/com/faforever/neroxis/util/Pipeline.java
index 5ee050617..c6460363e 100644
--- a/shared/src/main/java/com/faforever/neroxis/util/Pipeline.java
+++ b/shared/src/main/java/com/faforever/neroxis/util/Pipeline.java
@@ -45,7 +45,7 @@ public static void add(Mask, ?> executingMask, List> maskDependenci
String callingMethod = null;
String callingLine = null;
- if (DebugUtil.DEBUG) {
+ if (DebugUtil.DEBUG || DebugUtil.VISUALIZE) {
callingMethod = DebugUtil.getLastStackTraceMethodInPackage("com.faforever.neroxis.mask");
callingLine = DebugUtil.getLastStackTraceLineAfterPackage("com.faforever.neroxis.mask");
}
@@ -82,7 +82,7 @@ public static void add(Mask, ?> executingMask, List> maskDependenci
executingMask.setVisualDebug(visualDebug);
if ((DebugUtil.DEBUG && visualDebug) || (DebugUtil.VISUALIZE
&&
- !executingMask.isMock())) {
+ !executingMask.isImmutable())) {
VisualDebugger.visualizeMask(executingMask,
finalCallingMethod,
finalCallingLine);
@@ -223,9 +223,9 @@ public Entry(int index, Mask, ?> executingMask, Collection dependencies
this.methodName = method;
this.line = line;
this.future = future.thenRunAsync(() -> {
- if (!executingMask.isMock() && dependants.stream()
- .anyMatch(entry -> !entry.getExecutingMask()
- .equals(executingMask))) {
+ if (!executingMask.isImmutable() && dependants.stream()
+ .anyMatch(entry -> !entry.getExecutingMask()
+ .equals(executingMask))) {
immutableResult = executingMask.immutableCopy();
} else {
immutableResult = executingMask;
diff --git a/shared/src/main/java/com/faforever/neroxis/util/SymmetryUtil.java b/shared/src/main/java/com/faforever/neroxis/util/SymmetryUtil.java
new file mode 100644
index 000000000..2b6d2846d
--- /dev/null
+++ b/shared/src/main/java/com/faforever/neroxis/util/SymmetryUtil.java
@@ -0,0 +1,257 @@
+package com.faforever.neroxis.util;
+
+import com.faforever.neroxis.map.Symmetry;
+import com.faforever.neroxis.util.functional.SymmetryRegionBoundsChecker;
+import com.faforever.neroxis.util.vector.Vector2;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.IntUnaryOperator;
+
+public class SymmetryUtil {
+
+ public static SymmetryRegionBoundsChecker getSymmetryRegionBoundsChecker(Symmetry symmetry, int size) {
+ int maxXBound = getMaxXBound(symmetry, size);
+ IntUnaryOperator minYBoundFunction = getMinYBoundFunction(symmetry, size);
+ IntUnaryOperator maxYBoundFunction = getMaxYBoundFunction(symmetry, size);
+ return (x, y) -> x >= 0 && x < maxXBound && y >= minYBoundFunction.applyAsInt(x)
+ && y < maxYBoundFunction.applyAsInt(x);
+ }
+
+ public static int getMaxXBound(Symmetry symmetry, int size) {
+ return switch (symmetry) {
+ case POINT4, POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13, POINT14, POINT15,
+ POINT16, X, QUAD, DIAG -> size / 2 + size % 2;
+ case POINT2, POINT3, XZ, ZX, Z, NONE -> size;
+ };
+ }
+
+ public static IntUnaryOperator getMinYBoundFunction(Symmetry symmetry, int size) {
+ return switch (symmetry) {
+ case POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13, POINT14, POINT15,
+ POINT16 -> {
+ double radians = convertToRotatedRadians(360f / symmetry.getNumSymPoints());
+ double tan = StrictMath.tan(radians);
+ int halfSizeBound = size / 2 + size % 2;
+ float halfSize = size / 2f;
+ yield x -> {
+ if (x > halfSizeBound) {
+ return 0;
+ }
+
+ float dx = x - halfSize;
+ int y = (int) (halfSize + tan * dx);
+ return MathUtil.clamp(y, 0, size);
+ };
+ }
+ case DIAG, XZ -> x -> x;
+ case POINT2, POINT3, POINT4, ZX, X, Z, QUAD, NONE -> x -> 0;
+ };
+ }
+
+ public static IntUnaryOperator getMaxYBoundFunction(Symmetry symmetry, int size) {
+ return switch (symmetry) {
+ case POINT3 -> {
+ double tan = StrictMath.tan(convertToRotatedRadians(360f / symmetry.getNumSymPoints()));
+ int halfSizeBound = size / 2 + size % 2;
+ float halfSize = size / 2f;
+ yield x -> {
+ //The max Y in the first quadrant is always the halfway point
+ if (x < halfSizeBound) {
+ return halfSizeBound;
+ }
+
+ float dx = x - halfSize;
+ int y = (int) (halfSize + tan * dx);
+ return MathUtil.clamp(y, 0, halfSizeBound);
+ };
+ }
+ case X, QUAD, POINT4, POINT5, POINT6, POINT7, POINT8, POINT9, POINT10, POINT11, POINT12, POINT13, POINT14,
+ POINT15,
+ POINT16 -> {
+ int halfSizeBound = size / 2 + size % 2;
+ yield x -> x < halfSizeBound ? halfSizeBound : 0;
+ }
+ case ZX, DIAG -> x -> size - x;
+ case Z, POINT2 -> {
+ int halfSizeBound = size / 2 + size % 2;
+ yield x -> halfSizeBound;
+ }
+ case NONE, XZ -> x -> size;
+ };
+ }
+
+ static double convertToRotatedRadians(float angle) {
+ // For the map generator the 0 angle points to the left, so we have to adjust all angles by 180 degrees
+ float adjustedAngle = angle + 180;
+ return (adjustedAngle / 180) * StrictMath.PI;
+ }
+
+ public static List getSymmetryPoints(float x, float y, int size, Symmetry symmetry,
+ Symmetry secondarySymmetry) {
+ int numSymPoints = symmetry.getNumSymPoints();
+ return switch (symmetry) {
+ case NONE -> List.of();
+ case POINT2 -> List.of(new Vector2(size - x - 1, size - y - 1));
+ case POINT4 -> List.of(new Vector2(size - x - 1, size - y - 1), new Vector2(y, size - x - 1),
+ new Vector2(size - y - 1, x));
+ case POINT6, POINT8, POINT10, POINT12, POINT14, POINT16 -> {
+ List symmetryPoints = new ArrayList<>(numSymPoints - 1);
+ symmetryPoints.add(new Vector2(size - x - 1, size - y - 1));
+ for (int i = 1; i < numSymPoints / 2; i++) {
+ float angle = (float) (2 * StrictMath.PI * i / numSymPoints);
+ Vector2 rotated = getRotatedPoint(x, y, size, angle);
+ symmetryPoints.add(rotated);
+ Vector2 antiRotated = getRotatedPoint(x, y, size, (float) (angle + StrictMath.PI));
+ symmetryPoints.add(antiRotated);
+ }
+ yield symmetryPoints;
+ }
+ case POINT3, POINT5, POINT7, POINT9, POINT11, POINT13, POINT15 -> {
+ List symmetryPoints = new ArrayList<>(numSymPoints - 1);
+ for (int i = 1; i < numSymPoints; i++) {
+ Vector2 rotated = getRotatedPoint(x, y, size, (float) (2 * StrictMath.PI * i / numSymPoints));
+ symmetryPoints.add(rotated);
+ }
+ yield symmetryPoints;
+ }
+ case X -> List.of(new Vector2(size - x - 1, y));
+ case Z -> List.of(new Vector2(x, size - y - 1));
+ case XZ -> List.of(new Vector2(y, x));
+ case ZX -> List.of(new Vector2(size - y - 1, size - x - 1));
+ case QUAD -> {
+ if (secondarySymmetry == Symmetry.Z) {
+ yield List.of(new Vector2(x, size - y - 1), new Vector2(size - x - 1, y),
+ new Vector2(size - x - 1, size - y - 1));
+ } else {
+ yield List.of(new Vector2(size - x - 1, y), new Vector2(x, size - y - 1),
+ new Vector2(size - x - 1, size - y - 1));
+ }
+ }
+ case DIAG -> {
+ if (secondarySymmetry == Symmetry.ZX) {
+ yield List.of(new Vector2(size - y - 1, size - x - 1), new Vector2(y, x),
+ new Vector2(size - x - 1, size - y - 1));
+ } else {
+ yield List.of(new Vector2(y, x), new Vector2(size - y - 1, size - x - 1),
+ new Vector2(size - x - 1, size - y - 1));
+ }
+ }
+ };
+ }
+
+ public static Vector2 getSourcePoint(int x, int y, int size, Symmetry symmetry) {
+ int halfSizeBound = size / 2 + size % 2;
+ return switch (symmetry) {
+ case NONE -> new Vector2(x, y);
+ case POINT2 -> {
+ if (y >= halfSizeBound) {
+ yield new Vector2(size - x - 1, size - y - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case POINT4 -> {
+ if (x >= halfSizeBound && y >= halfSizeBound) {
+ yield new Vector2(size - x - 1, size - y - 1);
+ } else if (x <= halfSizeBound && y >= halfSizeBound) {
+ yield new Vector2(size - y - 1, x);
+ } else if (x >= halfSizeBound) {
+ yield new Vector2(y, size - x - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case POINT6, POINT8, POINT10, POINT12, POINT14, POINT16, POINT3, POINT5, POINT7, POINT9, POINT11, POINT13,
+ POINT15 -> {
+ float baseRadians = (float) (StrictMath.PI * 2f / symmetry.getNumSymPoints());
+ float dx = x - (size / 2f);
+ float dy = y - (size / 2f);
+
+ float angle = (float) (StrictMath.atan2(dy, dx) + StrictMath.PI);
+
+ float rawSlice = angle / baseRadians;
+ int slice = (int) rawSlice;
+ if (rawSlice == slice) {
+ slice--;
+ }
+ if (slice == 0) {
+ yield new Vector2(x, y);
+ } else {
+ float antiRotateAngle = -slice * baseRadians;
+ yield getRotatedPoint(x, y, size, antiRotateAngle);
+ }
+ }
+ case X -> {
+ if (x >= halfSizeBound) {
+ yield new Vector2(size - x - 1, y);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case Z -> {
+ if (y >= halfSizeBound) {
+ yield new Vector2(x, size - y - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case XZ -> {
+ if (x > y) {
+ yield new Vector2(y, x);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case ZX -> {
+ if (y > size - x - 1) {
+ yield new Vector2(size - y - 1, size - x - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ case QUAD -> {
+ if (x >= halfSizeBound) {
+ if (y >= halfSizeBound) {
+ yield new Vector2(size - x - 1, size - y - 1);
+ } else {
+ yield new Vector2(size - x - 1, y);
+ }
+ } else {
+ if (y >= halfSizeBound) {
+ yield new Vector2(x, size - y - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ }
+ case DIAG -> {
+ if (x > y) {
+ if (y > size - x - 1) {
+ yield new Vector2(size - x - 1, size - y - 1);
+ } else {
+ yield new Vector2(y, x);
+ }
+ } else {
+ if (y > size - x - 1) {
+ yield new Vector2(size - y - 1, size - x - 1);
+ } else {
+ yield new Vector2(x, y);
+ }
+ }
+ }
+ };
+ }
+
+ private static Vector2 getRotatedPoint(float x, float y, int size, float radians) {
+ float halfSize = size / 2f;
+ float xOffset = x - halfSize;
+ float yOffset = y - halfSize;
+ double cosAngle = StrictMath.cos(radians);
+ double sinAngle = StrictMath.sin(radians);
+ float newX = (float) (xOffset * cosAngle - yOffset * sinAngle + halfSize);
+ float newY = (float) (xOffset * sinAngle + yOffset * cosAngle + halfSize);
+ return new Vector2(newX, newY);
+ }
+
+}
diff --git a/shared/src/main/java/com/faforever/neroxis/util/functional/SymmetryRegionBoundsChecker.java b/shared/src/main/java/com/faforever/neroxis/util/functional/SymmetryRegionBoundsChecker.java
new file mode 100644
index 000000000..bd7151c0d
--- /dev/null
+++ b/shared/src/main/java/com/faforever/neroxis/util/functional/SymmetryRegionBoundsChecker.java
@@ -0,0 +1,13 @@
+package com.faforever.neroxis.util.functional;
+
+import com.faforever.neroxis.util.vector.Vector2;
+
+public interface SymmetryRegionBoundsChecker {
+
+ boolean inBounds(int x, int y);
+
+ default boolean inBounds(Vector2 location) {
+ return inBounds((int) location.getX(), (int) location.getY());
+ }
+
+}
diff --git a/shared/src/main/java/com/faforever/neroxis/util/functional/ToBooleanFunction.java b/shared/src/main/java/com/faforever/neroxis/util/functional/ToBooleanFunction.java
deleted file mode 100644
index e4dbaca7f..000000000
--- a/shared/src/main/java/com/faforever/neroxis/util/functional/ToBooleanFunction.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.faforever.neroxis.util.functional;
-
-@FunctionalInterface
-public interface ToBooleanFunction {
- boolean apply(T value);
-}
diff --git a/shared/src/main/java/com/faforever/neroxis/util/vector/Vector.java b/shared/src/main/java/com/faforever/neroxis/util/vector/Vector.java
index 67113ac78..dfd7fb3c6 100644
--- a/shared/src/main/java/com/faforever/neroxis/util/vector/Vector.java
+++ b/shared/src/main/java/com/faforever/neroxis/util/vector/Vector.java
@@ -1,11 +1,8 @@
package com.faforever.neroxis.util.vector;
-import lombok.EqualsAndHashCode;
-
import java.util.Arrays;
import java.util.Random;
-@EqualsAndHashCode
@SuppressWarnings("unchecked")
public abstract class Vector> {
public static final int X = 0;
@@ -340,4 +337,21 @@ public String toString() {
}
return Arrays.toString(strings).replace("[", "").replace("]", "");
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Vector> vector)) {
+ return false;
+ }
+
+ return Arrays.equals(components, vector.components);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(components);
+ }
}
diff --git a/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java b/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java
index 803de6096..074ee48cf 100644
--- a/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java
+++ b/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java
@@ -4,12 +4,19 @@
import javax.swing.*;
import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class VisualDebugger {
private static DefaultListModel listModel;
private static JFrame frame;
private static JList list;
private static EntryPanel canvas;
+ private static final Map> MASK_ITEMS_BY_NAME = new HashMap<>();
+ private static JTextField filter;
public static void visualizeMask(Mask, ?> mask) {
visualizeMask(mask, null, null);
@@ -24,8 +31,7 @@ public static void visualizeMask(Mask, ?> mask, String method, String line) {
SwingUtilities.invokeLater(() -> {
createGui();
String name = copyOfmask.getVisualName();
- name = name == null ? copyOfmask.getName() : name;
- updateList(name + " " + method + " " + line, copyOfmask.immutableCopy());
+ updateList(name, method, line, copyOfmask);
}
);
}
@@ -56,28 +62,46 @@ private static void setupList() {
list.addListSelectionListener(event -> {
if (!event.getValueIsAdjusting()) {
MaskListItem selectedItem = list.getSelectedValue();
+ if (selectedItem == null) {
+ return;
+ }
updateVisibleCanvas(selectedItem);
}
});
+
+ filter = new JTextField();
+ filter.addActionListener(event -> refreshList());
+ filter.setMinimumSize(new Dimension(350, 50));
+
+ GridBagConstraints filterConstraints = new GridBagConstraints();
+ filterConstraints.fill = GridBagConstraints.HORIZONTAL;
+ filterConstraints.gridx = 0;
+ filterConstraints.weightx = 0;
+ filterConstraints.gridy = 0;
+ filterConstraints.weighty = 0;
+
+ frame.add(filter, filterConstraints);
+
JScrollPane listScroller = new JScrollPane(list);
listScroller.setMinimumSize(new Dimension(350, 0));
listScroller.setPreferredSize(new Dimension(350, 0));
- GridBagConstraints constraints = new GridBagConstraints();
- constraints.fill = GridBagConstraints.BOTH;
- constraints.gridx = 0;
- constraints.weightx = 0;
- constraints.gridy = 0;
- constraints.weighty = 1;
+ GridBagConstraints scrollerConstraints = new GridBagConstraints();
+ scrollerConstraints.fill = GridBagConstraints.BOTH;
+ scrollerConstraints.gridx = 0;
+ scrollerConstraints.weightx = 0;
+ scrollerConstraints.gridy = 1;
+ scrollerConstraints.weighty = 1;
- frame.add(listScroller, constraints);
+ frame.add(listScroller, scrollerConstraints);
}
private static void updateVisibleCanvas(MaskListItem maskListItem) {
String maskName = maskListItem.maskName();
Mask, ?> mask = maskListItem.mask();
canvas.setMask(mask);
- frame.setTitle(String.format("Mask: %s MaskSize: %d", maskName, mask.getSize()));
+ frame.setTitle(String.format("Mask: %s Method: %s Line %s MaskSize: %d", maskName, maskListItem.method(),
+ maskListItem.line(), mask.getSize()));
}
private static void setupCanvas() {
@@ -89,31 +113,40 @@ private static void setupCanvas() {
constraints.weightx = 1;
constraints.gridy = 0;
constraints.weighty = 1;
+ constraints.gridheight = 2;
frame.add(canvas, constraints);
}
- public synchronized static void updateList(String uniqueMaskName, Mask, ?> mask) {
- if (!uniqueMaskName.isEmpty()) {
- int ind = listModel.getSize();
- for (int i = 0; i < listModel.getSize(); i++) {
- if (listModel.get(i).maskName.split(" ")[0].equals(uniqueMaskName.split(" ")[0])) {
- ind = i + 1;
- }
- }
+ private static void updateList(String maskIdentifier, String method, String line, Mask, ?> mask) {
+ MASK_ITEMS_BY_NAME.computeIfAbsent(maskIdentifier, ignored -> new ArrayList<>())
+ .add(new MaskListItem(maskIdentifier, method, line, mask));
+ refreshList();
+ }
- listModel.insertElementAt(new MaskListItem(uniqueMaskName, mask), ind);
- if (list.getSelectedIndex() == -1) {
- list.setSelectedIndex(ind);
- }
- list.revalidate();
- list.repaint();
+ private static void refreshList() {
+ MaskListItem selectedValue = list.getSelectedValue();
+ listModel.clear();
+ String text = filter.getText();
+ listModel.addAll(MASK_ITEMS_BY_NAME.entrySet()
+ .stream()
+ .filter(entry -> text.isBlank() || entry.getKey().contains(text))
+ .sorted(Map.Entry.comparingByKey())
+ .map(Map.Entry::getValue)
+ .flatMap(
+ Collection::stream)
+ .toList());
+ list.revalidate();
+ list.repaint();
+ int selected = listModel.indexOf(selectedValue);
+ if (selected != -1) {
+ list.setSelectedIndex(selected);
}
}
- public record MaskListItem(String maskName, Mask, ?> mask) {
+ private record MaskListItem(String maskName, String method, String line, Mask, ?> mask) {
@Override
public String toString() {
- return maskName;
+ return String.join(" ", maskName, method, line);
}
}
}
diff --git a/shared/src/test/java/com/faforever/neroxis/util/SymmetryUtilTest.java b/shared/src/test/java/com/faforever/neroxis/util/SymmetryUtilTest.java
new file mode 100644
index 000000000..69e0880fe
--- /dev/null
+++ b/shared/src/test/java/com/faforever/neroxis/util/SymmetryUtilTest.java
@@ -0,0 +1,159 @@
+package com.faforever.neroxis.util;
+
+import com.faforever.neroxis.map.Symmetry;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@Execution(ExecutionMode.CONCURRENT)
+public class SymmetryUtilTest {
+
+ @Test
+ public void testMaxXBound() {
+ int size = 256;
+ int halfSize = size / 2;
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.NONE, size));
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.POINT2, size));
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.XZ, size));
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.ZX, size));
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.Z, size));
+ assertEquals(size, SymmetryUtil.getMaxXBound(Symmetry.POINT3, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT4, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT5, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT6, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT7, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT9, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT10, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT8, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT11, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT12, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT13, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT14, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT15, size));
+ assertEquals(halfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT16, size));
+ int oddSize = size + 1;
+ int oddHalfSize = halfSize + 1;
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.X, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.QUAD, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.DIAG, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT4, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT5, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT6, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT7, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT8, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT9, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT10, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT11, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT12, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT13, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT14, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT15, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.POINT16, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.X, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.QUAD, oddSize));
+ assertEquals(oddHalfSize, SymmetryUtil.getMaxXBound(Symmetry.DIAG, oddSize));
+ }
+
+ @Test
+ public void testMinYBoundFunction() {
+ int size = 256;
+ int halfSize = size / 2;
+ int testPoint = halfSize + 1;
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.NONE, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT2, size).applyAsInt(testPoint));
+ assertEquals(testPoint, SymmetryUtil.getMinYBoundFunction(Symmetry.XZ, size).applyAsInt(testPoint));
+ assertEquals(testPoint, SymmetryUtil.getMinYBoundFunction(Symmetry.DIAG, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.ZX, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.Z, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT3, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT4, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT5, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT6, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT7, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT8, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT9, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT10, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT11, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT12, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT13, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT14, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT15, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMinYBoundFunction(Symmetry.POINT16, size).applyAsInt(testPoint));
+
+ int dx = 10;
+ testPoint = halfSize - dx;
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 5)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT5, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 6)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT6, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 7)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT7, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 8)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT8, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 9)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT9, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 10)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT10, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 11)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT11, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 12)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT12, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 13)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT13, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 14)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT14, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 15)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT15, size).applyAsInt(testPoint));
+ assertEquals((int) (halfSize - StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 16)) * dx),
+ SymmetryUtil.getMinYBoundFunction(Symmetry.POINT16, size).applyAsInt(testPoint));
+ }
+
+ @Test
+ public void testMaxYBoundFunction() {
+ int size = 256;
+ int halfSize = size / 2;
+ int testPoint = halfSize;
+ assertEquals(size, SymmetryUtil.getMaxYBoundFunction(Symmetry.NONE, size).applyAsInt(testPoint));
+ assertEquals(halfSize, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT2, size).applyAsInt(testPoint));
+ assertEquals(size, SymmetryUtil.getMaxYBoundFunction(Symmetry.XZ, size).applyAsInt(testPoint));
+ assertEquals(size - testPoint, SymmetryUtil.getMaxYBoundFunction(Symmetry.DIAG, size).applyAsInt(testPoint));
+ assertEquals(size - testPoint, SymmetryUtil.getMaxYBoundFunction(Symmetry.ZX, size).applyAsInt(testPoint));
+ assertEquals(halfSize, SymmetryUtil.getMaxYBoundFunction(Symmetry.Z, size).applyAsInt(testPoint));
+ assertEquals(halfSize, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT3, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.X, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.QUAD, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT4, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT5, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT6, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT7, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT8, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT9, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT10, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT11, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT12, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT13, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT14, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT15, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT16, size).applyAsInt(testPoint));
+
+ int dx = 10;
+ testPoint = halfSize + dx;
+ assertEquals((int) (halfSize + StrictMath.tan(SymmetryUtil.convertToRotatedRadians(360f / 3)) * dx),
+ SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT3, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT5, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT6, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT7, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT8, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT9, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT10, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT11, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT12, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT13, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT14, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT15, size).applyAsInt(testPoint));
+ assertEquals(0, SymmetryUtil.getMaxYBoundFunction(Symmetry.POINT16, size).applyAsInt(testPoint));
+ }
+
+}