diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 31268537..d4b86779 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 9c2fc7f0..603b826e 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 e5fedf95..f2a4fd18 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 04d40b99..cf5d6e6d 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 272ad01c..c600e8a0 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 ada02955..ce70fc3e 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); } @@ -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 21f5841d..5697675a 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 9fea44e3..09657287 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 6d19b5c4..77a4e3be 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 a19f62e7..7120eb52 100644 --- a/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java +++ b/shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java @@ -6,6 +6,7 @@ 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; @@ -29,7 +30,7 @@ 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; @@ -56,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) { @@ -126,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); @@ -234,7 +236,7 @@ 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); setPrimitive(x, y, value); }); @@ -606,13 +608,15 @@ public BooleanMask randomWalk(int numWalkers, int numSteps) { 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; for (int j = 0; j < numSteps; j++) { - if (inBounds(x, y, size)) { + if (inBounds(x, y, size) && symmetryRegionBoundsChecker.inBounds(x, y)) { setPrimitive(x, y, true); } switch (random.nextInt(4)) { @@ -714,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++) { @@ -737,9 +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)) { - setPrimitive((int) location.getX(), (int) location.getY(), 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 @@ -752,7 +761,7 @@ public BooleanMask path(Vector2 start, Vector2 end, float maxStepSize, int numMi break; } } - applySymmetry(SymmetryType.TERRAIN); + applySymmetry(symmetryType); }); } @@ -834,6 +843,8 @@ public BooleanMask progressiveWalk(int numWalkers, int numSteps) { 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); @@ -843,7 +854,7 @@ public BooleanMask progressiveWalk(int numWalkers, int numSteps) { int regressiveDir = random.nextInt(directions.size()); directions.remove(regressiveDir); for (int j = 0; j < numSteps; j++) { - if (inBounds(x, y, size)) { + if (inBounds(x, y, size) && symmetryRegionBoundsChecker.inBounds(x, y)) { setPrimitive(x, y, true); } switch (directions.get(random.nextInt(directions.size()))) { @@ -935,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(); @@ -950,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(); @@ -1141,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); 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 f5641aa8..b6a9f252 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) { 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 cca7cab0..81525b89 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 * @@ -667,7 +668,7 @@ 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)]; setPrimitive(x, y, value); }); 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 95169cbe..2de35ac9 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); } @@ -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())); } @@ -209,7 +210,7 @@ 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)]; setPrimitive(x, y, value); }); @@ -541,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 1772a8af..8ed13ad5 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 e05d6bbd..f2312f32 100644 --- a/shared/src/main/java/com/faforever/neroxis/mask/Mask.java +++ b/shared/src/main/java/com/faforever/neroxis/mask/Mask.java @@ -20,25 +20,27 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; 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 @@ -47,14 +49,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(), other.isMock() ? 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) { @@ -62,6 +72,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); @@ -110,10 +121,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; @@ -188,13 +195,6 @@ public static boolean inBounds(Vector2 location, int size) { return inBounds(x, y, size); } - @SneakyThrows - public U immutableCopy() { - Mask copy = copy(getName() + MOCK_NAME); - copy.setVisualName(getName()); - return copy.enqueue(copy::makeImmutable); - } - protected abstract U fill(T value); protected abstract T getZeroValue(); @@ -211,11 +211,6 @@ protected U enqueue(Runnable function) { return enqueue(ignored -> function.run()); } - private void makeImmutable() { - immutable = true; - mock = true; - } - /** * Set the mask to all zeros * @@ -248,14 +243,20 @@ protected U enqueue(Consumer>> function, Mask... usedMasks return (U) this; } - protected void copyValue(int sourceX, int sourceY, int destX, int destY) { - set(destX, destY, get(sourceX, sourceY)); + private void copyValue(Vector2 source, Vector2 dest) { + copyValue((int) source.getX(), (int) source.getY(), (int) dest.getX(), (int) dest.getY()); } - protected void assertMutable() { - if (immutable) { - throw new IllegalStateException("Mask is a mock and cannot be modified"); - } + 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); @@ -291,7 +292,9 @@ protected static boolean inBounds(int x, int y, int size) { } protected U enqueue(Consumer>> function, Mask... usedMasks) { - assertMutable(); + 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)) { @@ -303,7 +306,7 @@ protected U enqueue(Consumer>> function, Mask... usedMasks visible = false; function.accept(dependencies); visible = visibleState; - if (((DebugUtil.DEBUG && isVisualDebug()) || (DebugUtil.VISUALIZE && !isMock() && !isParallel())) + 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"); @@ -579,16 +582,41 @@ public boolean inHalf(int x, int y, float angle) { } protected U applySymmetry(SymmetryType symmetryType) { - return enqueue(() -> { - int size = getSize(); - Symmetry symmetry = symmetrySettings.getSymmetry(symmetryType); - loopOutsideSymmetryRegion(symmetryType, (x, y) -> { - Vector2 sourcePoint = SymmetryUtil.getSourcePoint(x, y, size, symmetry); - if (inBounds(sourcePoint, size)) { - copyValue((int) sourcePoint.getX(), (int) sourcePoint.getY(), x, y); - } + 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) { @@ -652,10 +680,10 @@ protected U applyWithOffset(U other, BiIntObjConsumer action, int xOffset, in int smallerSize = StrictMath.min(size, otherSize); int biggerSize = StrictMath.max(size, otherSize); if (smallerSize == otherSize) { - Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, - otherSize, size); - Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges, - otherSize, size); + 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); @@ -807,8 +835,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() { @@ -819,9 +851,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, boolean.class).newInstance(this, maskName, false); + } + + @SneakyThrows + public U immutableCopy() { Class clazz = getClass(); - return (U) clazz.getDeclaredConstructor(clazz, String.class).newInstance(this, maskName); + return (U) clazz.getDeclaredConstructor(clazz, String.class, boolean.class).newInstance(this, name, true); } public U startVisualDebugger() { 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 8f1e9a66..5aef5494 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 3c4bd434..20fb84a4 100644 --- a/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java +++ b/shared/src/main/java/com/faforever/neroxis/mask/OperationsMask.java @@ -6,15 +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(); 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 f247b8c7..322c40c2 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 200a7e99..944f899a 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 9f0a8cbe..304d005a 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 8438eb40..5109a0e8 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 3c249aa9..40424a70 100644 --- a/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java +++ b/shared/src/main/java/com/faforever/neroxis/mask/VectorMask.java @@ -19,12 +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, @@ -59,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) { @@ -168,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()); } }); } @@ -284,9 +279,9 @@ public U blurComponent(int radius, int component, BooleanMask other) { return enqueue(dependencies -> { 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); } @@ -635,8 +630,7 @@ public U setComponentWithOffset(FloatMask other, int component, int xOffset, int boolean wrapEdges) { return enqueue(dependencies -> { FloatMask source = (FloatMask) dependencies.getFirst(); - applyComponentWithOffset(source, this::setComponentAt, component, xOffset, yOffset, - center, wrapEdges); + applyComponentWithOffset(source, this::setComponentAt, component, xOffset, yOffset, center, wrapEdges); }, other); } @@ -644,8 +638,7 @@ public U addComponentWithOffset(FloatMask other, int component, int xOffset, int boolean wrapEdges) { return enqueue(dependencies -> { FloatMask source = (FloatMask) dependencies.getFirst(); - applyComponentWithOffset(source, this::addComponentAt, component, xOffset, yOffset, - center, wrapEdges); + applyComponentWithOffset(source, this::addComponentAt, component, xOffset, yOffset, center, wrapEdges); }, other); } @@ -653,8 +646,7 @@ public U subtractComponentWithOffset(FloatMask other, int component, int xOffset boolean wrapEdges) { return enqueue(dependencies -> { FloatMask source = (FloatMask) dependencies.getFirst(); - applyComponentWithOffset(source, this::subtractComponentAt, component, xOffset, - yOffset, center, wrapEdges); + applyComponentWithOffset(source, this::subtractComponentAt, component, xOffset, yOffset, center, wrapEdges); }, other); } @@ -662,8 +654,7 @@ public U multiplyComponentWithOffset(FloatMask other, int component, int xOffset boolean wrapEdges) { return enqueue(dependencies -> { FloatMask source = (FloatMask) dependencies.getFirst(); - applyComponentWithOffset(source, this::multiplyComponentAt, component, xOffset, - yOffset, center, wrapEdges); + applyComponentWithOffset(source, this::multiplyComponentAt, component, xOffset, yOffset, center, wrapEdges); }, other); } @@ -671,8 +662,7 @@ public U divideComponentWithOffset(FloatMask other, int component, int xOffset, boolean wrapEdges) { return enqueue(dependencies -> { FloatMask source = (FloatMask) dependencies.getFirst(); - applyComponentWithOffset(source, this::divideComponentAt, component, xOffset, - yOffset, center, wrapEdges); + applyComponentWithOffset(source, this::divideComponentAt, component, xOffset, yOffset, center, wrapEdges); }, other); } @@ -684,10 +674,10 @@ private U applyComponentWithOffset(FloatMask other, BiIntFloatIntConsumer action int smallerSize = StrictMath.min(size, otherSize); int biggerSize = StrictMath.max(size, otherSize); if (smallerSize == otherSize) { - Map coordinateXMap = getShiftedCoordinateMap(xOffset, center, wrapEdges, - otherSize, size); - Map coordinateYMap = getShiftedCoordinateMap(yOffset, center, wrapEdges, - otherSize, size); + 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); 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 5ee05061..c6460363 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 index 3034c7a7..2b6d2846 100644 --- a/shared/src/main/java/com/faforever/neroxis/util/SymmetryUtil.java +++ b/shared/src/main/java/com/faforever/neroxis/util/SymmetryUtil.java @@ -1,6 +1,7 @@ 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; @@ -9,6 +10,14 @@ 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, 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 00000000..bd7151c0 --- /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 e4dbaca7..00000000 --- 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 67113ac7..dfd7fb3c 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 52949420..074ee48c 100644 --- a/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java +++ b/shared/src/main/java/com/faforever/neroxis/visualization/VisualDebugger.java @@ -31,7 +31,7 @@ public static void visualizeMask(Mask mask, String method, String line) { SwingUtilities.invokeLater(() -> { createGui(); String name = copyOfmask.getVisualName(); - updateList(name + " " + method + " " + line, copyOfmask.immutableCopy()); + updateList(name, method, line, copyOfmask); } ); } @@ -100,7 +100,8 @@ 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() { @@ -116,9 +117,9 @@ private static void setupCanvas() { frame.add(canvas, constraints); } - private static void updateList(String uniqueMaskName, Mask mask) { - MASK_ITEMS_BY_NAME.computeIfAbsent(uniqueMaskName, ignored -> new ArrayList<>()) - .add(new MaskListItem(uniqueMaskName, mask)); + 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(); } @@ -142,10 +143,10 @@ private static void refreshList() { } } - private 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); } } }