fix: introduce approx size to patterns and use in ScatterBrush (#2631)

* fix: introduce approx size to patterns and use in ScatterBrush
 - fixes #2610

* Do not use patternsize in blockvectorset offset
This commit is contained in:
Jordan 2024-03-30 10:31:52 +01:00 committed by GitHub
parent b60a0c532c
commit 23bcdb0409
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 208 additions and 42 deletions

View File

@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.function.mask.RadiusMask;
import com.fastasyncworldedit.core.function.mask.SurfaceMask; import com.fastasyncworldedit.core.function.mask.SurfaceMask;
import com.fastasyncworldedit.core.math.BlockVectorSet; import com.fastasyncworldedit.core.math.BlockVectorSet;
import com.fastasyncworldedit.core.math.LocalBlockVectorSet; import com.fastasyncworldedit.core.math.LocalBlockVectorSet;
import com.fastasyncworldedit.core.util.collection.BlockVector3Set;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.command.tool.brush.Brush;
@ -64,8 +65,9 @@ public class ScatterBrush implements Brush {
length = 1; length = 1;
visited.add(position); visited.add(position);
} }
LocalBlockVectorSet placed = new LocalBlockVectorSet(); BlockVector3 patternSize = pattern.size();
placed.setOffset(position.getX(), position.getZ()); BlockVector3Set placed = BlockVector3Set.getAppropriateVectorSet(patternSize.add(distance, distance, distance));
placed.setOffset(position.getX(), position.getY(), position.getZ());
int maxFails = 1000; int maxFails = 1000;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int index = ThreadLocalRandom.current().nextInt(length); int index = ThreadLocalRandom.current().nextInt(length);
@ -88,7 +90,20 @@ public class ScatterBrush implements Brush {
finish(editSession, placed, position, pattern, size); finish(editSession, placed, position, pattern, size);
} }
/**
* @deprecated Use {@link ScatterBrush#finish(EditSession, BlockVector3Set, BlockVector3, Pattern, double)}
*/
@Deprecated(forRemoval = true, since = "TODO")
public void finish(EditSession editSession, LocalBlockVectorSet placed, BlockVector3 pos, Pattern pattern, double size) { public void finish(EditSession editSession, LocalBlockVectorSet placed, BlockVector3 pos, Pattern pattern, double size) {
finish(editSession, (BlockVector3Set) placed, pos, pattern, size);
}
/**
* Complete the scatter brush process.
*
* @since TODO
*/
public void finish(EditSession editSession, BlockVector3Set placed, BlockVector3 pos, Pattern pattern, double size) {
} }
public boolean canApply(BlockVector3 pos) { public boolean canApply(BlockVector3 pos) {
@ -99,8 +114,23 @@ public class ScatterBrush implements Brush {
return surface.direction(pt); return surface.direction(pt);
} }
/**
* @deprecated Use {@link ScatterBrush#apply(EditSession, BlockVector3Set, BlockVector3, Pattern, double)}
*/
@Deprecated(forRemoval = true, since = "TODO")
public void apply(EditSession editSession, LocalBlockVectorSet placed, BlockVector3 pt, Pattern p, double size) throws public void apply(EditSession editSession, LocalBlockVectorSet placed, BlockVector3 pt, Pattern p, double size) throws
MaxChangedBlocksException { MaxChangedBlocksException {
apply(editSession, (BlockVector3Set) placed, pt, p, size);
}
/**
* Apply the scatter brush to a given position
*
* @since TODO
*/
public void apply(EditSession editSession, BlockVector3Set placed, BlockVector3 pt, Pattern p, double size) throws
MaxChangedBlocksException {
editSession.setBlock(pt, p); editSession.setBlock(pt, p);
} }

View File

@ -3,6 +3,7 @@ package com.fastasyncworldedit.core.command.tool.brush;
import com.fastasyncworldedit.core.function.mask.SurfaceMask; import com.fastasyncworldedit.core.function.mask.SurfaceMask;
import com.fastasyncworldedit.core.math.LocalBlockVectorSet; import com.fastasyncworldedit.core.math.LocalBlockVectorSet;
import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.util.collection.BlockVector3Set;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
@ -24,7 +25,7 @@ public class ShatterBrush extends ScatterBrush {
@Override @Override
public void apply( public void apply(
final EditSession editSession, final EditSession editSession,
final LocalBlockVectorSet placed, final BlockVector3Set placed,
final BlockVector3 position, final BlockVector3 position,
Pattern p, Pattern p,
double size double size
@ -34,7 +35,7 @@ public class ShatterBrush extends ScatterBrush {
@Override @Override
public void finish( public void finish(
EditSession editSession, EditSession editSession,
LocalBlockVectorSet placed, BlockVector3Set placed,
final BlockVector3 position, final BlockVector3 position,
Pattern pattern, Pattern pattern,
double size double size

View File

@ -60,6 +60,13 @@ public class OffsetPattern extends AbstractPattern {
return pattern.apply(extent, get, mutable); return pattern.apply(extent, get, mutable);
} }
@Override
public BlockVector3 size() {
// Not exactly the "size" but offset should be taken into consideration in most
// places where the "size" matters
return BlockVector3.at(dx, dy, dz);
}
@Override @Override
public Pattern fork() { public Pattern fork() {
return new OffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY); return new OffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY);

View File

@ -1,7 +1,9 @@
package com.fastasyncworldedit.core.function.pattern; package com.fastasyncworldedit.core.function.pattern;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.pattern.AbstractPattern; import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
@ -9,6 +11,8 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
@ -23,6 +27,7 @@ public class RandomFullClipboardPattern extends AbstractPattern {
private final boolean randomRotate; private final boolean randomRotate;
private final boolean randomFlip; private final boolean randomFlip;
private final Vector3 flipVector = Vector3.at(1, 0, 0).multiply(-2).add(1, 1, 1); private final Vector3 flipVector = Vector3.at(1, 0, 0).multiply(-2).add(1, 1, 1);
private final BlockVector3 size;
/** /**
* Create a new {@link Pattern} instance * Create a new {@link Pattern} instance
@ -34,6 +39,12 @@ public class RandomFullClipboardPattern extends AbstractPattern {
public RandomFullClipboardPattern(List<ClipboardHolder> clipboards, boolean randomRotate, boolean randomFlip) { public RandomFullClipboardPattern(List<ClipboardHolder> clipboards, boolean randomRotate, boolean randomFlip) {
checkNotNull(clipboards); checkNotNull(clipboards);
this.clipboards = clipboards; this.clipboards = clipboards;
MutableBlockVector3 mut = new MutableBlockVector3();
clipboards.stream().flatMap(c -> c.getClipboards().stream()).map(c -> {
Region region = c.getRegion();
return region.getMaximumPoint().subtract(c.getOrigin().getMinimum(region.getMinimumPoint()));
}).forEach(mut::getMaximum);
this.size = mut.toImmutable();
this.randomRotate = randomRotate; this.randomRotate = randomRotate;
this.randomFlip = randomFlip; this.randomFlip = randomFlip;
} }
@ -66,4 +77,9 @@ public class RandomFullClipboardPattern extends AbstractPattern {
throw new IllegalStateException("Incorrect use. This pattern can only be applied to an extent!"); throw new IllegalStateException("Incorrect use. This pattern can only be applied to an extent!");
} }
@Override
public BlockVector3 size() {
return size;
}
} }

View File

@ -72,6 +72,11 @@ public class RandomOffsetPattern extends AbstractPattern {
return pattern.apply(extent, get, mutable); return pattern.apply(extent, get, mutable);
} }
@Override
public BlockVector3 size() {
return BlockVector3.at(dx2, dy2, dz2);
}
@Override @Override
public Pattern fork() { public Pattern fork() {
return new RandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY); return new RandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY);

View File

@ -129,6 +129,11 @@ public class SurfaceRandomOffsetPattern extends AbstractPattern {
return !block.getBlockType().getMaterial().isMovementBlocker(); return !block.getBlockType().getMaterial().isMovementBlocker();
} }
@Override
public BlockVector3 size() {
return BlockVector3.at(moves, moves, moves);
}
@Override @Override
public Pattern fork() { public Pattern fork() {
return new SurfaceRandomOffsetPattern(this.pattern.fork(), this.moves, this.minY, this.maxY); return new SurfaceRandomOffsetPattern(this.pattern.fork(), this.moves, this.minY, this.maxY);

View File

@ -78,6 +78,49 @@ public class BlockVectorSet extends AbstractCollection<BlockVector3> implements
return localMap != null && localMap.contains(x & 2047, ((y + 128) & 511) - 128, z & 2047); return localMap != null && localMap.contains(x & 2047, ((y + 128) & 511) - 128, z & 2047);
} }
@Override
public void setOffset(final int x, final int z) {
// Do nothing
}
@Override
public void setOffset(final int x, final int y, final int z) {
// Do nothing
}
@Override
public boolean containsRadius(final int x, final int y, final int z, final int radius) {
if (radius <= 0) {
return contains(x, y, z);
}
// Quick corners check
if (!contains(x - radius, y, z - radius)) {
return false;
}
if (!contains(x + radius, y, z + radius)) {
return false;
}
if (!contains(x - radius, y, z + radius)) {
return false;
}
if (!contains(x + radius, y, z - radius)) {
return false;
}
// Slow but if someone wants to think of an elegant way then feel free to add it
for (int xx = -radius; xx <= radius; xx++) {
int rx = x + xx;
for (int yy = -radius; yy <= radius; yy++) {
int ry = y + yy;
for (int zz = -radius; zz <= radius; zz++) {
if (contains(rx, ry, z + zz)) {
return true;
}
}
}
}
return false;
}
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
if (o instanceof BlockVector3 v) { if (o instanceof BlockVector3 v) {

View File

@ -100,14 +100,7 @@ public class LocalBlockVectorSet implements BlockVector3Set {
return new LocalBlockVectorSet(offsetX, offsetY, offsetZ, set.clone()); return new LocalBlockVectorSet(offsetX, offsetY, offsetZ, set.clone());
} }
/** @Override
* If a radius is contained by the set
*
* @param x x radius center
* @param y y radius center
* @param z z radius center
* @return if radius is contained by the set
*/
public boolean containsRadius(int x, int y, int z, int radius) { public boolean containsRadius(int x, int y, int z, int radius) {
if (radius <= 0) { if (radius <= 0) {
return contains(x, y, z); return contains(x, y, z);
@ -130,9 +123,11 @@ public class LocalBlockVectorSet implements BlockVector3Set {
return false; return false;
} }
for (int xx = -radius; xx <= radius; xx++) { for (int xx = -radius; xx <= radius; xx++) {
int rx = x + xx;
for (int yy = -radius; yy <= radius; yy++) { for (int yy = -radius; yy <= radius; yy++) {
int ry = y + yy;
for (int zz = -radius; zz <= radius; zz++) { for (int zz = -radius; zz <= radius; zz++) {
if (contains(x + xx, y + yy, z + zz)) { if (contains(rx, ry, z + zz)) {
return true; return true;
} }
} }
@ -141,27 +136,13 @@ public class LocalBlockVectorSet implements BlockVector3Set {
return false; return false;
} }
/** @Override
* Set the offset applied to values when storing and reading to keep the values within -1024 to 1023. Uses default y offset
* of 128 to allow -64 -> 320 world height use.
*
* @param x x offset
* @param z z offset
*/
public void setOffset(int x, int z) { public void setOffset(int x, int z) {
this.offsetX = x; this.offsetX = x;
this.offsetZ = z; this.offsetZ = z;
} }
/** @Override
* Set the offset applied to values when storing and reading to keep the x and z values within -1024 to 1023. Y values
* require keeping withing -256 and 255.
*
* @param x x offset
* @param y y offset
* @param z z offset
* @since 2.2.0
*/
public void setOffset(int x, int y, int z) { public void setOffset(int x, int y, int z) {
this.offsetX = x; this.offsetX = x;
this.offsetY = y; this.offsetY = y;

View File

@ -61,6 +61,22 @@ public class MutableBlockVector3 extends BlockVector3 {
return z; return z;
} }
@Override
public BlockVector3 getMinimum(BlockVector3 v2) {
this.x = Math.min(v2.getX(), x);
this.y = Math.min(v2.getY(), y);
this.z = Math.min(v2.getZ(), z);
return this;
}
@Override
public BlockVector3 getMaximum(BlockVector3 v2) {
this.x = Math.max(v2.getX(), x);
this.y = Math.max(v2.getY(), y);
this.z = Math.max(v2.getZ(), z);
return this;
}
@Override @Override
public MutableBlockVector3 mutX(double x) { public MutableBlockVector3 mutX(double x) {
this.x = (int) x; this.x = (int) x;

View File

@ -9,29 +9,80 @@ import java.util.Set;
public interface BlockVector3Set extends Set<BlockVector3> { public interface BlockVector3Set extends Set<BlockVector3> {
/**
* Get the appropriate {@link BlockVector3Set} implementation for the given region. Either {@link LocalBlockVectorSet} or
* {@link BlockVectorSet}. Sets the offset if using {@link LocalBlockVectorSet}.
*
* @param region Region to get for
* @return Appropriate {@link BlockVector3Set} implementation
*/
static BlockVector3Set getAppropriateVectorSet(Region region) { static BlockVector3Set getAppropriateVectorSet(Region region) {
BlockVector3 max = region.getMaximumPoint(); BlockVector3 max = region.getMaximumPoint();
BlockVector3 min = region.getMinimumPoint(); BlockVector3 min = region.getMinimumPoint();
BlockVector3 size = region.getDimensions(); BlockVector3Set set = getAppropriateVectorSet(region.getDimensions());
// Set default offset as many operations utilising a region are likely to start in a corner, this initialising the
// LocalBlockVectorSet poorly
// This needs to be ceiling as LocalBlockVector extends 1 block further "negative"
int offsetX = (int) Math.ceil((min.getX() + max.getX()) / 2d);
int offsetZ = (int) Math.ceil((min.getZ() + max.getZ()) / 2d);
int offsetY;
if (region.getMinimumY() < -128 || region.getMaximumY() > 320) {
offsetY = (min.getY() + max.getY()) / 2;
} else {
offsetY = 128;
}
set.setOffset(offsetX, offsetY, offsetZ);
return set;
}
/**
* Get the appropriate {@link BlockVector3Set} implementation for the given dimensions. Either {@link LocalBlockVectorSet} or
* {@link BlockVectorSet}. The offset should be manually set.
*
* @param size Dimensions to get for
* @return Appropriate {@link BlockVector3Set} implementation
*/
static BlockVector3Set getAppropriateVectorSet(BlockVector3 size) {
if (size.getBlockX() > 2048 || size.getBlockZ() > 2048 || size.getBlockY() > 512) { if (size.getBlockX() > 2048 || size.getBlockZ() > 2048 || size.getBlockY() > 512) {
return new BlockVectorSet(); return new BlockVectorSet();
} else { } else {
// Set default offset as many operations utilising a region are likely to start in a corner, this initialising the return new LocalBlockVectorSet();
// LocalBlockVectorSet poorly
// This needs to be ceiling as LocalBlockVector extends 1 block further "negative"
int offsetX = (int) Math.ceil((min.getX() + max.getX()) / 2d);
int offsetZ = (int) Math.ceil((min.getZ() + max.getZ()) / 2d);
int offsetY;
if (region.getMinimumY() < -128 || region.getMaximumY() > 320) {
offsetY = (min.getY() + max.getY()) / 2;
} else {
offsetY = 128;
}
return new LocalBlockVectorSet(offsetX, offsetY, offsetZ);
} }
} }
boolean add(int x, int y, int z); boolean add(int x, int y, int z);
boolean contains(int x, int y, int z); boolean contains(int x, int y, int z);
/**
* Set the offset applied to values when storing and reading to keep the values within -1024 to 1023. Uses default y offset
* of 128 to allow -64 -> 320 world height use.
*
* @param x x offset
* @param z z offset
* @since TODO
*/
void setOffset(int x, int z);
/**
* Set the offset applied to values when storing and reading to keep the x and z values within -1024 to 1023. Y values
* require keeping withing -256 and 255.
*
* @param x x offset
* @param y y offset
* @param z z offset
* @since TODO
*/
void setOffset(int x, int y, int z);
/**
* If a radius is contained by the set
*
* @param x x radius center
* @param y y radius center
* @param z z radius center
* @return if radius is contained by the set
* @since TODO
*/
boolean containsRadius(int x, int y, int z, int radius);
} }

View File

@ -25,6 +25,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility; import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
/** /**
@ -74,4 +75,14 @@ public interface Pattern extends Filter {
*/ */
BaseBlock applyBlock(BlockVector3 position); BaseBlock applyBlock(BlockVector3 position);
/**
* Get the likely maximum size of the volume this pattern will affect
*
* @return Pattern size
* @since TODO
*/
default BlockVector3 size() {
return BlockVector3.ONE;
}
} }