diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java
index 7d910a7c7d..87dfac4796 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -28,6 +28,7 @@
import com.sk89q.worldedit.extension.platform.Watchdog;
import com.sk89q.worldedit.extent.ChangeSetExtent;
import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.extent.InputExtent;
import com.sk89q.worldedit.extent.MaskingExtent;
import com.sk89q.worldedit.extent.NullExtent;
import com.sk89q.worldedit.extent.TracingExtent;
@@ -95,6 +96,8 @@
import com.sk89q.worldedit.math.interpolation.Node;
import com.sk89q.worldedit.math.noise.RandomNoise;
import com.sk89q.worldedit.math.transform.AffineTransform;
+import com.sk89q.worldedit.math.transform.ScaleAndTranslateTransform;
+import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.CylinderRegion;
import com.sk89q.worldedit.regions.EllipsoidRegion;
@@ -2310,7 +2313,9 @@ public List> getBlockDistribution(Region region, @Nullable
* @return number of blocks changed
* @throws ExpressionException if there is a problem with the expression
* @throws MaxChangedBlocksException if the maximum block change limit is exceeded
+ * @deprecated Use {@link EditSession#makeShape(Region, Transform, Pattern, String, boolean, int)} and pass a {@link ScaleAndTranslateTransform}.
*/
+ @Deprecated
public int makeShape(final Region region, final Vector3 zero, final Vector3 unit,
final Pattern pattern, final String expressionString, final boolean hollow)
throws ExpressionException, MaxChangedBlocksException {
@@ -2330,13 +2335,34 @@ public int makeShape(final Region region, final Vector3 zero, final Vector3 unit
* @return number of blocks changed
* @throws ExpressionException if there is a problem with the expression
* @throws MaxChangedBlocksException if the maximum block change limit is exceeded
+ * @deprecated Use {@link EditSession#makeShape(Region, Transform, Pattern, String, boolean, int)} and pass a {@link ScaleAndTranslateTransform}.
*/
+ @Deprecated
public int makeShape(final Region region, final Vector3 zero, final Vector3 unit,
final Pattern pattern, final String expressionString, final boolean hollow, final int timeout)
throws ExpressionException, MaxChangedBlocksException {
+ return makeShape(region, new ScaleAndTranslateTransform(zero, unit), pattern, expressionString, hollow, timeout);
+ }
+
+ /**
+ * Generate a shape for the given expression.
+ *
+ * @param region the region to generate the shape in
+ * @param transform the transformation for x/y/z variables
+ * @param pattern the default material to make the shape from
+ * @param expressionString the expression defining the shape
+ * @param hollow whether the shape should be hollow
+ * @param timeout the time, in milliseconds, to wait for each expression evaluation before halting it. -1 to disable
+ * @return number of blocks changed
+ * @throws ExpressionException if there is a problem with the expression
+ * @throws MaxChangedBlocksException if the maximum block change limit is exceeded
+ */
+ public int makeShape(final Region region,
+ Transform transform, final Pattern pattern, final String expressionString, final boolean hollow, final int timeout)
+ throws ExpressionException, MaxChangedBlocksException {
final Expression expression = Expression.compile(expressionString, "x", "y", "z", "type", "data");
expression.optimize();
- return makeShape(region, zero, unit, pattern, expression, hollow, timeout);
+ return makeShape(region, transform, pattern, expression, hollow, timeout);
}
/**
@@ -2346,7 +2372,7 @@ public int makeShape(final Region region, final Vector3 zero, final Vector3 unit
* The Expression class is subject to change. Expressions should be provided via the string overload.
*
*/
- public int makeShape(final Region region, final Vector3 zero, final Vector3 unit,
+ public int makeShape(final Region region, Transform transform,
final Pattern pattern, final Expression expression, final boolean hollow, final int timeout)
throws ExpressionException, MaxChangedBlocksException {
@@ -2362,16 +2388,17 @@ public int makeShape(final Region region, final Vector3 zero, final Vector3 unit
final Variable dataVariable = expression.getSlots().getVariable("data")
.orElseThrow(IllegalStateException::new);
- final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
+ final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, transform);
expression.setEnvironment(environment);
final int[] timedOut = {0};
+ final Transform transformInverse = transform.inverse();
final ArbitraryShape shape = new ArbitraryShape(region) {
@Override
protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) {
final Vector3 current = Vector3.at(x, y, z);
environment.setCurrentBlock(current);
- final Vector3 scaled = current.subtract(zero).divide(unit);
+ final Vector3 inputPosition = transformInverse.apply(current);
try {
int[] legacy = LegacyMapper.getInstance().getLegacyFromBlock(defaultMaterial.toImmutableState());
@@ -2383,7 +2410,7 @@ protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial)
dataVar = legacy[1];
}
}
- if (expression.evaluate(new double[]{ scaled.x(), scaled.y(), scaled.z(), typeVar, dataVar}, timeout) <= 0) {
+ if (expression.evaluate(new double[]{ inputPosition.x(), inputPosition.y(), inputPosition.z(), typeVar, dataVar}, timeout) <= 0) {
return null;
}
int newType = (int) typeVariable.value();
@@ -2427,10 +2454,12 @@ protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial)
*
* @throws ExpressionException thrown on invalid expression input
* @throws MaxChangedBlocksException thrown if too many blocks are changed
+ * @deprecated Use {@link EditSession#deformRegion(Region, Transform, String, int, InputExtent, Transform)} and pass a {@link ScaleAndTranslateTransform}.
*/
+ @Deprecated
public int deformRegion(final Region region, final Vector3 zero, final Vector3 unit, final String expressionString)
throws ExpressionException, MaxChangedBlocksException {
- return deformRegion(region, zero, unit, expressionString, WorldEdit.getInstance().getConfiguration().calculationTimeout);
+ return deformRegion(region, new ScaleAndTranslateTransform(zero, unit), expressionString, WorldEdit.getInstance().getConfiguration().calculationTimeout);
}
/**
@@ -2448,12 +2477,55 @@ public int deformRegion(final Region region, final Vector3 zero, final Vector3 u
*
* @throws ExpressionException thrown on invalid expression input
* @throws MaxChangedBlocksException thrown if too many blocks are changed
+ * @deprecated Use {@link EditSession#deformRegion(Region, Transform, String, int, InputExtent, Transform)} and pass a {@link ScaleAndTranslateTransform}.
*/
+ @Deprecated
public int deformRegion(final Region region, final Vector3 zero, final Vector3 unit, final String expressionString,
final int timeout) throws ExpressionException, MaxChangedBlocksException {
+ final Transform transform = new ScaleAndTranslateTransform(zero, unit);
+ return deformRegion(region, transform, expressionString, timeout);
+ }
+
+ /**
+ * Deforms the region by a given expression. A deform provides a block's x, y, and z coordinates (possibly scaled)
+ * to an expression, and then sets the block to the block given by the resulting values of the variables, if they
+ * have changed.
+ *
+ * @param region the region to deform
+ * @param targetTransform the target coordinate system
+ * @param expressionString the expression to evaluate for each block
+ * @param timeout maximum time for the expression to evaluate for each block. -1 for unlimited.
+ * @param sourceExtent the InputExtent to fetch blocks from, for instance a World or a Clipboard
+ * @param sourceTransform the source coordinate system
+ * @return number of blocks changed
+ * @throws ExpressionException thrown on invalid expression input
+ * @throws MaxChangedBlocksException thrown if too many blocks are changed
+ */
+ public int deformRegion(final Region region, final Transform targetTransform, final String expressionString,
+ final int timeout, InputExtent sourceExtent, Transform sourceTransform) throws ExpressionException, MaxChangedBlocksException {
final Expression expression = Expression.compile(expressionString, "x", "y", "z");
expression.optimize();
- return deformRegion(region, zero, unit, expression, timeout);
+ return deformRegion(region, targetTransform, expression, timeout, sourceExtent, sourceTransform);
+ }
+
+ /**
+ * Deforms the region by a given expression. A deform provides a block's x, y, and z coordinates (possibly scaled)
+ * to an expression, and then sets the block to the block given by the resulting values of the variables, if they
+ * have changed.
+ *
+ * @param region the region to deform
+ * @param transform the coordinate system
+ * @param expressionString the expression to evaluate for each block
+ * @param timeout maximum time for the expression to evaluate for each block. -1 for unlimited.
+ * @return number of blocks changed
+ * @throws ExpressionException thrown on invalid expression input
+ * @throws MaxChangedBlocksException thrown if too many blocks are changed
+ * @deprecated Use {@link EditSession#deformRegion(Region, Transform, String, int, InputExtent, Transform)}.
+ */
+ @Deprecated
+ public int deformRegion(final Region region, final Transform transform, final String expressionString,
+ final int timeout) throws ExpressionException, MaxChangedBlocksException {
+ return deformRegion(region, transform, expressionString, timeout, world, transform);
}
/**
@@ -2463,8 +2535,8 @@ public int deformRegion(final Region region, final Vector3 zero, final Vector3 u
* The Expression class is subject to change. Expressions should be provided via the string overload.
*
*/
- public int deformRegion(final Region region, final Vector3 zero, final Vector3 unit, final Expression expression,
- final int timeout) throws ExpressionException, MaxChangedBlocksException {
+ public int deformRegion(final Region region, final Transform targetTransform, final Expression expression,
+ final int timeout, InputExtent sourceExtent, final Transform sourceTransform) throws ExpressionException, MaxChangedBlocksException {
final Variable x = expression.getSlots().getVariable("x")
.orElseThrow(IllegalStateException::new);
final Variable y = expression.getSlots().getVariable("y")
@@ -2472,25 +2544,28 @@ public int deformRegion(final Region region, final Vector3 zero, final Vector3 u
final Variable z = expression.getSlots().getVariable("z")
.orElseThrow(IllegalStateException::new);
- final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
+ final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, targetTransform);
expression.setEnvironment(environment);
final DoubleArrayList queue = new DoubleArrayList<>(false);
+ final Transform targetTransformInverse = targetTransform.inverse();
for (BlockVector3 targetBlockPosition : region) {
final Vector3 targetPosition = targetBlockPosition.toVector3();
environment.setCurrentBlock(targetPosition);
- // offset, scale
- final Vector3 scaled = targetPosition.subtract(zero).divide(unit);
+ // transform from target coordinates
+ final Vector3 inputPosition = targetTransformInverse.apply(targetPosition);
- // transform
- expression.evaluate(new double[]{ scaled.x(), scaled.y(), scaled.z() }, timeout);
+ // deform
+ expression.evaluate(new double[]{ inputPosition.x(), inputPosition.y(), inputPosition.z() }, timeout);
+ final Vector3 outputPosition = Vector3.at(x.value(), y.value(), z.value());
- final BlockVector3 sourcePosition = environment.toWorld(x.value(), y.value(), z.value());
+ // transform to source coordinates, round-nearest
+ final BlockVector3 sourcePosition = sourceTransform.apply(outputPosition).add(0.5, 0.5, 0.5).toBlockPoint();
- // read block from world
- final BaseBlock material = world.getFullBlock(sourcePosition);
+ // read block from source extent (e.g. world/clipboard)
+ final BaseBlock material = sourceExtent.getFullBlock(sourcePosition);
// queue operation
queue.put(targetBlockPosition, material);
@@ -2498,11 +2573,11 @@ public int deformRegion(final Region region, final Vector3 zero, final Vector3 u
int affected = 0;
for (Map.Entry entry : queue) {
- BlockVector3 position = entry.getKey();
+ BlockVector3 targetPosition = entry.getKey();
BaseBlock material = entry.getValue();
- // set at new position
- if (setBlock(position, material)) {
+ // set at new targetPosition
+ if (setBlock(targetPosition, material)) {
++affected;
}
}
@@ -2795,31 +2870,57 @@ private void recurseHollow(Region region, BlockVector3 origin, Set
}
}
+ /**
+ * @deprecated Use {@link EditSession#makeBiomeShape(Region, Transform, BiomeType, String, boolean, int)} and pass a {@link ScaleAndTranslateTransform}.
+ */
+ @Deprecated
public int makeBiomeShape(final Region region, final Vector3 zero, final Vector3 unit, final BiomeType biomeType,
final String expressionString, final boolean hollow) throws ExpressionException {
return makeBiomeShape(region, zero, unit, biomeType, expressionString, hollow, WorldEdit.getInstance().getConfiguration().calculationTimeout);
}
+ /**
+ * @deprecated Use {@link EditSession#makeBiomeShape(Region, Transform, BiomeType, String, boolean, int)} and pass a {@link ScaleAndTranslateTransform}.
+ */
+ @Deprecated
public int makeBiomeShape(final Region region, final Vector3 zero, final Vector3 unit, final BiomeType biomeType,
final String expressionString, final boolean hollow, final int timeout) throws ExpressionException {
+ return makeBiomeShape(region, new ScaleAndTranslateTransform(zero, unit), biomeType, expressionString, hollow, timeout);
+ }
+
+ /**
+ * Generate a biome shape for the given expression.
+ *
+ * @param region the region to generate the shape in
+ * @param transform the transformation for x/y/z variables
+ * @param biomeType the biome to make the shape from
+ * @param expressionString the expression defining the shape
+ * @param hollow whether the shape should be hollow
+ * @param timeout
+ * @return number of blocks changed
+ * @throws ExpressionException if there is a problem with the expression
+ */
+ public int makeBiomeShape(final Region region, Transform transform, final BiomeType biomeType,
+ final String expressionString, final boolean hollow, final int timeout) throws ExpressionException {
final Expression expression = Expression.compile(expressionString, "x", "y", "z");
expression.optimize();
final EditSession editSession = this;
- final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(editSession, unit, zero);
+ final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(editSession, transform);
expression.setEnvironment(environment);
AtomicInteger timedOut = new AtomicInteger();
+ final Transform transformInverse = transform.inverse();
final ArbitraryBiomeShape shape = new ArbitraryBiomeShape(region) {
@Override
protected BiomeType getBiome(int x, int y, int z, BiomeType defaultBiomeType) {
final Vector3 current = Vector3.at(x, y, z);
environment.setCurrentBlock(current);
- final Vector3 scaled = current.subtract(zero).divide(unit);
+ final Vector3 inputPosition = transformInverse.apply(current);
try {
- if (expression.evaluate(new double[]{ scaled.x(), scaled.y(), scaled.z() }, timeout) <= 0) {
+ if (expression.evaluate(new double[]{ inputPosition.x(), inputPosition.y(), inputPosition.z() }, timeout) <= 0) {
return null;
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java
index 8de47a2857..260cd5d11d 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java
@@ -71,6 +71,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
@@ -453,6 +454,15 @@ public ClipboardHolder getClipboard() throws EmptyClipboardException {
return clipboard;
}
+ /**
+ * Gets the clipboard.
+ *
+ * @return clipboard
+ */
+ public Optional getClipboardOptional() {
+ return Optional.ofNullable(clipboard);
+ }
+
/**
* Sets the clipboard.
*
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
index 120ba27db8..f2155b3e96 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
@@ -506,7 +506,9 @@ public void deform(Player player, LocalSession localSession,
@Switch(name = 'r', desc = "Use the game's coordinate origin")
boolean useRawCoords,
@Switch(name = 'o', desc = "Use the placement position as the origin")
- boolean usePlacement) throws WorldEditException {
+ boolean usePlacement,
+ @Switch(name = 'l', desc = "Fetch from the clipboard instead of the world")
+ boolean useClipboard) throws WorldEditException {
Deform deform = new Deform(expression);
if (useRawCoords) {
deform.setMode(Deform.Mode.RAW_COORD);
@@ -514,6 +516,7 @@ public void deform(Player player, LocalSession localSession,
deform.setMode(Deform.Mode.OFFSET);
deform.setOffset(localSession.getPlacementPosition(player).toVector3());
}
+ deform.setUseClipboard(useClipboard);
setOperationBasedBrush(player, localSession, radius,
deform, shape, "worldedit.brush.deform");
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java
index 1c7bb86655..7cd222f742 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java
@@ -32,8 +32,9 @@
import com.sk89q.worldedit.internal.annotation.Radii;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.expression.ExpressionException;
+import com.sk89q.worldedit.internal.util.TransformUtil;
import com.sk89q.worldedit.math.BlockVector3;
-import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
@@ -370,45 +371,13 @@ public int generate(Actor actor, LocalSession session, EditSession editSession,
@Switch(name = 'r', desc = "Use the game's coordinate origin")
boolean useRawCoords,
@Switch(name = 'o', desc = "Use the placement's coordinate origin")
- boolean offset,
+ boolean offsetPlacement,
@Switch(name = 'c', desc = "Use the selection's center as origin")
boolean offsetCenter) throws WorldEditException {
-
- final Vector3 zero;
- Vector3 unit;
-
- if (useRawCoords) {
- zero = Vector3.ZERO;
- unit = Vector3.ONE;
- } else if (offset) {
- zero = session.getPlacementPosition(actor).toVector3();
- unit = Vector3.ONE;
- } else if (offsetCenter) {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = Vector3.ONE;
- } else {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = max.subtract(zero);
-
- if (unit.x() == 0) {
- unit = unit.withX(1.0);
- }
- if (unit.y() == 0) {
- unit = unit.withY(1.0);
- }
- if (unit.z() == 0) {
- unit = unit.withZ(1.0);
- }
- }
+ final Transform transform = TransformUtil.createTransformForExpressionCommand(actor, session, region, useRawCoords, offsetPlacement, offsetCenter);
try {
- final int affected = editSession.makeShape(region, zero, unit, pattern, String.join(" ", expression), hollow, session.getTimeout());
+ final int affected = editSession.makeShape(region, transform, pattern, String.join(" ", expression), hollow, session.getTimeout());
if (actor instanceof Player) {
((Player) actor).findFreePosition();
}
@@ -439,44 +408,13 @@ public int generateBiome(Actor actor, LocalSession session, EditSession editSess
@Switch(name = 'r', desc = "Use the game's coordinate origin")
boolean useRawCoords,
@Switch(name = 'o', desc = "Use the placement's coordinate origin")
- boolean offset,
+ boolean offsetPlacement,
@Switch(name = 'c', desc = "Use the selection's center as origin")
boolean offsetCenter) throws WorldEditException {
- final Vector3 zero;
- Vector3 unit;
-
- if (useRawCoords) {
- zero = Vector3.ZERO;
- unit = Vector3.ONE;
- } else if (offset) {
- zero = session.getPlacementPosition(actor).toVector3();
- unit = Vector3.ONE;
- } else if (offsetCenter) {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = Vector3.ONE;
- } else {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = max.subtract(zero);
-
- if (unit.x() == 0) {
- unit = unit.withX(1.0);
- }
- if (unit.y() == 0) {
- unit = unit.withY(1.0);
- }
- if (unit.z() == 0) {
- unit = unit.withZ(1.0);
- }
- }
+ final Transform transform = TransformUtil.createTransformForExpressionCommand(actor, session, region, useRawCoords, offsetPlacement, offsetCenter);
try {
- final int affected = editSession.makeBiomeShape(region, zero, unit, target, String.join(" ", expression), hollow, session.getTimeout());
+ final int affected = editSession.makeBiomeShape(region, transform, target, String.join(" ", expression), hollow, session.getTimeout());
actor.printInfo(TranslatableComponent.of("worldedit.generatebiome.changed", TextComponent.of(affected)));
return affected;
} catch (ExpressionException e) {
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
index 5555bd399e..cdd0d70142 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
@@ -31,7 +31,9 @@
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.extent.InputExtent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.GroundFunction;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.RegionMaskingFilter;
@@ -49,6 +51,7 @@
import com.sk89q.worldedit.internal.annotation.Offset;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.expression.ExpressionException;
+import com.sk89q.worldedit.internal.util.TransformUtil;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.convolution.GaussianKernel;
@@ -56,6 +59,7 @@
import com.sk89q.worldedit.math.convolution.HeightMapFilter;
import com.sk89q.worldedit.math.convolution.SnowHeightMap;
import com.sk89q.worldedit.math.noise.RandomNoise;
+import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.ConvexPolyhedralRegion;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
@@ -498,44 +502,30 @@ public int deform(Actor actor, LocalSession session, EditSession editSession,
@Switch(name = 'r', desc = "Use the game's coordinate origin")
boolean useRawCoords,
@Switch(name = 'o', desc = "Use the placement's coordinate origin")
- boolean offset,
+ boolean offsetPlacement,
@Switch(name = 'c', desc = "Use the selection's center as origin")
- boolean offsetCenter) throws WorldEditException {
- final Vector3 zero;
- Vector3 unit;
-
- if (useRawCoords) {
- zero = Vector3.ZERO;
- unit = Vector3.ONE;
- } else if (offset) {
- zero = session.getPlacementPosition(actor).toVector3();
- unit = Vector3.ONE;
- } else if (offsetCenter) {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = Vector3.ONE;
- } else {
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
+ boolean offsetCenter,
+ @Switch(name = 'l', desc = "Fetch from the clipboard instead of the world")
+ boolean useClipboard) throws WorldEditException {
+ final Transform targetTransform = TransformUtil.createTransformForExpressionCommand(actor, session, region, useRawCoords, offsetPlacement, offsetCenter);
- zero = max.add(min).divide(2);
- unit = max.subtract(zero);
+ final InputExtent sourceExtent;
+ final Transform sourceTransform;
+ if (useClipboard) {
+ final Clipboard clipboard = session.getClipboard().getClipboard();
+ sourceExtent = clipboard;
- if (unit.x() == 0) {
- unit = unit.withX(1.0);
- }
- if (unit.y() == 0) {
- unit = unit.withY(1.0);
- }
- if (unit.z() == 0) {
- unit = unit.withZ(1.0);
- }
+ final Vector3 clipboardMin = clipboard.getMinimumPoint().toVector3();
+ final Vector3 clipboardMax = clipboard.getMaximumPoint().toVector3();
+
+ sourceTransform = TransformUtil.createTransformForExpressionCommand(useRawCoords, offsetPlacement, offsetCenter, clipboardMin, clipboardMax, clipboardMin);
+ } else {
+ sourceExtent = editSession.getWorld();
+ sourceTransform = targetTransform;
}
try {
- final int affected = editSession.deformRegion(region, zero, unit, String.join(" ", expression), session.getTimeout());
+ final int affected = editSession.deformRegion(region, targetTransform, String.join(" ", expression), session.getTimeout(), sourceExtent, sourceTransform);
if (actor instanceof Player) {
((Player) actor).findFreePosition();
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java
index c58af7d566..09f684cf2e 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java
@@ -27,7 +27,7 @@
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.internal.registry.InputParser;
-import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.Identity;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.session.SessionOwner;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
@@ -58,7 +58,7 @@ public Mask parseFromInput(String input, ParserContext context) throws InputPars
try {
Expression exp = Expression.compile(input.substring(1), "x", "y", "z");
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
- context.requireExtent(), Vector3.ONE, Vector3.ZERO);
+ context.requireExtent(), new Identity());
exp.setEnvironment(env);
if (context.getActor() != null) {
SessionOwner owner = context.getActor();
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/factory/Deform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/factory/Deform.java
index 017e8b3a3e..6136a969b2 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/factory/Deform.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/factory/Deform.java
@@ -25,21 +25,28 @@
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.extent.InputExtent;
import com.sk89q.worldedit.extent.NullExtent;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.Contextual;
import com.sk89q.worldedit.function.EditContext;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
+import com.sk89q.worldedit.internal.util.TransformUtil;
import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.NullRegion;
import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
+import java.util.Optional;
+
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
@@ -50,6 +57,7 @@ public class Deform implements Contextual {
private final Expression expression;
private Mode mode;
private Vector3 offset = Vector3.ZERO;
+ private boolean useClipboard;
public Deform(String expression) {
this(new NullExtent(), new NullRegion(), expression);
@@ -111,6 +119,14 @@ public void setOffset(Vector3 offset) {
this.offset = offset;
}
+ public boolean useClipboard() {
+ return useClipboard;
+ }
+
+ public void setUseClipboard(boolean useClipboard) {
+ this.useClipboard = useClipboard;
+ }
+
@Override
public String toString() {
return "deformation of " + expression.getSource();
@@ -118,57 +134,51 @@ public String toString() {
@Override
public Operation createFromContext(final EditContext context) {
- final Vector3 zero;
- Vector3 unit;
Region region = firstNonNull(context.getRegion(), this.region);
+ final Vector3 min = region.getMinimumPoint().toVector3();
+ final Vector3 max = region.getMaximumPoint().toVector3();
- switch (mode) {
- case UNIT_CUBE:
- final Vector3 min = region.getMinimumPoint().toVector3();
- final Vector3 max = region.getMaximumPoint().toVector3();
-
- zero = max.add(min).multiply(0.5);
- unit = max.subtract(zero);
-
- if (unit.x() == 0) {
- unit = unit.withX(1.0);
- }
- if (unit.y() == 0) {
- unit = unit.withY(1.0);
- }
- if (unit.z() == 0) {
- unit = unit.withZ(1.0);
- }
- break;
- case RAW_COORD:
- zero = Vector3.ZERO;
- unit = Vector3.ONE;
- break;
- case OFFSET:
- default:
- zero = offset;
- unit = Vector3.ONE;
+ LocalSession session = context.getSession();
+ EditSession editSession = (EditSession) context.getDestination();
+ final Optional clipboardOptional = Optional.ofNullable(session)
+ .flatMap(LocalSession::getClipboardOptional)
+ .map(ClipboardHolder::getClipboard);
+
+ final Transform targetTransform = TransformUtil.createTransformForExpressionCommand(mode, min, max, offset);
+ final InputExtent sourceExtent;
+ final Transform sourceTransform;
+ if (useClipboard && clipboardOptional.isPresent()) {
+ final Clipboard clipboard = clipboardOptional.get();
+ final Vector3 clipboardMin = clipboard.getMinimumPoint().toVector3();
+ final Vector3 clipboardMax = clipboard.getMaximumPoint().toVector3();
+
+ sourceExtent = clipboard;
+ sourceTransform = TransformUtil.createTransformForExpressionCommand(mode, clipboardMin, clipboardMax, offset);
+ } else {
+ sourceExtent = editSession.getWorld();
+ sourceTransform = targetTransform;
}
- LocalSession session = context.getSession();
- return new DeformOperation(context.getDestination(), region, zero, unit, expression,
- session == null ? WorldEdit.getInstance().getConfiguration().calculationTimeout : session.getTimeout());
+ return new DeformOperation(context.getDestination(), region, targetTransform, expression,
+ session == null ? WorldEdit.getInstance().getConfiguration().calculationTimeout : session.getTimeout(), sourceExtent, sourceTransform);
}
private record DeformOperation(
Extent destination,
Region region,
- Vector3 zero,
- Vector3 unit,
+ Transform targetTransform,
Expression expression,
- int timeout
+ int timeout,
+ InputExtent sourceExtent,
+ Transform sourceTransform
) implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
try {
// TODO: Move deformation code
- ((EditSession) destination).deformRegion(region, zero, unit, expression, timeout);
+ final EditSession editSession = (EditSession) destination;
+ editSession.deformRegion(region, targetTransform, expression, timeout, sourceExtent, sourceTransform);
return null;
} catch (ExpressionException e) {
throw new RuntimeException("Failed to execute expression", e); // TODO: Better exception to throw here?
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/TransformUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/TransformUtil.java
new file mode 100644
index 0000000000..a1c1ee5151
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/TransformUtil.java
@@ -0,0 +1,121 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.util;
+
+import com.sk89q.worldedit.IncompleteRegionException;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.function.factory.Deform;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.Identity;
+import com.sk89q.worldedit.math.transform.ScaleAndTranslateTransform;
+import com.sk89q.worldedit.math.transform.Transform;
+import com.sk89q.worldedit.regions.Region;
+
+/**
+ * Various internal utility methods related to {@link Transform}s.
+ */
+public final class TransformUtil {
+
+ private TransformUtil() {
+ }
+
+ /**
+ * Creates a {@link Transform} for various expression commands.
+ *
+ * @param actor Actor that ran the command
+ * @param session Session that the command was run in
+ * @param region Selection that the command was run in
+ * @param useRawCoords Use the game's coordinate origin
+ * @param offsetPlacement Use the placement's coordinate origin
+ * @param offsetCenter Use the selection's center as origin
+ * @return A transform from the expression coordinate system to the raw coordinate system
+ */
+ public static Transform createTransformForExpressionCommand(Actor actor, LocalSession session, Region region, boolean useRawCoords, boolean offsetPlacement, boolean offsetCenter) throws IncompleteRegionException {
+ final Vector3 placement = session.getPlacementPosition(actor).toVector3();
+ final Vector3 min = region.getMinimumPoint().toVector3();
+ final Vector3 max = region.getMaximumPoint().toVector3();
+
+ return createTransformForExpressionCommand(useRawCoords, offsetPlacement, offsetCenter, min, max, placement);
+ }
+
+ /**
+ * Creates a {@link Transform} for various expression commands from raw min/max/placement values.
+ *
+ * @param useRawCoords Use the game's coordinate origin
+ * @param offsetPlacement Use the placement's coordinate origin
+ * @param offsetCenter Use the selection's center as origin
+ * @param min Minimum of the selection/clipboard
+ * @param max Maximum of the selection/clipboard
+ * @param placement Placement position
+ * @return A transform from the expression coordinate system to the world/clipboard coordinate system
+ */
+ public static Transform createTransformForExpressionCommand(boolean useRawCoords, boolean offsetPlacement, boolean offsetCenter, Vector3 min, Vector3 max, Vector3 placement) {
+ if (useRawCoords) {
+ return new Identity();
+ }
+
+ if (offsetPlacement) {
+ return new ScaleAndTranslateTransform(placement, Vector3.ONE);
+ }
+
+ final Vector3 center = max.add(min).multiply(0.5);
+
+ if (offsetCenter) {
+ return new ScaleAndTranslateTransform(center, Vector3.ONE);
+ }
+
+ Vector3 scale = max.subtract(center);
+
+ if (scale.x() == 0) {
+ scale = scale.withX(1.0);
+ }
+ if (scale.y() == 0) {
+ scale = scale.withY(1.0);
+ }
+ if (scale.z() == 0) {
+ scale = scale.withZ(1.0);
+ }
+ return new ScaleAndTranslateTransform(center, scale);
+ }
+
+ /**
+ * Creates a {@link Transform} for the //deform command with clipboard support.
+ *
+ * @param mode The coordinate mode to use
+ * @param min Minimum of the selection/clipboard
+ * @param max Maximum of the selection/clipboard
+ * @param placement Placement position
+ * @return A transform from the expression coordinate system to the world/clipboard coordinate system
+ */
+ public static Transform createTransformForExpressionCommand(Deform.Mode mode, Vector3 min, Vector3 max, Vector3 placement) {
+ switch (mode) {
+ case UNIT_CUBE:
+ return createTransformForExpressionCommand(false, false, false, min, max, placement);
+
+ case RAW_COORD:
+ return createTransformForExpressionCommand(true, false, false, min, max, placement);
+
+ case OFFSET:
+ default:
+ return createTransformForExpressionCommand(false, true, false, min, max, placement);
+ }
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/ScaleAndTranslateTransform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/ScaleAndTranslateTransform.java
new file mode 100644
index 0000000000..93630a8688
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/ScaleAndTranslateTransform.java
@@ -0,0 +1,68 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math.transform;
+
+import com.sk89q.worldedit.math.Vector3;
+
+/**
+ * A more light-weight {@link Transform} than {@link AffineTransform}, supporting only translation and scaling.
+ */
+public record ScaleAndTranslateTransform(Vector3 offset, Vector3 scale) implements Transform {
+
+ @Override
+ public boolean isIdentity() {
+ return offset.equals(Vector3.ZERO) && scale.equals(Vector3.ONE);
+ }
+
+ @Override
+ public Vector3 apply(Vector3 input) {
+ return input.multiply(scale).add(offset);
+ }
+
+ @Override
+ public Transform inverse() {
+ return new Transform() {
+ @Override
+ public boolean isIdentity() {
+ return ScaleAndTranslateTransform.this.isIdentity();
+ }
+
+ @Override
+ public Vector3 apply(Vector3 input) {
+ return input.subtract(offset).divide(scale);
+ }
+
+ @Override
+ public Transform inverse() {
+ return ScaleAndTranslateTransform.this;
+ }
+
+ @Override
+ public Transform combine(Transform other) {
+ return new CombinedTransform(this, other);
+ }
+ };
+ }
+
+ @Override
+ public Transform combine(Transform other) {
+ return new CombinedTransform(this, other);
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
index 56763d3bc2..a9f785ce3a 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
@@ -23,24 +23,31 @@
import com.sk89q.worldedit.internal.expression.ExpressionEnvironment;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.ScaleAndTranslateTransform;
+import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.world.registry.LegacyMapper;
public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
- private final Vector3 unit;
- private final Vector3 zero2;
+ private final Transform transform;
private Vector3 current = Vector3.ZERO;
private final Extent extent;
+ /**
+ * @deprecated Use {@link EditSession#makeBiomeShape(Region, Transform, BiomeType, String, boolean, int)} and pass a {@link ScaleAndTranslateTransform}.
+ */
+ @Deprecated
public WorldEditExpressionEnvironment(Extent extent, Vector3 unit, Vector3 zero) {
+ this(extent, new ScaleAndTranslateTransform(zero, unit));
+ }
+
+ public WorldEditExpressionEnvironment(Extent extent, Transform transform) {
this.extent = extent;
- this.unit = unit;
- this.zero2 = zero.add(0.5, 0.5, 0.5);
+ this.transform = transform;
}
public BlockVector3 toWorld(double x, double y, double z) {
- // unscale, unoffset, round-nearest
- return Vector3.at(x, y, z).multiply(unit).add(zero2).toBlockPoint();
+ return transform.apply(Vector3.at(x, y, z)).add(0.5, 0.5, 0.5).toBlockPoint();
}
public Vector3 toWorldRel(double x, double y, double z) {