mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2025-01-01 05:57:57 +01:00
Optimize BFS and ellipsoid iteration
This commit is contained in:
parent
4caadb8ab9
commit
47b7e858ff
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user