use of com.sk89q.worldedit.math.BlockVector2 in project ProtectionStones by espidev.
the class RegionTraverse method traverseRegionEdge.
// can't use recursion because stack overflow
// doesn't do so well with 1 block wide segments jutting out
public static void traverseRegionEdge(HashSet<BlockVector2> points, List<ProtectedRegion> regions, Consumer<TraverseReturn> run) {
int pointID = 0;
while (!points.isEmpty()) {
BlockVector2 start = points.iterator().next();
TraverseData td = new TraverseData(start, null, true);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ algorithm starts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boolean cont = true;
while (cont) {
cont = false;
BlockVector2 v = td.v, previous = td.previous;
if (!td.first && v.equals(start))
break;
int exposedEdges = 0;
List<BlockVector2> insideVertex = new ArrayList<>();
for (Vector2 dir : DIRECTIONS) {
BlockVector2 test = BlockVector2.at(v.getX() + dir.getX(), v.getZ() + dir.getZ());
if (!isInRegion(test, regions)) {
exposedEdges++;
} else {
insideVertex.add(test);
}
}
// remove current point if it exists
points.remove(v);
switch(exposedEdges) {
case // normal edge
1:
// run consumer
run.accept(new TraverseReturn(v, false, pointID, exposedEdges));
if (previous == null) {
// if this is the first node we need to determine a direction to go to (that isn't into the polygon, but is on edge)
if (insideVertex.get(0).getX() == insideVertex.get(1).getZ() || insideVertex.get(0).getZ() == insideVertex.get(1).getZ() || insideVertex.get(0).getX() == insideVertex.get(2).getZ() || insideVertex.get(0).getZ() == insideVertex.get(2).getZ()) {
previous = insideVertex.get(0);
} else {
previous = insideVertex.get(1);
}
}
td = new TraverseData(BlockVector2.at(v.getX() + (v.getX() - previous.getX()), v.getZ() + (v.getZ() - previous.getZ())), v, false);
cont = true;
break;
case // convex vertex
2:
// possibly also 1 block wide segment with 2 edges opposite, but we'll ignore that
// run consumer
run.accept(new TraverseReturn(v, true, pointID, exposedEdges));
if (insideVertex.get(0).equals(previous)) {
td = new TraverseData(insideVertex.get(1), v, false);
cont = true;
} else {
td = new TraverseData(insideVertex.get(0), v, false);
cont = true;
}
break;
case // random 1x1 jutting out
3:
// it's fine right now but it'd be nice if it worked
break;
case // concave vertex, or point in middle of region
0:
List<Vector2> cornersNotIn = new ArrayList<>();
for (Vector2 dir : CORNER_DIRECTIONS) {
BlockVector2 test = BlockVector2.at(v.getX() + dir.getX(), v.getZ() + dir.getZ());
if (!isInRegion(test, regions))
cornersNotIn.add(dir);
}
if (cornersNotIn.size() == 1) {
// concave vertex
// run consumer
run.accept(new TraverseReturn(v, true, pointID, exposedEdges));
Vector2 dir = cornersNotIn.get(0);
if (previous == null || previous.equals(BlockVector2.at(v.getX() + dir.getX(), v.getZ()))) {
td = new TraverseData(BlockVector2.at(v.getX(), v.getZ() + dir.getZ()), v, false);
cont = true;
} else {
td = new TraverseData(BlockVector2.at(v.getX() + dir.getX(), v.getZ()), v, false);
cont = true;
}
} else if (cornersNotIn.size() == 2) {
// 1 block diagonal perfect overlap
// run consumer
run.accept(new TraverseReturn(v, false, pointID, exposedEdges));
if (previous == null)
previous = insideVertex.get(0);
td = new TraverseData(BlockVector2.at(v.getX() + (v.getX() - previous.getX()), v.getZ() + (v.getZ() - previous.getZ())), v, false);
cont = true;
}
// ignore if in middle of region (cornersNotIn size = 0)
break;
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ algorithm ends ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pointID++;
}
}
use of com.sk89q.worldedit.math.BlockVector2 in project ProtectionStones by espidev.
the class WGMerge method mergeRegions.
// returns a merged region; root and merge must be overlapping or adjacent
// merge parameter must all be decomposed regions (down to cuboids, no polygon)
private static ProtectedRegion mergeRegions(String newID, PSRegion root, List<PSRegion> merge) throws RegionHoleException {
HashSet<BlockVector2> points = new HashSet<>();
List<ProtectedRegion> regions = new ArrayList<>();
// decompose regions down to their points
for (PSRegion r : merge) {
points.addAll(WGUtils.getPointsFromDecomposedRegion(r));
regions.add(r.getWGRegion());
}
// points of new region
List<BlockVector2> vertex = new ArrayList<>();
HashMap<Integer, ArrayList<BlockVector2>> vertexGroups = new HashMap<>();
// traverse region edges for vertex
RegionTraverse.traverseRegionEdge(points, regions, tr -> {
if (tr.isVertex) {
if (vertexGroups.containsKey(tr.vertexGroupID)) {
vertexGroups.get(tr.vertexGroupID).add(tr.point);
} else {
vertexGroups.put(tr.vertexGroupID, new ArrayList<>(Arrays.asList(tr.point)));
}
}
});
// prevent holes from being formed
if (vertexGroups.size() > 1 && !ProtectionStones.getInstance().getConfigOptions().allowMergingHoles) {
throw new RegionHoleException();
}
// assemble vertex group
// draw in and out lines between holes
boolean first = true;
BlockVector2 backPoint = null;
for (List<BlockVector2> l : vertexGroups.values()) {
if (first) {
first = false;
vertex.addAll(l);
backPoint = l.get(0);
} else {
vertex.addAll(l);
vertex.add(l.get(0));
}
vertex.add(backPoint);
}
// merge sets of region name flag
Set<String> regionNames = new HashSet<>(), regionLines = new HashSet<>();
for (PSRegion r : merge) {
if (r.getWGRegion().getFlag(FlagHandler.PS_MERGED_REGIONS) != null) {
regionNames.addAll(r.getWGRegion().getFlag(FlagHandler.PS_MERGED_REGIONS));
regionLines.addAll(r.getWGRegion().getFlag(FlagHandler.PS_MERGED_REGIONS_TYPES));
} else {
regionNames.add(r.getId());
regionLines.add(r.getId() + " " + r.getType());
}
}
// create new merged region
ProtectedRegion r = new ProtectedPolygonalRegion(newID, vertex, WGUtils.MIN_BUILD_HEIGHT, WGUtils.MAX_BUILD_HEIGHT);
r.copyFrom(root.getWGRegion());
// only make it a merged region if there is more than one contained region
if (regionNames.size() > 1 && regionLines.size() > 1) {
r.setFlag(FlagHandler.PS_MERGED_REGIONS, regionNames);
r.setFlag(FlagHandler.PS_MERGED_REGIONS_TYPES, regionLines);
} else {
r.setFlag(FlagHandler.PS_MERGED_REGIONS, null);
r.setFlag(FlagHandler.PS_MERGED_REGIONS_TYPES, null);
}
return r;
}
use of com.sk89q.worldedit.math.BlockVector2 in project FastAsyncWorldEdit by IntellectualSites.
the class Polygonal2DRegion method toString.
/**
* Returns string representation in the format
* "(x1, z1) - ... - (xN, zN) * (minY - maxY)"
*
* @return string
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
List<BlockVector2> pts = getPoints();
Iterator<BlockVector2> it = pts.iterator();
while (it.hasNext()) {
BlockVector2 current = it.next();
sb.append("(").append(current.getBlockX()).append(", ").append(current.getBlockZ()).append(")");
if (it.hasNext()) {
sb.append(" - ");
}
}
sb.append(" * (").append(minY).append(" - ").append(maxY).append(")");
return sb.toString();
}
use of com.sk89q.worldedit.math.BlockVector2 in project FastAsyncWorldEdit by IntellectualSites.
the class Polygonal2DRegion method shift.
@Override
public void shift(BlockVector3 change) throws RegionOperationException {
final double changeX = change.getX();
final double changeY = change.getY();
final double changeZ = change.getZ();
for (int i = 0; i < points.size(); ++i) {
BlockVector2 point = points.get(i);
points.set(i, BlockVector2.at(point.getX() + changeX, point.getZ() + changeZ));
}
minY += changeY;
maxY += changeY;
recalculate();
}
use of com.sk89q.worldedit.math.BlockVector2 in project FastAsyncWorldEdit by IntellectualSites.
the class Polygonal2DRegion method recalculate.
/**
* Recalculate the bounding box of this polygonal region. This should be
* called after points have been changed.
*/
protected void recalculate() {
if (points.isEmpty()) {
min = BlockVector2.ZERO;
minY = 0;
max = BlockVector2.ZERO;
maxY = 0;
return;
}
int minX = points.get(0).getBlockX();
int minZ = points.get(0).getBlockZ();
int maxX = points.get(0).getBlockX();
int maxZ = points.get(0).getBlockZ();
for (BlockVector2 v : points) {
int x = v.getBlockX();
int z = v.getBlockZ();
if (x < minX) {
minX = x;
}
if (z < minZ) {
minZ = z;
}
if (x > maxX) {
maxX = x;
}
if (z > maxZ) {
maxZ = z;
}
}
int oldMinY = minY;
int oldMaxY = maxY;
minY = Math.min(oldMinY, oldMaxY);
maxY = Math.max(oldMinY, oldMaxY);
minY = Math.min(Math.max(getWorldMinY(), minY), getWorldMaxY());
maxY = Math.min(Math.max(getWorldMinY(), maxY), getWorldMaxY());
min = BlockVector2.at(minX, minZ);
max = BlockVector2.at(maxX, maxZ);
}
Aggregations