From 459629a2f2e8e6c7f566dda5834ac7c3081580d2 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Mon, 6 May 2019 15:57:12 +1000 Subject: [PATCH] scanchunk --- .../beta/implementation/QueueHandler.java | 13 +- .../fawe/object/regions/FuzzyRegion.java | 4 +- .../fawe/object/visitor/AboveVisitor.java | 17 +- .../function/visitor/BreadthFirstSearch.java | 91 ++++------- .../function/visitor/DirectionalVisitor.java | 17 +- .../function/visitor/DownwardVisitor.java | 14 +- .../function/visitor/NonRisingVisitor.java | 15 +- .../worldedit/function/visitor/ScanChunk.java | 151 ++++++++++++++++++ .../worldedit/regions/EllipsoidRegion.java | 10 +- 9 files changed, 231 insertions(+), 101 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/ScanChunk.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java index a7ebe3676..a3fbf1c04 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java @@ -181,13 +181,12 @@ public abstract class QueueHandler implements Trimable, Runnable { // Initialize chunk.init(queue, X, Z); - chunk = newFilter.applyChunk(chunk, region); - - if (chunk == null) continue; - - if (block == null) block = queue.initFilterBlock(); - chunk.filter(newFilter, block, region, mbv1, mbv2); - + IChunk newChunk = newFilter.applyChunk(chunk, region); + if (newChunk != null) { + chunk = newChunk; + if (block == null) block = queue.initFilterBlock(); + chunk.filter(newFilter, block, region, mbv1, mbv2); + } queue.submit(chunk); } queue.flush(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java index 3abaa65cf..f7ecfa2b4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java @@ -19,10 +19,8 @@ public class FuzzyRegion extends AbstractRegion { private final Mask mask; private BlockVectorSet set = new BlockVectorSet(); - private boolean populated; private int minX, minY, minZ, maxX, maxY, maxZ; private Extent extent; - private int count = 0; { minX = minY = minZ = Integer.MAX_VALUE; @@ -59,7 +57,7 @@ public class FuzzyRegion extends AbstractRegion { @Override public Iterator iterator() { - return (Iterator) set.iterator(); + return set.iterator(); } private final void setMinMax(int x, int y, int z) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/AboveVisitor.java b/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/AboveVisitor.java index 71de953b1..373e71f5d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/AboveVisitor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/AboveVisitor.java @@ -6,6 +6,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; +import java.util.Arrays; import java.util.Collection; @@ -39,14 +40,14 @@ public class AboveVisitor extends RecursiveVisitor { this.baseY = baseY; - Collection directions = getDirections(); - directions.clear(); - directions.add(BlockVector3.at(1, 0, 0)); - directions.add(BlockVector3.at(-1, 0, 0)); - directions.add(BlockVector3.at(0, 0, 1)); - directions.add(BlockVector3.at(0, 0, -1)); - directions.add(BlockVector3.at(0, 1, 0)); - directions.add(BlockVector3.at(0, -1, 0)); + setDirections( + BlockVector3.at(1, 0, 0), + BlockVector3.at(-1, 0, 0), + BlockVector3.at(0, 0, 1), + BlockVector3.at(0, 0, -1), + BlockVector3.at(0, 1, 0), + BlockVector3.at(0, -1, 0) + ); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index 859110b77..bbc420a76 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Queue; import java.util.Set; @@ -73,7 +74,7 @@ public abstract class BreadthFirstSearch implements Operation { } private final RegionFunction function; - private List directions = new ArrayList<>(); + private BlockVector3[] directions; private BlockVectorSet visited; private final MappedFaweQueue mFaweQueue; private BlockVectorSet queue; @@ -96,23 +97,18 @@ public abstract class BreadthFirstSearch implements Operation { this.queue = new BlockVectorSet(); this.visited = new BlockVectorSet(); this.function = function; - this.directions.addAll(Arrays.asList(DEFAULT_DIRECTIONS)); + this.directions = DEFAULT_DIRECTIONS; this.maxDepth = maxDepth; } - public void setDirections(List directions) { + public void setDirections(BlockVector3... directions) { this.directions = directions; } - private IntegerTrio[] getIntDirections() { - IntegerTrio[] array = new IntegerTrio[directions.size()]; - for (int i = 0; i < array.length; i++) { - BlockVector3 dir = directions.get(i); - array[i] = new IntegerTrio(dir.getBlockX(), dir.getBlockY(), dir.getBlockZ()); - } - return array; + public void setDirections(Collection directions) { + setDirections(directions.toArray(new BlockVector3[0])); } - + /** * Get the list of directions will be visited. * @@ -121,34 +117,36 @@ public abstract class BreadthFirstSearch implements Operation { * unit vectors. An example of a valid direction is * {@code BlockVector3.at(1, 0, 1)}.

* - *

The list of directions can be cleared.

- * * @return the list of directions */ - protected Collection getDirections() { - return directions; + public Collection getDirections() { + return Arrays.asList(directions); } /** * Add the directions along the axes as directions to visit. */ - protected void addAxes() { - directions.add(BlockVector3.at(0, -1, 0)); - directions.add(BlockVector3.at(0, 1, 0)); - directions.add(BlockVector3.at(-1, 0, 0)); - directions.add(BlockVector3.at(1, 0, 0)); - directions.add(BlockVector3.at(0, 0, -1)); - directions.add(BlockVector3.at(0, 0, 1)); + public void addAxes() { + HashSet set = new HashSet<>(Arrays.asList(directions)); + set.add(BlockVector3.at(0, -1, 0)); + set.add(BlockVector3.at(0, 1, 0)); + set.add(BlockVector3.at(-1, 0, 0)); + set.add(BlockVector3.at(1, 0, 0)); + set.add(BlockVector3.at(0, 0, -1)); + set.add(BlockVector3.at(0, 0, 1)); + setDirections(set); } /** * Add the diagonal directions as directions to visit. */ - protected void addDiagonal() { - directions.add(BlockVector3.at(1, 0, 1)); - directions.add(BlockVector3.at(-1, 0, -1)); - directions.add(BlockVector3.at(1, 0, -1)); - directions.add(BlockVector3.at(-1, 0, 1)); + public void addDiagonal() { + HashSet set = new HashSet<>(Arrays.asList(directions)); + set.add(BlockVector3.at(1, 0, 1)); + set.add(BlockVector3.at(-1, 0, -1)); + set.add(BlockVector3.at(1, 0, -1)); + set.add(BlockVector3.at(-1, 0, 1)); + setDirections(set); } public void visit(final BlockVector3 pos) { @@ -159,12 +157,6 @@ public abstract class BreadthFirstSearch implements Operation { } } - public void resetVisited() { - queue.clear(); - visited.clear(); - affected = 0; - } - public void setVisited(BlockVectorSet set) { this.visited = set; } @@ -180,21 +172,6 @@ public abstract class BreadthFirstSearch implements Operation { public void setMaxBranch(int maxBranch) { this.maxBranch = maxBranch; } - /** - * Try to visit the given 'to' location. - * - * @param from the origin block - * @param to the block under question - */ - private void visit(BlockVector3 from, BlockVector3 to) { - BlockVector3 blockVector = to; - if (!visited.contains(blockVector)) { - visited.add(blockVector); - if (isVisitable(from, to)) { - queue.add(blockVector); - } - } - } /** * Return whether the given 'to' block should be visited, starting from the @@ -220,7 +197,7 @@ public abstract class BreadthFirstSearch implements Operation { MutableBlockVector3 mutable = new MutableBlockVector3(); // MutableBlockVector3 mutable2 = new MutableBlockVector3(); boolean shouldTrim = false; - IntegerTrio[] dirs = getIntDirections(); + BlockVector3[] dirs = directions; BlockVectorSet tempQueue = new BlockVectorSet(); BlockVectorSet chunkLoadSet = new BlockVectorSet(); for (currentDepth = 0; !queue.isEmpty() && currentDepth <= maxDepth; currentDepth++) { @@ -228,11 +205,11 @@ public abstract class BreadthFirstSearch implements Operation { int cx = Integer.MIN_VALUE; int cz = Integer.MIN_VALUE; for (BlockVector3 from : queue) { - for (IntegerTrio direction : dirs) { - int x = from.getBlockX() + direction.x; - int z = from.getBlockZ() + direction.z; + for (BlockVector3 direction : dirs) { + int x = from.getBlockX() + direction.getX(); + int z = from.getBlockZ() + direction.getZ(); if (cx != (cx = x >> 4) || cz != (cz = z >> 4)) { - int y = from.getBlockY() + direction.y; + int y = from.getBlockY() + direction.getY(); if (y < 0 || y >= 256) { continue; } @@ -249,13 +226,13 @@ public abstract class BreadthFirstSearch implements Operation { for (BlockVector3 from : queue) { if (function.apply(from)) affected++; for (int i = 0, j = 0; i < dirs.length && j < maxBranch; i++) { - IntegerTrio direction = dirs[i]; - int y = from.getBlockY() + direction.y; + BlockVector3 direction = dirs[i]; + int y = from.getBlockY() + direction.getY(); if (y < 0 || y >= 256) { continue; } - int x = from.getBlockX() + direction.x; - int z = from.getBlockZ() + direction.z; + int x = from.getBlockX() + direction.getX(); + int z = from.getBlockZ() + direction.getZ(); if (!visited.contains(x, y, z)) { if (isVisitable(from, BlockVector3.at(x, y, z))) { j++; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DirectionalVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DirectionalVisitor.java index 8a9542e32..9a0b8c912 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DirectionalVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DirectionalVisitor.java @@ -51,14 +51,15 @@ public class DirectionalVisitor extends RecursiveVisitor { checkNotNull(mask); this.origin = origin; this.dirVec = direction; - final Collection directions = this.getDirections(); - directions.clear(); - directions.add(BlockVector3.at(1, 0, 0)); - directions.add(BlockVector3.at(-1, 0, 0)); - directions.add(BlockVector3.at(0, 0, 1)); - directions.add(BlockVector3.at(0, 0, -1)); - directions.add(BlockVector3.at(0, -1, 0)); - directions.add(BlockVector3.at(0, 1, 0)); + + setDirections( + BlockVector3.at(1, 0, 0), + BlockVector3.at(-1, 0, 0), + BlockVector3.at(0, 0, 1), + BlockVector3.at(0, 0, -1), + BlockVector3.at(0, -1, 0), + BlockVector3.at(0, 1, 0) + ); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java index a3a8dac66..e2c4f667a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java @@ -60,13 +60,13 @@ public class DownwardVisitor extends RecursiveVisitor { checkNotNull(mask); this.baseY = baseY; - Collection directions = getDirections(); - directions.clear(); - directions.add(BlockVector3.at(1, 0, 0)); - directions.add(BlockVector3.at(-1, 0, 0)); - directions.add(BlockVector3.at(0, 0, 1)); - directions.add(BlockVector3.at(0, 0, -1)); - directions.add(BlockVector3.at(0, -1, 0)); + setDirections( + BlockVector3.at(1, 0, 0), + BlockVector3.at(-1, 0, 0), + BlockVector3.at(0, 0, 1), + BlockVector3.at(0, 0, -1), + BlockVector3.at(0, -1, 0) + ); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java index 7e79e57a7..5a67fa950 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java @@ -46,13 +46,14 @@ public class NonRisingVisitor extends RecursiveVisitor { public NonRisingVisitor(Mask mask, RegionFunction function, int depth, HasFaweQueue hasFaweQueue) { super(mask, function, depth, hasFaweQueue); - Collection directions = getDirections(); - directions.clear(); - directions.add(BlockVector3.at(1, 0, 0)); - directions.add(BlockVector3.at(-1, 0, 0)); - directions.add(BlockVector3.at(0, 0, 1)); - directions.add(BlockVector3.at(0, 0, -1)); - directions.add(BlockVector3.at(0, -1, 0)); + + setDirections( + BlockVector3.at(1, 0, 0), + BlockVector3.at(-1, 0, 0), + BlockVector3.at(0, 0, 1), + BlockVector3.at(0, 0, -1), + BlockVector3.at(0, -1, 0) + ); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/ScanChunk.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/ScanChunk.java new file mode 100644 index 000000000..cfef60a74 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/ScanChunk.java @@ -0,0 +1,151 @@ +package com.sk89q.worldedit.function.visitor; + +import com.boydti.fawe.example.MappedFaweQueue; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; +import com.boydti.fawe.object.collection.BlockVectorSet; +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.math.BlockVector3; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArraySet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.objects.ObjectIterator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * A chunk based search algorithm + */ +public class ScanChunk { + private static final int MAX_QUEUE = 34816; + public static final BlockVector3[] DEFAULT_DIRECTIONS = new BlockVector3[6]; + public static final BlockVector3[] DIAGONAL_DIRECTIONS; + + static { + DEFAULT_DIRECTIONS[0] = (BlockVector3.at(0, -1, 0)); + DEFAULT_DIRECTIONS[1] = (BlockVector3.at(0, 1, 0)); + DEFAULT_DIRECTIONS[2] = (BlockVector3.at(-1, 0, 0)); + DEFAULT_DIRECTIONS[3] = (BlockVector3.at(1, 0, 0)); + DEFAULT_DIRECTIONS[4] = (BlockVector3.at(0, 0, -1)); + DEFAULT_DIRECTIONS[5] = (BlockVector3.at(0, 0, 1)); + List list = new ArrayList<>(); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x != 0 || y != 0 || z != 0) { + BlockVector3 pos = BlockVector3.at(x, y, z); + if (!list.contains(pos)) { + list.add(pos); + } + } + } + } + } + Collections.sort(list, new Comparator() { + @Override + public int compare(BlockVector3 o1, BlockVector3 o2) { + return (int) Math.signum(o1.lengthSq() - o2.lengthSq()); + } + }); + DIAGONAL_DIRECTIONS = list.toArray(new BlockVector3[list.size()]); + } + + private final RegionFunction function; + private final BlockVector3[] directions; + private final Long2ObjectOpenHashMap visited; + private final Long2ObjectOpenHashMap queues; + + public ScanChunk(final RegionFunction function) { + this.function = function; + this.directions = DEFAULT_DIRECTIONS; + + this.queues = new Long2ObjectOpenHashMap<>(); + this.visited = new Long2ObjectOpenHashMap<>(); + } + + public static final long pairInt(int x, int y) { + return (((long) x) << 32) | (y & 0xffffffffL); + } + + public boolean isVisited(int x, int y, int z) { + int X = x >> 4; + int Z = z >> 4; + long pair = pairInt(X, Z); + long[][] chunk = visited.get(pair); + if (chunk == null) return false; + int layer = y >> 4; + long[] section = chunk[layer]; + if (section == null) return false; + return get(section, getLocalIndex(x & 15, y & 15, z & 15)); + } + + public void start(int x, int y, int z) { + if (!isVisited(x, y, z)) { + queue(x, y, z); + visit(x, y, z); + } + } + + public void visit(int x, int y, int z) { + int X = x >> 4; + int Z = z >> 4; + long pair = pairInt(X, Z); + long[][] chunk = visited.get(pair); + if (chunk == null) { + visited.put(pair, chunk = new long[16][]); + } + int layer = y >> 4; + long[] section = chunk[layer]; + if (section == null) { + chunk[layer] = section = new long[64]; + } + set(section, getLocalIndex(x & 15, y & 15, z & 15)); + } + + public void queue(int x, int y, int z) { + int X = x >> 4; + int Z = z >> 4; + long pair = pairInt(X, Z); + char[] queue = queues.get(pair); + if (queue == null) { + queue = queues.put(pair, queue = new char[MAX_QUEUE + 2]); + queue[0] = 2; + queue[1] = 2; + } + if (queue[1] >= queue.length) { + queue[1] = 2; + } + queue[queue[1]++] = getLocalIndex(x & 15, y, z & 15); + } + + public void process() { + LongArraySet set = new LongArraySet(); + while (!queues.isEmpty()) { + ObjectIterator> iter = queues.long2ObjectEntrySet().fastIterator(); + Long2ObjectMap.Entry entry = iter.next(); + long index = entry.getLongKey(); + int X = MathMan.unpairIntX(index); + int Z = MathMan.unpairIntY(index); + } + } + + public void set(long[] bits, int i) { + bits[i >> 6] |= (1L << (i & 0x3F)); + } + + public boolean get(long[] bits, final int i) { + return (bits[i >> 6] & (1L << (i & 0x3F))) != 0; + } + + public char getLocalIndex(int x, int y, int z) { + return (char) (y + (x << 8) + (z << 12)); + } + + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java index 9cef04a0e..0c0be6781 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java @@ -44,6 +44,7 @@ public class EllipsoidRegion extends AbstractRegion { */ private Vector3 radius; private Vector3 radiusSqr; + private Vector3 inverseRadius; private int radiusLengthSqr; private boolean sphere; @@ -136,7 +137,7 @@ public class EllipsoidRegion extends AbstractRegion { public void contract(BlockVector3... changes) throws RegionOperationException { center = center.subtract(calculateDiff(changes)); Vector3 newRadius = radius.subtract(calculateChanges(changes)); - radius = Vector3.at(1.5, 1.5, 1.5).getMaximum(newRadius); + setRadius(Vector3.at(1.5, 1.5, 1.5).getMaximum(newRadius)); } @Override @@ -187,6 +188,7 @@ public class EllipsoidRegion extends AbstractRegion { } else { this.sphere = false; } + inverseRadius = Vector3.ONE.divide(radius); } @Override @@ -233,9 +235,9 @@ public class EllipsoidRegion extends AbstractRegion { if (sphere) { return cx2 + cy2 + cz2 <= radiusLengthSqr; } - double cxd = (double) cx / radius.getBlockX(); - double cyd = (double) cy / radius.getBlockY(); - double czd = (double) cz / radius.getBlockZ(); + double cxd = cx * inverseRadius.getX(); + double cyd = cy * inverseRadius.getY(); + double czd = cz * inverseRadius.getZ(); return cxd * cxd + cyd * cyd + czd * czd <= 1; }