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..9169a6231e 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,56 @@ 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 zero the coordinate origin for x/y/z variables
+ * @param unit the scale of the x/y/z/ variables
+ * @param pattern the default material to make the shape from
+ * @param expression 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
+ * @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 Expression expression, final boolean hollow, final int timeout)
+ throws ExpressionException, MaxChangedBlocksException {
+ return makeShape(region, new ScaleAndTranslateTransform(zero, unit), pattern, expression, 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 +2394,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 +2410,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 +2432,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,7 +2476,9 @@ 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);
@@ -2448,7 +2499,9 @@ 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 Expression expression = Expression.compile(expressionString, "x", "y", "z");
@@ -2456,6 +2509,50 @@ public int deformRegion(final Region region, final Vector3 zero, final Vector3 u
return deformRegion(region, zero, unit, expression, 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, 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 zero the coordinate origin for x/y/z variables
+ * @param unit the scale of the x/y/z/ variables
+ * @param expression 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 Vector3 zero, final Vector3 unit, final Expression expression,
+ final int timeout) throws ExpressionException, MaxChangedBlocksException {
+ var transform = new ScaleAndTranslateTransform(zero, unit);
+ return deformRegion(region, transform, expression, timeout, world, transform);
+ }
+
/**
* Internal version of {@link EditSession#deformRegion(Region, Vector3, Vector3, String, int)}.
*
@@ -2463,8 +2560,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 +2569,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 +2598,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 +2895,61 @@ private void recurseHollow(Region region, BlockVector3 origin, Set
}
}
+ /**
+ * Generate a biome shape for the given expression.
+ *
+ * @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);
}
+ /**
+ * Generate a biome shape for the given expression.
+ *
+ * @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 maximum time for the expression to evaluate for each block. -1 for unlimited.
+ * @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/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..6ae469b4c1 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
@@ -33,6 +33,9 @@
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
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.NullRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.formatting.text.Component;
@@ -118,18 +121,16 @@ public String toString() {
@Override
public Operation createFromContext(final EditContext context) {
- final Vector3 zero;
- Vector3 unit;
Region region = firstNonNull(context.getRegion(), this.region);
+ final Transform transform;
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);
+ final Vector3 zero = max.add(min).multiply(0.5);
+ Vector3 unit = max.subtract(zero);
if (unit.x() == 0) {
unit = unit.withX(1.0);
@@ -140,27 +141,28 @@ public Operation createFromContext(final EditContext context) {
if (unit.z() == 0) {
unit = unit.withZ(1.0);
}
+
+ transform = new ScaleAndTranslateTransform(zero, unit);
break;
+
case RAW_COORD:
- zero = Vector3.ZERO;
- unit = Vector3.ONE;
+ transform = new Identity();
break;
+
case OFFSET:
default:
- zero = offset;
- unit = Vector3.ONE;
+ transform = new ScaleAndTranslateTransform(offset, Vector3.ONE);
+ break;
}
-
LocalSession session = context.getSession();
- return new DeformOperation(context.getDestination(), region, zero, unit, expression,
+ return new DeformOperation(context.getDestination(), region, transform, expression,
session == null ? WorldEdit.getInstance().getConfiguration().calculationTimeout : session.getTimeout());
}
private record DeformOperation(
Extent destination,
Region region,
- Vector3 zero,
- Vector3 unit,
+ Transform transform,
Expression expression,
int timeout
) implements Operation {
@@ -168,7 +170,8 @@ private record DeformOperation(
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, transform, expression, timeout, editSession.getWorld(), transform);
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..c58c8a501f
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/TransformUtil.java
@@ -0,0 +1,97 @@
+/*
+ * 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.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);
+ }
+}
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..27926d3da1 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,33 @@
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;
+ /**
+ * Creates a new WorldEditExpressionEnvironment.
+ *
+ * @deprecated Use {@link #WorldEditExpressionEnvironment(Extent, Transform)} 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) {