Skip to content
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 @@ -768,6 +768,7 @@ public void select(Actor actor, World world, LocalSession session,
box.appendCommand("cuboid", TranslatableComponent.of("worldedit.select.cuboid.description"), "//sel cuboid");
box.appendCommand("extend", TranslatableComponent.of("worldedit.select.extend.description"), "//sel extend");
box.appendCommand("poly", TranslatableComponent.of("worldedit.select.poly.description"), "//sel poly");
box.appendCommand("polyextend", TranslatableComponent.of("worldedit.select.polyextend.description"), "//sel polyextend");
box.appendCommand("ellipsoid", TranslatableComponent.of("worldedit.select.ellipsoid.description"), "//sel ellipsoid");
box.appendCommand("sphere", TranslatableComponent.of("worldedit.select.sphere.description"), "//sel sphere");
box.appendCommand("cyl", TranslatableComponent.of("worldedit.select.cyl.description"), "//sel cyl");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.sk89q.worldedit.regions.selector.CylinderRegionSelector;
import com.sk89q.worldedit.regions.selector.EllipsoidRegionSelector;
import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector;
import com.sk89q.worldedit.regions.selector.ExtendingPolygonal2DRegionSelector;
import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector;
import com.sk89q.worldedit.regions.selector.SphereRegionSelector;
import com.sk89q.worldedit.util.formatting.text.Component;
Expand All @@ -50,6 +51,16 @@ public void explainNewSelector(Actor actor) {
)));
}
},
POLYEXTEND(ExtendingPolygonal2DRegionSelector::new, ExtendingPolygonal2DRegionSelector::new, "worldedit.select.polyextend.message") {
@Override
public void explainNewSelector(Actor actor) {
super.explainNewSelector(actor);
Optional<Integer> limit = ActorSelectorLimits.forActor(actor).getPolygonVertexLimit();
limit.ifPresent(integer -> actor.printInfo(TranslatableComponent.of(
"worldedit.select.poly.limit-message", TextComponent.of(integer)
)));
}
},
ELLIPSOID(EllipsoidRegionSelector::new, EllipsoidRegionSelector::new, "worldedit.select.ellipsoid.message"),
SPHERE(SphereRegionSelector::new, SphereRegionSelector::new, "worldedit.select.sphere.message"),
CYL(CylinderRegionSelector::new, CylinderRegionSelector::new, "worldedit.select.cyl.message"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ public void addPoint(BlockVector2 position) {
recalculate();
}

/**
* Insert a point into the list.
*
* @param i index to insert after
* @param position the position
*/
public void addPoint(int i, BlockVector2 position) {
points.add(i, position);
recalculate();
}

/**
* Add a point to the list.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 <https://www.gnu.org/licenses/>.
*/

package com.sk89q.worldedit.regions.selector;

import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector2;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.world.World;

import java.util.List;
import javax.annotation.Nullable;

/**
* Creates a {@code Polygonal2DRegion} from a user's selections by expanding
* the region on every right click.
*/
public class ExtendingPolygonal2DRegionSelector extends Polygonal2DRegionSelector {

/**
* Create a new selector with a {@code null} world.
*/
public ExtendingPolygonal2DRegionSelector() {
super((World) null);
}

/**
* Create a new selector.
*
* @param world the world, which may be {@code null}
*/
public ExtendingPolygonal2DRegionSelector(@Nullable World world) {
super(world);
}

/**
* Create a new selector from another one.
*
* @param oldSelector the other selector
*/
public ExtendingPolygonal2DRegionSelector(RegionSelector oldSelector) {
super(oldSelector);
}

/**
* Create a new selector.
*
* @param world the world
* @param points a list of points
* @param minY the minimum Y
* @param maxY the maximum Y
*/
public ExtendingPolygonal2DRegionSelector(@Nullable World world, List<BlockVector2> points, int minY, int maxY) {
super(world, points, minY, maxY);
}

private static double distanceFromLine(Vector2 position, Vector2 start, Vector2 end) {
// Vector from start to end
final Vector2 difference = end.subtract(start);

// Distance between start and end
final double length = difference.length();

// Normalized direction vector pointing from start towards end
final Vector2 normalizedDifference = difference.divide(length);

// Distance from start to position, projected onto the line
final double determinant = position.subtract(start).dot(normalizedDifference);

if (determinant <= 0) {
// We're before the line start
return position.distance(start);
} else if (determinant >= length) {
// We're beyond the line end
return position.distance(end);
} else {
// We're next to the line

// Calculate normalized normal by rotating the normalized difference 90°
final Vector2 normalizedNormal = new Vector2(-normalizedDifference.z(), normalizedDifference.x());

// Calculate and return the absolute distance along that normal
return Math.abs(position.subtract(start).dot(normalizedNormal));
}
}

@Override
public boolean selectSecondary(BlockVector3 position, SelectorLimits limits) {
// Use the regular algo until we have at least 3 points
if (region.size() < 3) {
return super.selectSecondary(position, limits);
}

final BlockVector2 blockPosition2D = position.toBlockVector2();
final Vector2 position2D = blockPosition2D.toVector2();

// Find the closest edge
final List<BlockVector2> points = region.getPoints();
double minDistance = Integer.MAX_VALUE;
int insertBeforeIndex = -1;

// Start with the last vertex to close the loop
Vector2 previousPoint = points.getLast().toVector2();

for (int i = 0; i < points.size(); i++) {
final BlockVector2 blockPoint = points.get(i);

// Don't duplicate points
if (blockPoint.equals(blockPosition2D)) {
return false;
}

final Vector2 point = blockPoint.toVector2();

// Calculate distance from edge
final double currentDistance = distanceFromLine(position2D, previousPoint, point);
if (currentDistance < minDistance) {
minDistance = currentDistance;
insertBeforeIndex = i;
}

// Keep previousPoint for the next iteration
previousPoint = point;
}

assert insertBeforeIndex >= 0;

// Insert new point on the determined edge
region.addPoint(insertBeforeIndex, blockPosition2D);
region.expandY(position.y());

return true;
}

@Override
public void explainSecondarySelection(Actor player, LocalSession session, BlockVector3 pos) {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.polygon2d.explain.secondary",
TextComponent.of(region.size()),
TextComponent.of(pos.toString())
));

explainRegionAdjust(player, session);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion {

private transient BlockVector3 pos1;
private transient Polygonal2DRegion region;
protected transient Polygonal2DRegion region;

/**
* Create a new selector with a {@code null} world.
Expand Down
4 changes: 3 additions & 1 deletion worldedit-core/src/main/resources/lang/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,12 @@
"worldedit.select.cuboid.message": "Cuboid: left click for point 1, right click for point 2",
"worldedit.select.cuboid.description": "Select two corners of a cuboid",
"worldedit.select.extend.message": "Cuboid: left click for a starting point, right click to extend",
"worldedit.select.extend.description": "Fast cuboid selection mode",
"worldedit.select.extend.description": "Extending cuboid selection mode",
"worldedit.select.poly.message": "2D polygon selector: Left/right click to add a point.",
"worldedit.select.poly.limit-message": "{0} points maximum.",
"worldedit.select.poly.description": "Select a 2D polygon with height",
"worldedit.select.polyextend.message": "2D polygon selector: left click for a starting point, right click to extend",
"worldedit.select.polyextend.description": "Extending 2D polygonal selection mode",
"worldedit.select.ellipsoid.message": "Ellipsoid selector: left click=center, right click to extend",
"worldedit.select.ellipsoid.description": "Select an ellipsoid",
"worldedit.select.sphere.message": "Sphere selector: left click=center, right click to set radius",
Expand Down
Loading