Optimize BFS and ellipsoid iteration

This commit is contained in:
Jesse Boyd 2017-02-15 14:25:48 +11:00
parent 4caadb8ab9
commit 47b7e858ff
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
19 changed files with 426 additions and 357 deletions

View File

@ -92,6 +92,7 @@ import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.EllipsoidRegion;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.session.PasteBuilder;
import com.sk89q.worldedit.session.SessionManager;
@ -383,6 +384,7 @@ public class Fawe {
BrushTool.inject(); // Add transform
// Selectors
CuboidRegionSelector.inject(); // Translations
EllipsoidRegion.inject(); // Optimizations
// Visitors
BreadthFirstSearch.inject(); // Translations + Optimizations
DownwardVisitor.inject(); // Optimizations

View File

@ -3,7 +3,6 @@ package com.boydti.fawe.object.brush;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.clipboard.ResizableClipboardBuilder;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.function.NullRegionFunction;
import com.boydti.fawe.object.function.mask.AbstractDelegateMask;
import com.sk89q.worldedit.EditSession;
@ -44,16 +43,11 @@ public class CopyPastaBrush implements DoubleActionBrush {
final int size2 = (int) (size * size);
final int minY = position.getBlockY();
mask = new AbstractDelegateMask(mask) {
private int visited = 0;
@Override
public boolean test(Vector vector) {
if (super.test(vector) && vector.getBlockY() >= minY) {
BaseBlock block = editSession.getLazyBlock(vector);
if (block != EditSession.nullBlock) {
if (visited++ > size2) {
throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
}
builder.add(vector, EditSession.nullBlock, block);
return true;
}
@ -63,7 +57,7 @@ public class CopyPastaBrush implements DoubleActionBrush {
};
// Add origin
mask.test(position);
RecursiveVisitor visitor = new RecursiveVisitor(mask, new NullRegionFunction());
RecursiveVisitor visitor = new RecursiveVisitor(mask, new NullRegionFunction(), (int) size, editSession);
visitor.visit(position);
Operations.completeBlindly(visitor);
// Build the clipboard

View File

@ -51,7 +51,7 @@ public class RecurseBrush implements Brush {
visitor.visit(position);
Operations.completeBlindly(visitor);
} else {
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius) {
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius, editSession) {
@Override
public boolean isVisitable(Vector from, Vector to) {
int y = to.getBlockY();

View File

@ -45,7 +45,7 @@ public class BlockVectorSet extends AbstractCollection<Vector> implements Set<Ve
public boolean contains(int x, int y, int z) {
int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11));
LocalBlockVectorSet localMap = localSets.get(pair);
return localMap.contains(x & 2047, y, z & 2047);
return localMap != null && localMap.contains(x & 2047, y, z & 2047);
}
@Override
@ -126,6 +126,8 @@ public class BlockVectorSet extends AbstractCollection<Vector> implements Set<Ve
return false;
}
@Override
public boolean remove(Object o) {
if (o instanceof Vector) {
@ -177,6 +179,8 @@ public class BlockVectorSet extends AbstractCollection<Vector> implements Set<Ve
return result;
}
@Override
public void clear() {
localSets.clear();

View File

@ -23,6 +23,10 @@ public class LocalBlockVectorSet implements Set<Vector> {
this.set = new SparseBitSet();
}
public SparseBitSet getBitSet() {
return set;
}
@Override
public int size() {
return set.cardinality();
@ -46,6 +50,11 @@ public class LocalBlockVectorSet implements Set<Vector> {
return false;
}
public void addOffset(int x, int z) {
this.offsetX += x;
this.offsetZ += z;
}
public void setOffset(int x, int z) {
this.offsetX = x;
this.offsetZ = z;

View File

@ -1,26 +1,27 @@
package com.boydti.fawe.object.regions;
import com.boydti.fawe.object.visitor.FuzzySearch;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
import com.sk89q.worldedit.regions.AbstractRegion;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.world.World;
import java.util.BitSet;
import java.util.Iterator;
public class FuzzyRegion extends AbstractRegion {
private final Mask mask;
private BitSet set = new BitSet();
private BlockVectorSet set = new BlockVectorSet();
private boolean populated;
private int minX, minY, minZ, maxX, maxY, maxZ;
private int offsetX, offsetY, offsetZ;
private Extent extent;
private int count = 0;
@ -41,65 +42,28 @@ public class FuzzyRegion extends AbstractRegion {
@Override
public int getArea() {
return set.cardinality();
return set.size();
}
public void select(int x, int y, int z) {
FuzzySearch search = new FuzzySearch(this, extent, new Vector(x, y, z));
RecursiveVisitor search = new RecursiveVisitor(mask, new RegionFunction() {
@Override
public boolean apply(Vector position) throws WorldEditException {
return true;
}
}, 256, extent instanceof HasFaweQueue ? (HasFaweQueue) extent : null);
search.setVisited(set);
search.visit(new Vector(x, y, z));
Operations.completeBlindly(search);
}
@Override
public Iterator<BlockVector> iterator() {
return new Iterator<BlockVector>() {
private int index = set.nextSetBit(0);
private BlockVector pos = new BlockVector(0, 0, 0);
@Override
public boolean hasNext() {
return index != -1;
}
@Override
public BlockVector next() {
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte)(index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
pos.mutX(offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21));
pos.mutY(offsetY + b1);
pos.mutZ(offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21));
index = set.nextSetBit(index + 1);
return pos;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
return (Iterator) set.iterator();
}
public void set(int x, int y, int z) throws RegionOperationException{
if (populated) {
if (++count > 16777216) {
throw new RegionOperationException("Selection is too large! (16777216 blocks)");
}
x -= offsetX;
y -= offsetY;
z -= offsetZ;
} else {
offsetX = x;
offsetZ = z;
x = 0;
z = 0;
populated = true;
}
set.set(MathMan.tripleSearchCoords(x, y, z), true);
if (x >= 1024 || x <= -1024 || z >= 1024 || z <= -1024) {
throw new RegionOperationException("Selection is too large! (1024 blocks wide)");
}
public void set(int x, int y, int z) throws RegionOperationException {
set.add(x, y, z);
if (x > maxX) {
maxX = x;
}
@ -121,31 +85,27 @@ public class FuzzyRegion extends AbstractRegion {
}
public boolean contains(int x, int y, int z) {
try {
return set.get(MathMan.tripleSearchCoords(x - offsetX, y - offsetY, z - offsetZ));
} catch (IndexOutOfBoundsException e) {
throw new RuntimeException(e);
}
return set.contains(x, y, z);
}
@Override
public Vector getMinimumPoint() {
return new Vector(minX + offsetX, minY + offsetY, minZ + offsetZ);
return new Vector(minX, minY, minZ);
}
@Override
public Vector getMaximumPoint() {
return new Vector(maxX + offsetX, maxY + offsetY, maxZ + offsetZ);
return new Vector(maxX, maxY, maxZ);
}
@Override
public void expand(Vector... changes) throws RegionOperationException {
throw new RegionOperationException("Selection is too large!");
throw new RegionOperationException("Selection cannot expand");
}
@Override
public void contract(Vector... changes) throws RegionOperationException {
throw new RegionOperationException("Selection is too large!");
throw new RegionOperationException("Selection cannot contract");
}
@Override
@ -155,15 +115,7 @@ public class FuzzyRegion extends AbstractRegion {
@Override
public void shift(Vector change) throws RegionOperationException {
offsetX += change.getBlockX();
offsetY += change.getBlockY();
offsetZ += change.getBlockZ();
minX += change.getBlockX();
minY += change.getBlockY();
minZ += change.getBlockZ();
maxX += change.getBlockX();
maxY += change.getBlockY();
maxZ += change.getBlockZ();
throw new RegionOperationException("Selection cannot be shifted");
}
public void setExtent(EditSession extent) {

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
@ -28,7 +29,11 @@ public class AboveVisitor extends RecursiveVisitor {
* @param baseY the base Y
*/
public AboveVisitor(Mask mask, RegionFunction function, int baseY) {
super(mask, function);
this(mask, function, baseY, Integer.MAX_VALUE, null);
}
public AboveVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue);
checkNotNull(mask);
this.baseY = baseY;

View File

@ -41,7 +41,7 @@ public class Fast2DIterator implements Iterable<Vector2D> {
@Override
public Iterator<Vector2D> iterator() {
if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) {
return (Iterator<Vector2D>) iterable;
return (Iterator<Vector2D>) iterable.iterator();
}
return new Iterator<Vector2D>() {
Iterator<? extends Vector2D> trailIter = iterable.iterator();
@ -87,22 +87,6 @@ public class Fast2DIterator implements Iterable<Vector2D> {
queue.queueChunkLoad(vcx, vcz);
count++;
}
// Skip the next 15 blocks
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
}
} catch (Throwable ignore) {}
}

View File

@ -41,7 +41,7 @@ public class FastChunkIterator implements Iterable<Vector2D> {
@Override
public Iterator<Vector2D> iterator() {
if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) {
return (Iterator<Vector2D>) iterable;
return (Iterator<Vector2D>) iterable.iterator();
}
final Iterator<? extends Vector2D> trailIter = iterable.iterator();
final Iterator<? extends Vector2D> leadIter = iterable.iterator();

View File

@ -41,7 +41,7 @@ public class FastIterator implements Iterable<Vector> {
@Override
public Iterator<Vector> iterator() {
if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) {
return (Iterator<Vector>) iterable;
return (Iterator<Vector>) iterable.iterator();
}
return new Iterator<Vector>() {
Iterator<? extends Vector> trailIter = iterable.iterator();
@ -87,22 +87,6 @@ public class FastIterator implements Iterable<Vector> {
queue.queueChunkLoad(vcx, vcz);
count++;
}
// Skip the next 15 blocks
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
}
} catch (Throwable ignore) {}
}

View File

@ -1,106 +0,0 @@
package com.boydti.fawe.object.visitor;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.collection.SparseBitSet;
import com.boydti.fawe.object.regions.FuzzyRegion;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.regions.RegionOperationException;
import java.util.List;
public class FuzzySearch implements Operation {
private final FuzzyRegion region;
private final SparseBitSet visited;
private final SparseBitSet queue;
private final int offsetZ;
private final int offsetX;
private final Extent extent;
private final int maxY;
private int affected;
public FuzzySearch(FuzzyRegion region, Extent extent, Vector origin) {
this.region = region;
this.queue = new SparseBitSet();
this.visited = new SparseBitSet();
this.offsetX = origin.getBlockX();
this.offsetZ = origin.getBlockZ();
this.queue.set(MathMan.tripleSearchCoords(0, origin.getBlockY(), 0));
this.extent = extent;
this.maxY = extent.getMaximumPoint().getBlockY();
}
private boolean hasVisited(int x, int y, int z) {
return visited.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ));
}
private void queueVisit(int x, int y, int z) throws RegionOperationException {
if (y < 0 || y > maxY) {
return;
}
int ox = x - offsetX;
if (ox >= 1024 || ox < -1024) {
throw new RegionOperationException("Selection is too large! (1024 blocks wide)");
}
int oz = z - offsetZ;
if (oz >= 1024 || oz < -1024) {
throw new RegionOperationException("Selection is too large! (1024 blocks wide)");
}
int index = MathMan.tripleSearchCoords(ox, y, oz);
if (!visited.get(index)) {
visited.set(index);
queue.set(index);
}
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
Vector pos = new Vector();
Mask mask = region.getMask();
int index = 0;
while ((index = queue.nextSetBit(index)) != -1 || (index = queue.nextSetBit(0)) != -1) {
queue.clear(index);
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte)(index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
int x = offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21);
int y = b1;
int z = offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21);
pos.mutX(x);
pos.mutY(y);
pos.mutZ(z);
if (mask.test(pos)) {
affected++;
region.set(x, y, z);
queueVisit(x + 1, y, z);
queueVisit(x - 1, y, z);
queueVisit(x, y + 1, z);
queueVisit(x, y - 1, z);
queueVisit(x, y, z + 1);
queueVisit(x, y, z - 1);
}
}
return null;
}
@Override
public void cancel() {
}
public int getAffected() {
return affected;
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(BBC.VISITOR_BLOCK.format(getAffected()));
}
}

View File

@ -1463,9 +1463,9 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
// Pick how we're going to visit blocks
RecursiveVisitor visitor;
if (recursive) {
visitor = new RecursiveVisitor(mask, replace);
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
} else {
visitor = new DownwardVisitor(mask, replace, origin.getBlockY());
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), this);
}
// Start at the origin
@ -2016,7 +2016,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
new Vector(radius,radius, radius))), liquidMask);
final BlockReplace replace = new BlockReplace(EditSession.this, new BlockPattern(new BaseBlock(BlockID.AIR)));
final RecursiveVisitor visitor = new RecursiveVisitor(mask, replace);
final RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
// Around the origin in a 3x3 block
for (final BlockVector position : CuboidRegion.fromCenter(origin, 1)) {
@ -2075,7 +2075,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
blockMask);
BlockReplace replace = new BlockReplace(this, new BlockPattern(FaweCache.getBlock(stationary, 0)));
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace);
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace, (int) (radius * 2 + 1), this);
// Around the origin in a 3x3 block
for (BlockVector position : CuboidRegion.fromCenter(origin, 1)) {

View File

@ -3,6 +3,10 @@ package com.sk89q.worldedit;
public class MutableBlockVector extends BlockVector {
private int x,y,z;
public MutableBlockVector(Vector v) {
this(v.getBlockX(), v.getBlockY(), v.getBlockZ());
}
public MutableBlockVector(int x, int y, int z) {
super(0, 0, 0);
this.x = x;

View File

@ -55,7 +55,7 @@ public class RecursivePickaxe implements BlockTool {
final int radius = (int) range;
final BlockReplace replace = new BlockReplace(editSession, new BlockPattern(editSession.nullBlock));
editSession.setMask((Mask) null);
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius);
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius, editSession);
visitor.visit(pos);
Operations.completeBlindly(visitor);

View File

@ -1,27 +1,29 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.IntegerTrio;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public abstract class BreadthFirstSearch implements Operation {
private final RegionFunction function;
private final List<Vector> directions = new ArrayList<>();
private final Map<Node, Integer> visited;
private final ArrayDeque<Node> queue;
private BlockVectorSet visited;
private final MappedFaweQueue mFaweQueue;
private BlockVectorSet queue;
private final int maxDepth;
private int affected = 0;
@ -30,8 +32,14 @@ public abstract class BreadthFirstSearch implements Operation {
}
public BreadthFirstSearch(final RegionFunction function, int maxDepth) {
this.queue = new ArrayDeque<>();
this.visited = new LinkedHashMap<>();
this(function, maxDepth, null);
}
public BreadthFirstSearch(final RegionFunction function, int maxDepth, HasFaweQueue faweQueue) {
FaweQueue fq = faweQueue != null ? faweQueue.getQueue() : null;
this.mFaweQueue = fq instanceof MappedFaweQueue ? (MappedFaweQueue) fq : null;
this.queue = new BlockVectorSet();
this.visited = new BlockVectorSet();
this.function = function;
this.directions.add(new Vector(0, -1, 0));
this.directions.add(new Vector(0, 1, 0));
@ -58,85 +66,88 @@ public abstract class BreadthFirstSearch implements Operation {
}
public void visit(final Vector pos) {
Node node = new Node(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
if (!isVisited(node)) {
if (!isVisited(pos)) {
isVisitable(pos, pos); // Ignore this, just to initialize mask on this point
queue.add(node);
addVisited(node, 0);
queue.add(pos);
visited.add(pos);
}
}
public void addVisited(Node node, int depth) {
visited.put(node, depth);
public void setVisited(BlockVectorSet set) {
this.visited = set;
}
public boolean isVisited(Node node) {
return visited.containsKey(node);
public BlockVectorSet getVisited() {
return visited;
}
public void cleanVisited(int layer) {
if (layer == Integer.MAX_VALUE) {
visited.clear();
return;
}
int size = visited.size();
if (size > 16384) {
Iterator<Map.Entry<Node, Integer>> iter = visited.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Node, Integer> entry = iter.next();
Integer val = entry.getValue();
if (val < layer) {
iter.remove();
} else {
break;
}
}
}
public boolean isVisited(Vector pos) {
return visited.contains(pos);
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
Node from;
Node adjacent;
MutableBlockVector mutable = new MutableBlockVector();
Vector mutable2 = new Vector();
MutableBlockVector mutable2 = new MutableBlockVector();
boolean shouldTrim = false;
IntegerTrio[] dirs = getIntDirections();
BlockVectorSet tempQueue = new BlockVectorSet();
BlockVectorSet chunkLoadSet = new BlockVectorSet();
for (int layer = 0; !queue.isEmpty() && layer <= maxDepth; layer++) {
int size = queue.size();
if (layer == maxDepth) {
cleanVisited(Integer.MAX_VALUE);
for (Node current : queue) {
mutable.mutX(current.getX());
mutable.mutY(current.getY());
mutable.mutZ(current.getZ());
function.apply(mutable);
affected++;
if (mFaweQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) {
int cx = Integer.MIN_VALUE;
int cz = Integer.MIN_VALUE;
for (Vector from : queue) {
for (IntegerTrio direction : dirs) {
int x = from.getBlockX() + direction.x;
int z = from.getBlockZ() + direction.z;
if (cx != (cx = x >> 4) || cz != (cz = z >> 4)) {
int y = from.getBlockY() + direction.y;
if (y < 0 || y >= 256) {
continue;
}
if (!visited.contains(x, y, z)) {
chunkLoadSet.add(cx, 0, cz);
}
}
}
}
for (Vector chunk : chunkLoadSet) {
mFaweQueue.queueChunkLoad(chunk.getBlockX(), chunk.getBlockZ());
}
break;
}
for (int i = 0; i < size; i++) {
from = queue.poll();
mutable.mutX(from.getX());
mutable.mutY(from.getY());
mutable.mutZ(from.getZ());
function.apply(mutable);
affected++;
for (Vector from : queue) {
if (function.apply(from)) affected++;
for (IntegerTrio direction : dirs) {
mutable2.mutX(from.getX() + direction.x);
mutable2.mutY(from.getY() + direction.y);
mutable2.mutZ(from.getZ() + direction.z);
if (isVisitable(mutable, mutable2)) {
adjacent = new Node(mutable2.getBlockX(), mutable2.getBlockY(), mutable2.getBlockZ());
if (!isVisited(adjacent)) {
addVisited(adjacent, layer);
queue.add(adjacent);
int y = from.getBlockY() + direction.y;
if (y < 0 || y >= 256) {
continue;
}
int x = from.getBlockX() + direction.x;
int z = from.getBlockZ() + direction.z;
if (!visited.contains(x, y, z)) {
mutable2.mutX(x);
mutable2.mutY(y);
mutable2.mutZ(z);
if (isVisitable(from, mutable2)) {
visited.add(x, y, z);
tempQueue.add(x, y, z);
}
}
}
}
int lastLayer = layer - 1;
cleanVisited(lastLayer);
if (layer == maxDepth) {
break;
}
int size = queue.size();
BlockVectorSet tmp = queue;
queue = tempQueue;
tmp.clear();
chunkLoadSet.clear();
tempQueue = tmp;
}
return null;
}
@ -154,64 +165,6 @@ public abstract class BreadthFirstSearch implements Operation {
public void cancel() {
}
public static final class Node {
private int x,y,z;
public Node(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public Node(Node node) {
this.x = node.x;
this.y = node.y;
this.z = node.z;
}
public Node() {}
public final void set(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public final void set(Node node) {
this.x = node.x;
this.y = node.y;
this.z = node.z;
}
@Override
public final int hashCode() {
return (x ^ (z << 12)) ^ (y << 24);
}
public final int getX() {
return x;
}
public final int getY() {
return y;
}
public final int getZ() {
return z;
}
@Override
public String toString() {
return x + "," + y + "," + z;
}
@Override
public boolean equals(Object obj) {
Node other = (Node) obj;
return other.x == x && other.z == z && other.y == y;
}
}
public static Class<?> inject() {
return BreadthFirstSearch.class;
}

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operations;
import java.util.Collection;
@ -46,8 +46,12 @@ public class DownwardVisitor extends RecursiveVisitor {
* @param function the function
* @param baseY the base Y
*/
public DownwardVisitor(final Mask mask, final RegionFunction function, final int baseY) {
super(mask, function);
public DownwardVisitor(Mask mask, RegionFunction function, int baseY) {
this(mask, function, baseY, Integer.MAX_VALUE, null);
}
public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue);
checkNotNull(mask);
this.baseY = baseY;

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operations;
import java.util.Collection;
/**
@ -36,8 +36,13 @@ public class NonRisingVisitor extends RecursiveVisitor {
* @param mask the mask
* @param function the function
*/
public NonRisingVisitor(final Mask mask, final RegionFunction function) {
super(mask, function);
public NonRisingVisitor(Mask mask, RegionFunction function) {
this(mask, function, Integer.MAX_VALUE, null);
}
public NonRisingVisitor(Mask mask, RegionFunction function, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue);
final Collection<Vector> directions = this.getDirections();
directions.clear();
directions.add(new Vector(1, 0, 0));

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
@ -45,7 +46,11 @@ public class RecursiveVisitor extends BreadthFirstSearch {
* @param function the function
*/
public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth) {
super(function, maxDepth);
this(mask, function, maxDepth, null);
}
public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth, HasFaweQueue faweQueue) {
super(function, maxDepth, faweQueue);
checkNotNull(mask);
this.mask = mask;
}

View File

@ -0,0 +1,270 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.regions;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.storage.ChunkStore;
import java.util.HashSet;
import java.util.Set;
/**
* Represents an ellipsoid region.
*/
public class EllipsoidRegion extends AbstractRegion {
/**
* Stores the center.
*/
private MutableBlockVector center;
/**
* Stores the radii plus 0.5 on each axis.
*/
private MutableBlockVector radius;
private MutableBlockVector radiusSqr;
private int radiusLengthSqr;
private boolean sphere;
/**
* Construct a new instance of this ellipsoid region.
*
* @param pos1 the first position
* @param pos2 the second position
*/
public EllipsoidRegion(Vector pos1, Vector pos2) {
this(null, pos1, pos2);
}
@Deprecated
public EllipsoidRegion(LocalWorld world, Vector center, Vector radius) {
this((World) world, center, radius);
}
/**
* Construct a new instance of this ellipsoid region.
*
* @param world the world
* @param center the center
* @param radius the radius
*/
public EllipsoidRegion(World world, Vector center, Vector radius) {
super(world);
this.center = new MutableBlockVector(center);
setRadius(radius);
}
public EllipsoidRegion(EllipsoidRegion ellipsoidRegion) {
this(ellipsoidRegion.world, ellipsoidRegion.center, ellipsoidRegion.getRadius());
}
@Override
public Vector getMinimumPoint() {
return center.subtract(getRadius()).clampY(0, 255);
}
@Override
public Vector getMaximumPoint() {
return center.add(getRadius()).clampY(0, 255);
}
@Override
public int getArea() {
return (int) Math.floor((4.0 / 3.0) * Math.PI * radius.getX() * radius.getY() * radius.getZ());
}
@Override
public int getWidth() {
return (int) (2 * radius.getX());
}
@Override
public int getHeight() {
return Math.max((int) (2 * radius.getY()), 256);
}
@Override
public int getLength() {
return (int) (2 * radius.getZ());
}
private Vector calculateDiff(Vector... changes) throws RegionOperationException {
Vector diff = new Vector().add(changes);
if ((diff.getBlockX() & 1) + (diff.getBlockY() & 1) + (diff.getBlockZ() & 1) != 0) {
throw new RegionOperationException(
"Ellipsoid changes must be even for each dimensions.");
}
return diff.divide(2).floor();
}
private Vector calculateChanges(Vector... changes) {
Vector total = new Vector();
for (Vector change : changes) {
total = total.add(change.positive());
}
return total.divide(2).floor();
}
@Override
public void expand(Vector... changes) throws RegionOperationException {
center = new MutableBlockVector(center.add(calculateDiff(changes)));
setRadius(radius.add(calculateChanges(changes)));
}
@Override
public void contract(Vector... changes) throws RegionOperationException {
center = new MutableBlockVector(center.subtract(calculateDiff(changes)));
Vector newRadius = radius.subtract(calculateChanges(changes));
setRadius(Vector.getMaximum(new Vector(1.5, 1.5, 1.5), newRadius));
}
@Override
public void shift(Vector change) throws RegionOperationException {
center = new MutableBlockVector(center.add(change));
}
/**
* Get the center.
*
* @return center
*/
@Override
public Vector getCenter() {
return center;
}
/**
* Set the center.
*
* @param center the center
*/
public void setCenter(Vector center) {
this.center = new MutableBlockVector(center);
}
/**
* Get the radii.
*
* @return radii
*/
public Vector getRadius() {
return radius.subtract(0.5, 0.5, 0.5);
}
/**
* Set the radii.
*
* @param radius the radius
*/
public void setRadius(Vector radius) {
this.radius = new MutableBlockVector(radius.add(0.5, 0.5, 0.5));
radiusSqr = new MutableBlockVector(radius.multiply(radius));
radiusLengthSqr = radiusSqr.getBlockX();
if (radius.getBlockY() == radius.getBlockX() && radius.getBlockX() == radius.getBlockZ()) {
this.sphere = true;
} else {
this.sphere = false;
}
}
@Override
public Set<Vector2D> getChunks() {
final Set<Vector2D> chunks = new HashSet<Vector2D>();
final Vector min = getMinimumPoint();
final Vector max = getMaximumPoint();
final int centerY = getCenter().getBlockY();
for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) {
if (!contains(new BlockVector(x, centerY, z))) {
continue;
}
chunks.add(new BlockVector2D(
x >> ChunkStore.CHUNK_SHIFTS,
z >> ChunkStore.CHUNK_SHIFTS
));
}
}
return chunks;
}
@Override
public boolean contains(Vector position) {
int cx = position.getBlockX() - center.getBlockX();
int cx2 = cx * cx;
if (cx2 > radiusSqr.getBlockX()) {
return false;
}
int cz = position.getBlockZ() - center.getBlockZ();
int cz2 = cz * cz;
if (cz2 > radiusSqr.getBlockZ()) {
return false;
}
int cy = position.getBlockY() - center.getBlockY();
int cy2 = cy * cy;
if (radiusSqr.getBlockY() < 255 && cy2 > radiusSqr.getBlockY()) {
return false;
}
if (sphere) {
System.out.println((cx2 + cy2 + cz2) + " | " + radiusLengthSqr);
return cx2 + cy2 + cz2 <= radiusLengthSqr;
}
double cxd = (double) cx / radius.getBlockX();
double cyd = (double) cy / radius.getBlockY();
double czd = (double) cz / radius.getBlockZ();
System.out.println((cxd * cxd + cyd * cyd + czd * czd) + " | " + 1);
return cxd * cxd + cyd * cyd + czd * czd <= 1;
}
/**
* Returns string representation in the format
* "(centerX, centerY, centerZ) - (radiusX, radiusY, radiusZ)".
*
* @return string
*/
@Override
public String toString() {
return center + " - " + getRadius();
}
public void extendRadius(Vector minRadius) {
setRadius(Vector.getMaximum(minRadius, getRadius()));
}
@Override
public EllipsoidRegion clone() {
return (EllipsoidRegion) super.clone();
}
public static Class<EllipsoidRegion> inject() {
return EllipsoidRegion.class;
}
}