mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2024-11-25 03:55:35 +01:00
Various
Optimize spline Translate spline brush Add various new patterns (#nox #noy #noz #rel `[stone,wood,blah` #existing Can now use percentages with patterns, not just blocks e.g. 50%#clipboard,50%stone Add resettable patterns
This commit is contained in:
parent
5b96a52e99
commit
1e79ae4a0f
@ -41,6 +41,7 @@ import com.sk89q.worldedit.command.tool.RecursivePickaxe;
|
||||
import com.sk89q.worldedit.command.tool.brush.GravityBrush;
|
||||
import com.sk89q.worldedit.event.extent.EditSessionEvent;
|
||||
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
|
||||
import com.sk89q.worldedit.extension.factory.HashTagPatternParser;
|
||||
import com.sk89q.worldedit.extension.platform.CommandManager;
|
||||
import com.sk89q.worldedit.extension.platform.PlatformManager;
|
||||
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
||||
@ -71,6 +72,7 @@ import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||
import com.sk89q.worldedit.history.change.EntityCreate;
|
||||
import com.sk89q.worldedit.history.change.EntityRemove;
|
||||
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||
import com.sk89q.worldedit.session.SessionManager;
|
||||
@ -386,6 +388,7 @@ public class Fawe {
|
||||
Patterns.inject(); // Optimizations (reduce object creation)
|
||||
RandomPattern.inject(); // Optimizations
|
||||
ClipboardPattern.inject(); // Optimizations
|
||||
HashTagPatternParser.inject(); // Add new patterns
|
||||
// Mask
|
||||
BlockMask.inject(); // Optimizations
|
||||
SolidBlockMask.inject(); // Optimizations
|
||||
@ -403,6 +406,8 @@ public class Fawe {
|
||||
// NBT
|
||||
NBTInputStream.inject(); // Add actual streaming + Optimizations + New methods
|
||||
NBTOutputStream.inject(); // New methods
|
||||
// Math
|
||||
KochanekBartelsInterpolation.inject(); // Optimizations
|
||||
try {
|
||||
CommandManager.inject(); // Async commands
|
||||
PlatformManager.inject(); // Async brushes / tools
|
||||
|
@ -93,7 +93,10 @@ public enum BBC {
|
||||
BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2).", "WorldEdit.Brush"),
|
||||
BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"),
|
||||
BRUSH_LINE("Line brush shape equipped (%s0).", "WorldEdit.Brush"),
|
||||
BRUSH_SPLINE("Line brush shape equipped (%s0). Right click to select points, left click to execute.", "WorldEdit.Brush"),
|
||||
BRUSH_SPLINE("Line brush shape equipped (%s0). Right click an end to add a shape", "WorldEdit.Brush"),
|
||||
BRUSH_SPLINE_PRIMARY("Added position, left click to spline them together!", "WorldEdit.Brush"),
|
||||
BRUSH_SPLINE_SECONDARY_ERROR("Not enough positions set!", "WorldEdit.Brush"),
|
||||
BRUSH_SPLINE_SECONDARY("Created spline", "WorldEdit.Brush"),
|
||||
BRUSH_BLEND_BALL("Blend ball brush equipped (%s0).", "WorldEdit.Brush"),
|
||||
BRUSH_ERODE("Erode brush equipped (%s0).", "WorldEdit.Brush"),
|
||||
BRUSH_PASTE_NONE("Nothing to paste", "WorldEdit.Brush"),
|
||||
|
@ -68,12 +68,12 @@ public class SplineBrush implements DoubleActionBrush {
|
||||
numSplines = points.size();
|
||||
}
|
||||
this.positionSets.add(points);
|
||||
player.print("Added position, right click to spline them together!");
|
||||
BBC.BRUSH_SPLINE_PRIMARY.send(player);
|
||||
break;
|
||||
}
|
||||
case SECONDARY: {
|
||||
if (positionSets.size() < 2) {
|
||||
player.print("Not enough positions set!");
|
||||
BBC.BRUSH_SPLINE_SECONDARY_ERROR.send(player);
|
||||
return;
|
||||
}
|
||||
List<Vector> centroids = new ArrayList<>();
|
||||
@ -112,7 +112,7 @@ public class SplineBrush implements DoubleActionBrush {
|
||||
}
|
||||
editSession.drawSpline(Patterns.wrap(pattern), currentSpline, 0, 0, 0, 10, 0, true);
|
||||
}
|
||||
player.print("Created spline");
|
||||
BBC.BRUSH_SPLINE_SECONDARY.send(player);
|
||||
positionSets.clear();
|
||||
numSplines = 0;
|
||||
break;
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
|
||||
public class ExistingPattern extends AbstractPattern {
|
||||
private final Extent extent;
|
||||
|
||||
public ExistingPattern(Extent extent) {
|
||||
this.extent = extent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector position) {
|
||||
return extent.getBlock(position);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
|
||||
public class LinearBlockPattern extends AbstractPattern {
|
||||
|
||||
private final BaseBlock[] blocks;
|
||||
private int index;
|
||||
|
||||
public LinearBlockPattern(BaseBlock[] blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector position) {
|
||||
if (index == blocks.length) {
|
||||
index = 0;
|
||||
}
|
||||
return blocks[index++];
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
|
||||
public class NoXPattern extends AbstractPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
public NoXPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
private Vector mutable = new Vector();
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector pos) {
|
||||
mutable.y = pos.y;
|
||||
mutable.z = pos.z;
|
||||
return pattern.apply(mutable);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
|
||||
public class NoYPattern extends AbstractPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
public NoYPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
private Vector mutable = new Vector();
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector pos) {
|
||||
mutable.x = pos.x;
|
||||
mutable.z = pos.z;
|
||||
return pattern.apply(mutable);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
|
||||
public class NoZPattern extends AbstractPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
public NoZPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
private Vector mutable = new Vector();
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector pos) {
|
||||
mutable.x = pos.x;
|
||||
mutable.y = pos.y;
|
||||
return pattern.apply(mutable);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PatternTraverser {
|
||||
private final Object pattern;
|
||||
|
||||
public PatternTraverser(Object start) {
|
||||
this.pattern = start;
|
||||
}
|
||||
|
||||
public void reset(Extent newExtent) {
|
||||
reset(pattern, newExtent);
|
||||
}
|
||||
|
||||
private void reset(Object pattern, Extent newExtent) {
|
||||
if (pattern == null) {
|
||||
return;
|
||||
}
|
||||
if (pattern instanceof ResettablePattern) {
|
||||
((ResettablePattern) pattern).reset();
|
||||
}
|
||||
Class<?> current = pattern.getClass();
|
||||
while(current.getSuperclass() != null) {
|
||||
if (newExtent != null) {
|
||||
try {
|
||||
Field field = current.getDeclaredField("extent");
|
||||
field.setAccessible(true);
|
||||
field.set(pattern, newExtent);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
|
||||
}
|
||||
try {
|
||||
Field field = current.getDeclaredField("pattern");
|
||||
field.setAccessible(true);
|
||||
Pattern next = (Pattern) field.get(pattern);
|
||||
reset(next, newExtent);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
|
||||
try {
|
||||
Field field = current.getDeclaredField("material");
|
||||
field.setAccessible(true);
|
||||
Pattern next = (Pattern) field.get(pattern);
|
||||
reset(next, newExtent);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
|
||||
try {
|
||||
Field field = current.getDeclaredField("patterns");
|
||||
field.setAccessible(true);
|
||||
Collection<Pattern> patterns = (Collection<Pattern>) field.get(pattern);
|
||||
for (Pattern next : patterns) {
|
||||
reset(next, newExtent);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
|
||||
public class RelativePattern extends AbstractPattern implements ResettablePattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
public RelativePattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
private Vector origin;
|
||||
private Vector mutable = new Vector();
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector pos) {
|
||||
if (origin == null) {
|
||||
origin = new Vector(pos);
|
||||
}
|
||||
mutable.x = pos.x - origin.x;
|
||||
mutable.y = pos.y - origin.y;
|
||||
mutable.z = pos.z - origin.z;
|
||||
return pattern.apply(mutable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
origin = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.boydti.fawe.object.pattern;
|
||||
|
||||
public interface ResettablePattern {
|
||||
void reset();
|
||||
}
|
@ -18,6 +18,9 @@ public class MaskTraverser {
|
||||
}
|
||||
|
||||
private void reset(Mask mask, Extent newExtent) {
|
||||
if (mask == null) {
|
||||
return;
|
||||
}
|
||||
if (mask instanceof ResettableMask) {
|
||||
((ResettableMask) mask).reset();
|
||||
}
|
||||
|
@ -2700,14 +2700,20 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
|
||||
final int tipx = (int) Math.round(tipv.getX());
|
||||
final int tipy = (int) Math.round(tipv.getY());
|
||||
final int tipz = (int) Math.round(tipv.getZ());
|
||||
vset.add(new Vector(tipx, tipy, tipz));
|
||||
if (radius == 0) {
|
||||
setBlock(tipx, tipy, tipz, pattern.next(tipx, tipy, tipz));
|
||||
} else {
|
||||
vset.add(new Vector(tipx, tipy, tipz));
|
||||
}
|
||||
}
|
||||
|
||||
vset = this.getBallooned(vset, radius);
|
||||
if (!filled) {
|
||||
vset = this.getHollowed(vset);
|
||||
if (radius != 0) {
|
||||
vset = this.getBallooned(vset, radius);
|
||||
if (!filled) {
|
||||
vset = this.getHollowed(vset);
|
||||
}
|
||||
return this.setBlocks(vset, pattern);
|
||||
}
|
||||
return this.setBlocks(vset, pattern);
|
||||
return changes;
|
||||
}
|
||||
|
||||
private double hypot(final double... pars) {
|
||||
|
@ -0,0 +1,123 @@
|
||||
package com.sk89q.worldedit.extension.factory;
|
||||
|
||||
import com.boydti.fawe.object.pattern.ExistingPattern;
|
||||
import com.boydti.fawe.object.pattern.LinearBlockPattern;
|
||||
import com.boydti.fawe.object.pattern.NoXPattern;
|
||||
import com.boydti.fawe.object.pattern.NoYPattern;
|
||||
import com.boydti.fawe.object.pattern.NoZPattern;
|
||||
import com.boydti.fawe.object.pattern.RelativePattern;
|
||||
import com.sk89q.worldedit.EmptyClipboardException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.pattern.BlockPattern;
|
||||
import com.sk89q.worldedit.function.pattern.ClipboardPattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.function.pattern.RandomPattern;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HashTagPatternParser extends InputParser<Pattern> {
|
||||
|
||||
public HashTagPatternParser(WorldEdit worldEdit) {
|
||||
super(worldEdit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
|
||||
switch (input.toLowerCase().charAt(0)) {
|
||||
case '#': {
|
||||
switch (input) {
|
||||
case "#existing": {
|
||||
return new ExistingPattern(context.requireExtent());
|
||||
}
|
||||
case "#clipboard":
|
||||
case "#copy": {
|
||||
LocalSession session = context.requireSession();
|
||||
if (session != null) {
|
||||
try {
|
||||
ClipboardHolder holder = session.getClipboard();
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
return new ClipboardPattern(clipboard);
|
||||
} catch (EmptyClipboardException e) {
|
||||
throw new InputParseException("To use #clipboard, please first copy something to your clipboard");
|
||||
}
|
||||
} else {
|
||||
throw new InputParseException("No session is available, so no clipboard is available");
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] split2 = input.split(":");
|
||||
if (split2.length > 1) {
|
||||
switch (split2[0]) {
|
||||
case "#relative":
|
||||
case "#rel": {
|
||||
String rest = input.substring(5);
|
||||
return new RelativePattern(parseFromInput(rest, context));
|
||||
}
|
||||
case "#nox": {
|
||||
String rest = input.substring(5);
|
||||
return new NoXPattern(parseFromInput(rest, context));
|
||||
}
|
||||
case "#noy": {
|
||||
String rest = input.substring(5);
|
||||
return new NoYPattern(parseFromInput(rest, context));
|
||||
}
|
||||
case "#noz": {
|
||||
String rest = input.substring(5);
|
||||
return new NoZPattern(parseFromInput(rest, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new InputParseException("Invalid, see: https://github.com/boy0001/FastAsyncWorldedit/wiki/WorldEdit-and-FAWE-patterns");
|
||||
}
|
||||
case '[': {
|
||||
ArrayList<BaseBlock> blocks = new ArrayList<>();
|
||||
for (String token : input.substring(1).split(",")) {
|
||||
BlockFactory blockRegistry = worldEdit.getBlockFactory();
|
||||
BaseBlock block = blockRegistry.parseFromInput(token, context);
|
||||
blocks.add(block);
|
||||
}
|
||||
if (blocks.isEmpty()) {
|
||||
throw new InputParseException("No blocks provided for linear pattern e.g. [stone,wood");
|
||||
}
|
||||
return new LinearBlockPattern(blocks.toArray(new BaseBlock[blocks.size()]));
|
||||
}
|
||||
default:
|
||||
String[] items = input.split(",");
|
||||
if (items.length == 1) {
|
||||
return new BlockPattern(worldEdit.getBlockFactory().parseFromInput(items[0], context));
|
||||
}
|
||||
BlockFactory blockRegistry = worldEdit.getBlockFactory();
|
||||
RandomPattern randomPattern = new RandomPattern();
|
||||
for (String token : input.split(",")) {
|
||||
Pattern pattern;
|
||||
double chance;
|
||||
// Parse special percentage syntax
|
||||
if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) {
|
||||
String[] p = token.split("%");
|
||||
if (p.length < 2) {
|
||||
throw new InputParseException("Missing the pattern after the % symbol for '" + input + "'");
|
||||
} else {
|
||||
chance = Double.parseDouble(p[0]);
|
||||
pattern = parseFromInput(p[1], context);
|
||||
}
|
||||
} else {
|
||||
chance = 1;
|
||||
pattern = parseFromInput(token, context);
|
||||
}
|
||||
randomPattern.add(pattern, chance);
|
||||
}
|
||||
return randomPattern;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> inject() {
|
||||
return HashTagPatternParser.class;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ package com.sk89q.worldedit.extension.platform;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.object.pattern.PatternTraverser;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.wrappers.PlayerWrapper;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
@ -330,6 +331,11 @@ public class PlatformManager {
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Tool> T reset(T tool) {
|
||||
new PatternTraverser(tool).reset(null);
|
||||
return tool;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Subscribe
|
||||
public void handleBlockInteract(BlockInteractEvent event) {
|
||||
@ -371,7 +377,7 @@ public class PlatformManager {
|
||||
fp.runAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
superPickaxe.actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
reset(superPickaxe).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
}
|
||||
}, true, true);
|
||||
event.setCancelled(true);
|
||||
@ -385,7 +391,7 @@ public class PlatformManager {
|
||||
fp.runAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
reset(((DoubleActionBlockTool) tool)).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
}
|
||||
}, true, true);
|
||||
event.setCancelled(true);
|
||||
@ -417,7 +423,7 @@ public class PlatformManager {
|
||||
fp.runAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
|
||||
}
|
||||
}, true, true);
|
||||
event.setCancelled(true);
|
||||
@ -443,6 +449,7 @@ public class PlatformManager {
|
||||
// Create a proxy actor with a potentially different world for
|
||||
// making changes to the world
|
||||
final Player player = PlayerWrapper.wrap(createProxyActor(event.getPlayer()));
|
||||
|
||||
try {
|
||||
switch (event.getInputType()) {
|
||||
case PRIMARY: {
|
||||
@ -475,7 +482,7 @@ public class PlatformManager {
|
||||
fp.runAsyncIfFree(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
|
||||
reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
|
||||
}
|
||||
});
|
||||
event.setCancelled(true);
|
||||
@ -513,7 +520,7 @@ public class PlatformManager {
|
||||
fp.runAsyncIfFree(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
|
||||
reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
|
||||
}
|
||||
});
|
||||
event.setCancelled(true);
|
||||
|
@ -12,7 +12,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
public class ClipboardPattern extends AbstractPattern {
|
||||
|
||||
private final Clipboard clipboard;
|
||||
private final Vector size;
|
||||
private final int sx, sy, sz;
|
||||
private final Vector min;
|
||||
|
||||
/**
|
||||
@ -23,7 +23,10 @@ public class ClipboardPattern extends AbstractPattern {
|
||||
public ClipboardPattern(Clipboard clipboard) {
|
||||
checkNotNull(clipboard);
|
||||
this.clipboard = clipboard;
|
||||
this.size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1);
|
||||
Vector size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1);
|
||||
this.sx = size.getBlockX();
|
||||
this.sy = size.getBlockY();
|
||||
this.sz = size.getBlockZ();
|
||||
this.min = clipboard.getMinimumPoint();
|
||||
}
|
||||
|
||||
@ -31,9 +34,12 @@ public class ClipboardPattern extends AbstractPattern {
|
||||
|
||||
@Override
|
||||
public BaseBlock apply(Vector position) {
|
||||
int xp = Math.abs(position.getBlockX()) % size.getBlockX();
|
||||
int yp = Math.abs(position.getBlockY()) % size.getBlockY();
|
||||
int zp = Math.abs(position.getBlockZ()) % size.getBlockZ();
|
||||
int xp = position.getBlockX() % sx;
|
||||
int yp = position.getBlockY() % sy;
|
||||
int zp = position.getBlockZ() % sz;
|
||||
if (xp < 0) xp += sx;
|
||||
if (yp < 0) yp += sy;
|
||||
if (zp < 0) zp += sz;
|
||||
mutable.x = min.x + xp;
|
||||
mutable.y = min.y + yp;
|
||||
mutable.z = min.z + zp;
|
||||
|
@ -5,6 +5,7 @@ import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -16,6 +17,7 @@ public class RandomPattern extends AbstractPattern {
|
||||
|
||||
private Map<Pattern, Double> weights = new HashMap<>();
|
||||
private RandomCollection<Pattern> collection;
|
||||
private Set<Pattern> patterns;
|
||||
|
||||
/**
|
||||
* Add a pattern to the weight list of patterns.
|
||||
@ -30,6 +32,7 @@ public class RandomPattern extends AbstractPattern {
|
||||
checkNotNull(pattern);
|
||||
weights.put(pattern, chance);
|
||||
collection = RandomCollection.of(weights);
|
||||
this.patterns = weights.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A Kochanek-Bartels interpolation; continuous in the 2nd derivative.
|
||||
*
|
||||
* <p>Supports {@link Node#tension tension}, {@link Node#bias bias} and
|
||||
* {@link Node#continuity continuity} parameters per {@link Node}.</p>
|
||||
*/
|
||||
public class KochanekBartelsInterpolation implements Interpolation {
|
||||
|
||||
private List<Node> nodes;
|
||||
private Vector[] coeffA;
|
||||
private Vector[] coeffB;
|
||||
private Vector[] coeffC;
|
||||
private Vector[] coeffD;
|
||||
private double scaling;
|
||||
|
||||
public KochanekBartelsInterpolation() {
|
||||
setNodes(Collections.<Node>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodes(List<Node> nodes) {
|
||||
checkNotNull(nodes);
|
||||
|
||||
this.nodes = nodes;
|
||||
recalc();
|
||||
}
|
||||
|
||||
private void recalc() {
|
||||
final int nNodes = nodes.size();
|
||||
coeffA = new Vector[nNodes];
|
||||
coeffB = new Vector[nNodes];
|
||||
coeffC = new Vector[nNodes];
|
||||
coeffD = new Vector[nNodes];
|
||||
|
||||
if (nNodes == 0)
|
||||
return;
|
||||
|
||||
Node nodeB = nodes.get(0);
|
||||
double tensionB = nodeB.getTension();
|
||||
double biasB = nodeB.getBias();
|
||||
double continuityB = nodeB.getContinuity();
|
||||
for (int i = 0; i < nNodes; ++i) {
|
||||
final double tensionA = tensionB;
|
||||
final double biasA = biasB;
|
||||
final double continuityA = continuityB;
|
||||
|
||||
if (i + 1 < nNodes) {
|
||||
nodeB = nodes.get(i + 1);
|
||||
tensionB = nodeB.getTension();
|
||||
biasB = nodeB.getBias();
|
||||
continuityB = nodeB.getContinuity();
|
||||
}
|
||||
|
||||
// Kochanek-Bartels tangent coefficients
|
||||
final double ta = (1-tensionA)*(1+biasA)*(1+continuityA)/2; // Factor for lhs of d[i]
|
||||
final double tb = (1-tensionA)*(1-biasA)*(1-continuityA)/2; // Factor for rhs of d[i]
|
||||
final double tc = (1-tensionB)*(1+biasB)*(1-continuityB)/2; // Factor for lhs of d[i+1]
|
||||
final double td = (1-tensionB)*(1-biasB)*(1+continuityB)/2; // Factor for rhs of d[i+1]
|
||||
|
||||
coeffA[i] = linearCombination(i, -ta, ta- tb-tc+2, tb+tc-td-2, td);
|
||||
coeffB[i] = linearCombination(i, 2*ta, -2*ta+2*tb+tc-3, -2*tb-tc+td+3, -td);
|
||||
coeffC[i] = linearCombination(i, -ta, ta- tb , tb , 0);
|
||||
//coeffD[i] = linearCombination(i, 0, 1, 0, 0);
|
||||
coeffD[i] = retrieve(i); // this is an optimization
|
||||
}
|
||||
|
||||
scaling = nodes.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the linear combination of the given coefficients with the nodes adjacent to baseIndex.
|
||||
*
|
||||
* @param baseIndex node index
|
||||
* @param f1 coefficient for baseIndex-1
|
||||
* @param f2 coefficient for baseIndex
|
||||
* @param f3 coefficient for baseIndex+1
|
||||
* @param f4 coefficient for baseIndex+2
|
||||
* @return linear combination of nodes[n-1..n+2] with f1..4
|
||||
*/
|
||||
private Vector linearCombination(int baseIndex, double f1, double f2, double f3, double f4) {
|
||||
final Vector r1 = retrieve(baseIndex - 1).multiply(f1);
|
||||
final Vector r2 = retrieve(baseIndex ).multiply(f2);
|
||||
final Vector r3 = retrieve(baseIndex + 1).multiply(f3);
|
||||
final Vector r4 = retrieve(baseIndex + 2).multiply(f4);
|
||||
|
||||
return r1.add(r2).add(r3).add(r4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a node. Indexes are clamped to the valid range.
|
||||
*
|
||||
* @param index node index to retrieve
|
||||
* @return nodes[clamp(0, nodes.length-1)]
|
||||
*/
|
||||
private Vector retrieve(int index) {
|
||||
if (index < 0)
|
||||
return fastRetrieve(0);
|
||||
|
||||
if (index >= nodes.size())
|
||||
return fastRetrieve(nodes.size()-1);
|
||||
|
||||
return fastRetrieve(index);
|
||||
}
|
||||
|
||||
private Vector fastRetrieve(int index) {
|
||||
return nodes.get(index).getPosition();
|
||||
}
|
||||
|
||||
private Vector mutable = new Vector();
|
||||
|
||||
@Override
|
||||
public Vector getPosition(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
final Vector d = coeffD[index];
|
||||
|
||||
double r2 = remainder * remainder;
|
||||
double r3 = r2 * remainder;
|
||||
mutable.x = a.x * r3 + b.x * r2 + c.x * remainder + d.x;
|
||||
mutable.y = a.y * r3 + b.y * r2 + c.y * remainder + d.y;
|
||||
mutable.z = a.z * r3 + b.z * r2 + c.z * remainder + d.z;
|
||||
return mutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector get1stDerivative(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
//final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
return a.multiply(1.5*position - 3.0*index).add(b).multiply(2.0*position).add(a.multiply(1.5*index).subtract(b).multiply(2.0*index)).add(c).multiply(scaling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double arcLength(double positionA, double positionB) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (positionA > positionB)
|
||||
return arcLength(positionB, positionA);
|
||||
|
||||
positionA *= scaling;
|
||||
positionB *= scaling;
|
||||
|
||||
final int indexA = (int) Math.floor(positionA);
|
||||
final double remainderA = positionA - indexA;
|
||||
|
||||
final int indexB = (int) Math.floor(positionB);
|
||||
final double remainderB = positionB - indexB;
|
||||
|
||||
return arcLengthRecursive(indexA, remainderA, indexB, remainderB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a < b
|
||||
*/
|
||||
private double arcLengthRecursive(int indexLeft, double remainderLeft, int indexRight, double remainderRight) {
|
||||
switch (indexRight - indexLeft) {
|
||||
case 0:
|
||||
return arcLengthRecursive(indexLeft, remainderLeft, remainderRight);
|
||||
|
||||
case 1:
|
||||
// This case is merely a speed-up for a very common case
|
||||
return
|
||||
arcLengthRecursive(indexLeft, remainderLeft, 1.0) +
|
||||
arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
|
||||
default:
|
||||
return
|
||||
arcLengthRecursive(indexLeft, remainderLeft, indexRight - 1, 1.0) +
|
||||
arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
}
|
||||
}
|
||||
|
||||
private double arcLengthRecursive(int index, double remainderLeft, double remainderRight) {
|
||||
final Vector a = coeffA[index].multiply(3.0);
|
||||
final Vector b = coeffB[index].multiply(2.0);
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
final int nPoints = 8;
|
||||
|
||||
double accum = a.multiply(remainderLeft).add(b).multiply(remainderLeft).add(c).length() / 2.0;
|
||||
for (int i = 1; i < nPoints-1; ++i) {
|
||||
double t = ((double) i) / nPoints;
|
||||
t = (remainderRight-remainderLeft)*t + remainderLeft;
|
||||
accum += a.multiply(t).add(b).multiply(t).add(c).length();
|
||||
}
|
||||
|
||||
accum += a.multiply(remainderRight).add(b).multiply(remainderRight).add(c).length() / 2.0;
|
||||
return accum * (remainderRight - remainderLeft) / nPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegment(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return Integer.MAX_VALUE;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
return (int) Math.floor(position);
|
||||
}
|
||||
|
||||
public static Class<?> inject() {
|
||||
return KochanekBartelsInterpolation.class;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user