Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Odd summetry fix take2 #405

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions shared/src/main/java/com/faforever/neroxis/mask/BooleanMask.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ protected BooleanMask setSizeInternal(int newSize) {
long[] oldMask = mask;
initializeMask(newSize);
Map<Integer, Integer> 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);
});
}
});
Expand Down Expand Up @@ -1004,7 +1004,8 @@ && getPrimitive(x
*/
public BooleanMask dilute(float strength, int count) {
SymmetryType symmetryType = SymmetryType.SPAWN;
return enqueue(() -> {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
var q = enqueue(() -> {
int size = getSize();
for (int i = 0; i < count; i++) {
long[] maskCopy = getMaskCopy();
Expand All @@ -1016,6 +1017,10 @@ public BooleanMask dilute(float strength, int count) {
mask = maskCopy;
}
});
if (!isPerfectSym) {
q.enqueue(() -> apply(this::copyPrimitiveFromReverseLookup));
}
return q;
}

/**
Expand All @@ -1026,7 +1031,8 @@ public BooleanMask dilute(float strength, int count) {
*/
public BooleanMask erode(float strength, int count) {
SymmetryType symmetryType = SymmetryType.SPAWN;
return enqueue(() -> {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
var q = enqueue(() -> {
int size = getSize();
for (int i = 0; i < count; i++) {
long[] maskCopy = getMaskCopy();
Expand All @@ -1038,6 +1044,10 @@ public BooleanMask erode(float strength, int count) {
mask = maskCopy;
}
});
if (!isPerfectSym) {
q.enqueue(() -> apply(this::copyPrimitiveFromReverseLookup));
}
return q;
}

public BooleanMask addBrush(Vector2 location, String brushName, float minValue, float maxValue, int size) {
Expand Down
69 changes: 64 additions & 5 deletions shared/src/main/java/com/faforever/neroxis/mask/FloatMask.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public FloatMask addPerlinNoise(int resolution, float scale) {
FloatMask noise = new FloatMask(size, null, symmetrySettings, getName() + "PerlinNoise", isParallel());
noise.enqueue(dependencies -> {
Vector2Mask source = (Vector2Mask) dependencies.get(0);
noise.setPrimitiveWithSymmetry(SymmetryType.SPAWN, (x, y) -> {
noise.setPrimitiveWithSymmetryUsingReverseLookup(SymmetryType.SPAWN, (x, y) -> {
int xLow = (int) (x / gradientScale);
float dXLow = x / gradientScale - xLow;
int xHigh = xLow + 1;
Expand Down Expand Up @@ -237,7 +237,7 @@ public FloatMask addGaussianNoise(float scale) {
* @param scale Multiplicative factor for the noise
*/
public FloatMask addWhiteNoise(float scale) {
return addPrimitiveWithSymmetry(SymmetryType.SPAWN, (x, y) -> random.nextFloat() * scale);
return addPrimitiveWithSymmetryUsingReverseLookup(SymmetryType.SPAWN, (x, y) -> random.nextFloat() * scale);
}

/**
Expand All @@ -248,7 +248,7 @@ public FloatMask addWhiteNoise(float scale) {
*/
public FloatMask addWhiteNoise(float minValue, float maxValue) {
float range = maxValue - minValue;
return addPrimitiveWithSymmetry(SymmetryType.SPAWN, (x, y) -> random.nextFloat() * range + minValue);
return addPrimitiveWithSymmetryUsingReverseLookup(SymmetryType.SPAWN, (x, y) -> random.nextFloat() * range + minValue);
}

public FloatMask waterErode(int numDrops, int maxIterations, float friction, float speed, float erosionRate, float depositionRate, float maxOffset, float iterationScale) {
Expand Down Expand Up @@ -663,9 +663,9 @@ protected FloatMask setSizeInternal(int newSize) {
float[][] oldMask = mask;
initializeMask(newSize);
Map<Integer, Integer> 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);
});
}
});
Expand Down Expand Up @@ -954,19 +954,54 @@ public FloatMask setPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIn
});
}

public FloatMask setPrimitiveWithSymmetryUsingReverseLookup(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (isPerfectSym) {
return setPrimitiveWithSymmetry(symmetryType, valueFunction);
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
float value = valueFunction.apply(x, y);
setPrimitive(x, y, value);
}).apply(this::copyPrimitiveFromReverseLookup);
}
}

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));
});
}

public FloatMask addPrimitiveWithSymmetryUsingReverseLookup(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (isPerfectSym) {
return addPrimitiveWithSymmetry(symmetryType, valueFunction);
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
float value = valueFunction.apply(x, y);
addPrimitiveAt(x, y, value);
}).apply(this::copyPrimitiveFromReverseLookup);
}
}

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));
});
}
public FloatMask subtractPrimitiveWithSymmetryUsingReverseLookup(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (isPerfectSym) {
return subtractPrimitiveWithSymmetry(symmetryType, valueFunction);
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
float value = valueFunction.apply(x, y);
subtractPrimitiveAt(x, y, value);
}).apply(this::copyPrimitiveFromReverseLookup);
}
}

