Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the trim command #2278

Merged
merged 2 commits into from
Jun 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.block.BlockDistributionCounter;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.MaskIntersection;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.RegionMask;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.visitor.RegionVisitor;
import com.sk89q.worldedit.internal.annotation.Chunk3d;
import com.sk89q.worldedit.internal.annotation.Direction;
import com.sk89q.worldedit.internal.annotation.MultiDirection;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.regions.RegionSelector;
Expand Down Expand Up @@ -504,6 +508,141 @@ private BlockVector3[] getChangesForEachDir(int amount, boolean onlyHorizontal,
return changes.build().map(v -> v.multiply(amount)).toArray(BlockVector3[]::new);
}

@Command(
name = "/trim",
desc = "Minimize the selection to encompass matching blocks"
)
@Logging(REGION)
@CommandPermissions("worldedit.selection.trim")
public void trim(Actor actor, World world, LocalSession session,
@Arg(desc = "Mask of blocks to keep within the selection", def = "#existing")
Mask mask) throws WorldEditException {
// Avoid checking blocks outside the original region but within the cuboid region
Region originalRegion = session.getSelection(world);
if (!(originalRegion instanceof CuboidRegion)) {
mask = new MaskIntersection(new RegionMask(originalRegion), mask);
}
// Memoize the mask to reduce duplicated lookups
mask = Masks.memoize(mask);

// Result region will be cuboid
CuboidRegion region = originalRegion.getBoundingBox();

BlockVector3 min = region.getMinimumPoint();
BlockVector3 max = region.getMaximumPoint();

int minY = 0;
boolean found = false;

outer: for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
found = true;
minY = y;

break outer;
}
}
}
}

// If anything was found in the first pass, then the remaining variables are guaranteed to be set
if (!found) {
throw new StopExecutionException(TranslatableComponent.of(
"worldedit.trim.no-blocks"));
}

int maxY = minY;

outer: for (int y = max.getBlockY(); y > minY; y--) {
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
maxY = y;
break outer;
}
}
}
}

int minX = 0;

outer: for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
for (int y = minY; y <= maxY; y++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
minX = x;
break outer;
}
}
}
}

int maxX = minX;

outer: for (int x = max.getBlockX(); x > minX; x--) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
for (int y = minY; y <= maxY; y++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
maxX = x;
break outer;
}
}
}
}

int minZ = 0;

outer: for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
minZ = z;
break outer;
}
}
}
}

int maxZ = minZ;

outer: for (int z = max.getBlockZ(); z > minZ; z--) {
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
BlockVector3 vec = BlockVector3.at(x, y, z);

if (mask.test(vec)) {
maxZ = z;
break outer;
}
}
}
}

final CuboidRegionSelector selector;
if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) {
selector = new ExtendingCuboidRegionSelector(world, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ));
} else {
selector = new CuboidRegionSelector(world, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ));
}
session.setRegionSelector(world, selector);

session.getRegionSelector(world).learnChanges();
session.getRegionSelector(world).explainRegionAdjust(actor, session);
actor.printInfo(TranslatableComponent.of("worldedit.trim.trim"));
}

@Command(
name = "/size",
desc = "Get information about the selection"
Expand Down
2 changes: 2 additions & 0 deletions worldedit-core/src/main/resources/lang/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@
"worldedit.shift.shifted": "Region shifted.",
"worldedit.outset.outset": "Region outset.",
"worldedit.inset.inset": "Region inset.",
"worldedit.trim.trim": "Region trimmed.",
"worldedit.trim.no-blocks": "No blocks matched the trim mask.",
"worldedit.size.offset": "Offset: {0}",
"worldedit.size.type": "Type: {0}",
"worldedit.size.size": "Size: {0}",
Expand Down