|
47 | 47 | import com.sk89q.worldedit.extent.clipboard.Clipboard; |
48 | 48 | import com.sk89q.worldedit.function.block.BlockDistributionCounter; |
49 | 49 | import com.sk89q.worldedit.function.mask.Mask; |
| 50 | +import com.sk89q.worldedit.function.mask.MaskIntersection; |
| 51 | +import com.sk89q.worldedit.function.mask.Masks; |
| 52 | +import com.sk89q.worldedit.function.mask.RegionMask; |
50 | 53 | import com.sk89q.worldedit.function.operation.Operations; |
51 | 54 | import com.sk89q.worldedit.function.visitor.RegionVisitor; |
52 | 55 | import com.sk89q.worldedit.internal.annotation.Direction; |
53 | 56 | import com.sk89q.worldedit.internal.annotation.MultiDirection; |
54 | 57 | import com.sk89q.worldedit.math.BlockVector3; |
| 58 | +import com.sk89q.worldedit.regions.CuboidRegion; |
55 | 59 | import com.sk89q.worldedit.regions.Region; |
56 | 60 | import com.sk89q.worldedit.regions.RegionOperationException; |
57 | 61 | import com.sk89q.worldedit.regions.RegionSelector; |
@@ -508,6 +512,138 @@ private BlockVector3[] getChangesForEachDir(int amount, boolean onlyHorizontal, |
508 | 512 | return changes.build().map(v -> v.multiply(amount)).toArray(BlockVector3[]::new); |
509 | 513 | } |
510 | 514 |
|
| 515 | + @Command( |
| 516 | + name = "/trim", |
| 517 | + desc = "Minimize the selection to encompass matching blocks" |
| 518 | + ) |
| 519 | + @Logging(REGION) |
| 520 | + @CommandPermissions("worldedit.selection.trim") |
| 521 | + public void trim(Actor actor, World world, LocalSession session, |
| 522 | + @Arg(desc = "Mask of blocks to keep within the selection", def = "#existing") |
| 523 | + Mask mask) throws WorldEditException { |
| 524 | + // Avoid checking blocks outside the original region but within the cuboid region |
| 525 | + Region originalRegion = session.getSelection(world); |
| 526 | + if (!(originalRegion instanceof CuboidRegion)) { |
| 527 | + mask = new MaskIntersection(new RegionMask(originalRegion), mask); |
| 528 | + } |
| 529 | + |
| 530 | + // Result region will be cuboid |
| 531 | + CuboidRegion region = originalRegion.getBoundingBox(); |
| 532 | + |
| 533 | + BlockVector3 min = region.getMinimumPoint(); |
| 534 | + BlockVector3 max = region.getMaximumPoint(); |
| 535 | + |
| 536 | + int minY = 0; |
| 537 | + boolean found = false; |
| 538 | + |
| 539 | + outer: for (int y = min.y(); y <= max.y(); y++) { |
| 540 | + for (int x = min.x(); x <= max.x(); x++) { |
| 541 | + for (int z = min.z(); z <= max.z(); z++) { |
| 542 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 543 | + |
| 544 | + if (mask.test(vec)) { |
| 545 | + found = true; |
| 546 | + minY = y; |
| 547 | + |
| 548 | + break outer; |
| 549 | + } |
| 550 | + } |
| 551 | + } |
| 552 | + } |
| 553 | + |
| 554 | + // If anything was found in the first pass, then the remaining variables are guaranteed to be set |
| 555 | + if (!found) { |
| 556 | + throw new StopExecutionException(Caption.of("worldedit.trim.no-blocks")); |
| 557 | + } |
| 558 | + |
| 559 | + int maxY = minY; |
| 560 | + |
| 561 | + outer: for (int y = max.y(); y > minY; y--) { |
| 562 | + for (int x = min.x(); x <= max.x(); x++) { |
| 563 | + for (int z = min.z(); z <= max.z(); z++) { |
| 564 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 565 | + |
| 566 | + if (mask.test(vec)) { |
| 567 | + maxY = y; |
| 568 | + break outer; |
| 569 | + } |
| 570 | + } |
| 571 | + } |
| 572 | + } |
| 573 | + |
| 574 | + int minX = 0; |
| 575 | + |
| 576 | + outer: for (int x = min.x(); x <= max.x(); x++) { |
| 577 | + for (int z = min.z(); z <= max.z(); z++) { |
| 578 | + for (int y = minY; y <= maxY; y++) { |
| 579 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 580 | + |
| 581 | + if (mask.test(vec)) { |
| 582 | + minX = x; |
| 583 | + break outer; |
| 584 | + } |
| 585 | + } |
| 586 | + } |
| 587 | + } |
| 588 | + |
| 589 | + int maxX = minX; |
| 590 | + |
| 591 | + outer: for (int x = max.x(); x > minX; x--) { |
| 592 | + for (int z = min.z(); z <= max.z(); z++) { |
| 593 | + for (int y = minY; y <= maxY; y++) { |
| 594 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 595 | + |
| 596 | + if (mask.test(vec)) { |
| 597 | + maxX = x; |
| 598 | + break outer; |
| 599 | + } |
| 600 | + } |
| 601 | + } |
| 602 | + } |
| 603 | + |
| 604 | + int minZ = 0; |
| 605 | + |
| 606 | + outer: for (int z = min.z(); z <= max.z(); z++) { |
| 607 | + for (int x = minX; x <= maxX; x++) { |
| 608 | + for (int y = minY; y <= maxY; y++) { |
| 609 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 610 | + |
| 611 | + if (mask.test(vec)) { |
| 612 | + minZ = z; |
| 613 | + break outer; |
| 614 | + } |
| 615 | + } |
| 616 | + } |
| 617 | + } |
| 618 | + |
| 619 | + int maxZ = minZ; |
| 620 | + |
| 621 | + outer: for (int z = max.z(); z > minZ; z--) { |
| 622 | + for (int x = minX; x <= maxX; x++) { |
| 623 | + for (int y = minY; y <= maxY; y++) { |
| 624 | + BlockVector3 vec = BlockVector3.at(x, y, z); |
| 625 | + |
| 626 | + if (mask.test(vec)) { |
| 627 | + maxZ = z; |
| 628 | + break outer; |
| 629 | + } |
| 630 | + } |
| 631 | + } |
| 632 | + } |
| 633 | + |
| 634 | + final CuboidRegionSelector selector; |
| 635 | + if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { |
| 636 | + selector = new ExtendingCuboidRegionSelector(world, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ)); |
| 637 | + } else { |
| 638 | + selector = new CuboidRegionSelector(world, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ)); |
| 639 | + } |
| 640 | + session.setRegionSelector(world, selector); |
| 641 | + |
| 642 | + session.getRegionSelector(world).learnChanges(); |
| 643 | + session.getRegionSelector(world).explainRegionAdjust(actor, session); |
| 644 | + actor.print(Caption.of("worldedit.trim.trim")); |
| 645 | + } |
| 646 | + |
511 | 647 | @Command( |
512 | 648 | name = "/size", |
513 | 649 | desc = "Get information about the selection" |
|
0 commit comments