public FloatMask multiplyPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
return applyWithSymmetry(symmetryType, (x, y) -> {
Expand All @@ -975,13 +1010,37 @@ public FloatMask multiplyPrimitiveWithSymmetry(SymmetryType symmetryType, ToFloa
});
}

public FloatMask multiplyPrimitiveWithSymmetryUsingReverseLookup(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (isPerfectSym) {
return multiplyPrimitiveWithSymmetry(symmetryType, valueFunction);
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
float value = valueFunction.apply(x, y);
multiplyPrimitiveAt(x, y, value);
}).apply(this::copyPrimitiveFromReverseLookup);
}
}

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));
});
}

public FloatMask dividePrimitiveWithSymmetryUsingReverseLookup(SymmetryType symmetryType, ToFloatBiIntFunction valueFunction) {
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (isPerfectSym) {
return dividePrimitiveWithSymmetry(symmetryType, valueFunction);
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
float value = valueFunction.apply(x, y);
dividePrimitiveAt(x, y, value);
}).apply(this::copyPrimitiveFromReverseLookup);
}
}

private FloatMask applyWithOffset(FloatMask other, BiIntFloatConsumer action, int xOffset, int yOffset, boolean center, boolean wrapEdges) {
return enqueue(() -> {
int size = getSize();
Expand Down
51 changes: 47 additions & 4 deletions shared/src/main/java/com/faforever/neroxis/mask/Mask.java
Original file line number Diff line number Diff line change
Expand Up @@ -617,10 +617,16 @@ public boolean inHalf(Vector3 pos, float 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));
});
boolean isPerfectSym = symmetrySettings.getSymmetry(SymmetryType.SPAWN).isPerfectSymmetry();
if (!isPerfectSym) {
// When we don't have a perfect symmetry, we can skip this.
return enqueue(() -> {});
} else {
return applyWithSymmetry(symmetryType, (x, y) -> {
T value = get(x, y);
applyAtSymmetryPoints(x, y, symmetryType, (sx, sy) -> set(sx, sy, value));
});
}
} else {
if (symmetrySettings.getSymmetry(symmetryType).getNumSymPoints() != 2) {
throw new IllegalArgumentException("Symmetry has more than two symmetry points");
Expand Down Expand Up @@ -682,6 +688,43 @@ public boolean inBounds(Vector2 location) {
return inBounds(StrictMath.round(location.getX()), StrictMath.round(location.getY()));
}

void copyPrimitiveFromReverseLookup(int x, int y) {
int numSpawns = symmetrySettings.spawnSymmetry().getNumSymPoints();
double radiansPerSlice = StrictMath.PI * 2 / numSpawns;
int size = getSize();
int dx = x - (size / 2);
int dy = y - (size / 2);

// Find the angle of this point relative to the center of the map
double angle = StrictMath.atan2(dy, dx);
if (y < 0) {
angle = StrictMath.PI - angle;
} else {
angle = StrictMath.PI + angle;
}

// Find out what slice of the pie this pixel sits in
int slice = (int) (angle / radiansPerSlice);
if (slice > 0) {
// Find the angle we need to rotate, in order to lookup this pixels value on the original slice.
double antiRotateAngle = -slice * radiansPerSlice;

// Find the X and Y coords of this pixel in the original slice
float halfSize = size / 2f;
float xOffset = x - halfSize;
float yOffset = y - halfSize;
double cosAngle = StrictMath.cos(antiRotateAngle);
double sinAngle = StrictMath.sin(antiRotateAngle);
float antiRotatedX = (float) (xOffset * cosAngle - yOffset * sinAngle + halfSize);
float antiRotatedY = (float) (xOffset * sinAngle + yOffset * cosAngle + halfSize);

// Copy the value from the original slice
if (inBounds((int) antiRotatedX, (int) antiRotatedY)) {
set(x, y, get((int) antiRotatedX, (int) antiRotatedY));
}
}
}

public U forceSymmetry(SymmetryType symmetryType) {
return forceSymmetry(symmetryType, false);
}
Expand Down