Skip to content

Commit

Permalink
Merge branch 'develop' into resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
Phergus authored Jul 9, 2022
2 parents e2e899e + 1a4ebf1 commit d20dd45
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 228 deletions.
157 changes: 0 additions & 157 deletions src/main/java/net/rptools/lib/GeometryUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,9 @@
*/
package net.rptools.lib;

import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class GeometryUtil {
public static Line2D findClosestLine(Point2D origin, PointNode pointList) {
Line2D line = null;
double distance = 0;

PointNode node = pointList;
do {
Line2D newLine = new Line2D.Double(node.previous.point, node.point);
double newDistance = getDistanceToCenter(origin, newLine);
if (line == null || newDistance < distance) {
line = newLine;
distance = newDistance;
}
node = node.next;
} while (node != pointList);

return line;
}

public static double getDistanceToCenter(Point2D p, Line2D line) {
Point2D midPoint =
new Point2D.Double(
(line.getP1().getX() + line.getP2().getX()) / 2,
(line.getP1().getY() + line.getP2().getY()) / 2);

return Math.hypot(midPoint.getX() - p.getX(), midPoint.getY() - p.getY());
}

public static Point2D getCloserPoint(Point2D origin, Line2D line) {
double dist1 =
Math.hypot(origin.getX() - line.getP1().getX(), origin.getY() - line.getP1().getY());
double dist2 =
Math.hypot(origin.getX() - line.getP2().getX(), origin.getY() - line.getP2().getY());

return dist1 < dist2 ? line.getP1() : line.getP2();
}

public static double getAngle(Point2D origin, Point2D target) {
double angle =
Math.toDegrees(
Expand All @@ -82,118 +39,4 @@ public static double getAngleDelta(double sourceAngle, double targetAngle) {
}
return targetAngle;
}

public static class PointNode {
public PointNode previous;
public PointNode next;
public Point2D point;

public PointNode(Point2D point) {
this.point = point;
}
}

public static double getDistanceXXX(Point2D p1, Point2D p2) {
double a = p2.getX() - p1.getX();
double b = p2.getY() - p1.getY();
return Math.sqrt(
a * a + b * b); // Was just "a+b" -- was that on purpose? A shortcut speed-up perhaps?
}

public static Set<Line2D> getFrontFaces(PointNode nodeList, Point2D origin) {
Set<Line2D> frontFaces = new HashSet<Line2D>();

Line2D closestLine = GeometryUtil.findClosestLine(origin, nodeList);
Point2D closestPoint = GeometryUtil.getCloserPoint(origin, closestLine);
PointNode closestNode = nodeList;
do {
if (closestNode.point.equals(closestPoint)) {
break;
}
closestNode = closestNode.next;
} while (closestNode != nodeList);

Point2D secondPoint =
closestLine.getP1().equals(closestPoint) ? closestLine.getP2() : closestLine.getP1();
Point2D thirdPoint =
secondPoint.equals(closestNode.next.point)
? closestNode.previous.point
: closestNode.next.point;

// Determine whether the first line segment is visible
Line2D l1 = new Line2D.Double(origin, secondPoint);
Line2D l2 = new Line2D.Double(closestNode.point, thirdPoint);
boolean frontFace = !(l1.intersectsLine(l2));
if (frontFace) {
frontFaces.add(new Line2D.Double(closestPoint, secondPoint));
}
Point2D startPoint =
closestNode.previous.point.equals(secondPoint) ? secondPoint : closestNode.point;
Point2D endPoint = closestNode.point.equals(startPoint) ? secondPoint : closestNode.point;
double originAngle = GeometryUtil.getAngle(origin, startPoint);
double pointAngle = GeometryUtil.getAngle(startPoint, endPoint);
int lastDirection = GeometryUtil.getAngleDelta(originAngle, pointAngle) > 0 ? 1 : -1;

// System.out.format("%s: %.2f %s, %.2f %s => %.2f : %d : %s\n", frontFace, originAngle,
// startPoint.toString(), pointAngle, endPoint.toString(), getAngleDelta(originAngle,
// pointAngle),
// lastDirection, (closestNode.previous.point.equals(secondPoint) ? "second" :
// "closest").toString());
PointNode node = secondPoint.equals(closestNode.next.point) ? closestNode.next : closestNode;
do {
Point2D point = node.point;
Point2D nextPoint = node.next.point;

originAngle = GeometryUtil.getAngle(origin, point);
pointAngle = GeometryUtil.getAngle(origin, nextPoint);

// System.out.println(point + ":" + originAngle + ", " + nextPoint + ":"+ pointAngle + ", " +
// getAngleDelta(originAngle, pointAngle));
if (GeometryUtil.getAngleDelta(originAngle, pointAngle) > 0) {
if (lastDirection < 0) {
frontFace = !frontFace;
lastDirection = 1;
}
} else {
if (lastDirection > 0) {
frontFace = !frontFace;
lastDirection = -1;
}
}
if (frontFace) {
frontFaces.add(new Line2D.Double(nextPoint, point));
}
node = node.next;
} while (!node.point.equals(closestPoint));

return frontFaces;
}

public static int countAreaPoints(Area area) {
int count = 0;
for (PathIterator iter = area.getPathIterator(null); !iter.isDone(); iter.next()) {
count++;
}
return count;
}

public static Area createLine(List<Point2D> points, int width) {
Point2D lastPoint = null;
Line2D lastLine = null;
for (Point2D point : points) {
if (lastPoint == null) {
lastPoint = point;
continue;
}
if (lastLine == null) {
// First line segment
lastLine = new Line2D.Double(lastPoint, point);

// Keep track
continue;
}
}
GeneralPath path = new GeneralPath();
return new Area(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void setParentOcean(AreaOcean parentOcean) {

@Override
public @Nullable AreaContainer getDeepestContainerAt(Point2D point) {
if (!meta.area.contains(point)) {
if (!meta.contains(point)) {
// Point not contained within this island, so nothing to return.
return null;
}
Expand All @@ -83,7 +83,7 @@ public void addOcean(AreaOcean ocean) {
////
// AREA CONTAINER
public Area getBounds() {
return meta.area;
return meta.getBounds();
}

@Override
Expand Down
98 changes: 32 additions & 66 deletions src/main/java/net/rptools/maptool/client/ui/zone/vbl/AreaMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import net.rptools.lib.GeometryUtil;
import net.rptools.lib.GeometryUtil.PointNode;
import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
Expand All @@ -30,17 +28,23 @@

/** Represents the boundary of a piece of topology. */
public class AreaMeta {
Area area;
List<Coordinate> vertices = new ArrayList<>();
private Area area;
private List<Coordinate> vertices = new ArrayList<>();

// Only used during construction
boolean isHole;
PointNode pointNodeList;
GeneralPath path;
PointNode lastPointNode;
private boolean isHole;
private GeneralPath path;

public AreaMeta() {}

public boolean contains(Point2D point) {
return area.contains(point);
}

public Area getBounds() {
return new Area(area);
}

/**
* @param origin
* @param faceAway If `true`, only return segments facing away from origin.
Expand Down Expand Up @@ -95,15 +99,11 @@ public List<LineString> getFacingSegments(
return segments;
}

private static Coordinate toCoordinate(Point2D point2D) {
return new Coordinate(point2D.getX(), point2D.getY());
}

public boolean isHole() {
return isHole;
}

public void addPoint(float x, float y) {
public void addPoint(double x, double y) {
// Cut out redundant points
// TODO: This works ... in concept, but in practice it can create holes that pop outside of
// their parent bounds
Expand All @@ -115,75 +115,41 @@ public void addPoint(float x, float y) {
// skippedPoints++;
// return;
// }
PointNode pointNode = new PointNode(new Point2D.Double(x, y));

// Don't add if we haven't moved
if (lastPointNode != null && lastPointNode.point.equals(pointNode.point)) {
return;
final var vertex = new Coordinate(x, y);
if (!vertices.isEmpty()) {
final var lastVertex = vertices.get(vertices.size() - 1);
// Don't add if we haven't moved
if (lastVertex.equals(vertex)) {
return;
}
}
vertices.add(vertex);

if (path == null) {
path = new GeneralPath();
path.moveTo(x, y);

pointNodeList = pointNode;
} else {
path.lineTo(x, y);

lastPointNode.next = pointNode;
pointNode.previous = lastPointNode;
}
lastPointNode = pointNode;
}

public void close() {
area = new Area(path);

// Close the circle
lastPointNode.next = pointNodeList;
pointNodeList.previous = lastPointNode;
lastPointNode = null;

// For some odd reason, sometimes the first and last point are the same, which causes
// bugs in the way areas are calculated
if (pointNodeList.point.equals(pointNodeList.previous.point)) {
// Pull out the dupe node
PointNode trueLastPoint = pointNodeList.previous.previous;
trueLastPoint.next = pointNodeList;
pointNodeList.previous = trueLastPoint;
// Close the circle.
// For some odd reason, sometimes the first and last point are already the same, so don't add
// the point again in that case.
final var first = vertices.get(0);
final var last = vertices.get(vertices.size() - 1);
if (!first.equals(last)) {
vertices.add(first);
}
computeIsHole();
computeVertices();

// Don't need point list anymore
pointNodeList = null;
isHole = Orientation.isCCW(vertices.toArray(Coordinate[]::new));

// Don't need this anymore
path = null;
// System.out.println("AreaMeta.skippedPoints: " + skippedPoints + " h:" + isHole + " f:" +
// faceList.size());
}

private void computeIsHole() {
double angle = 0;

PointNode currNode = pointNodeList.next;

while (currNode != pointNodeList) {
double currAngle =
GeometryUtil.getAngleDelta(
GeometryUtil.getAngle(currNode.previous.point, currNode.point),
GeometryUtil.getAngle(currNode.point, currNode.next.point));

angle += currAngle;
currNode = currNode.next;
}
isHole = angle < 0;
}

private void computeVertices() {
PointNode node = pointNodeList;
vertices.add(toCoordinate(node.point));
do {
vertices.add(toCoordinate(node.next.point));
node = node.next;
} while (!node.point.equals(pointNodeList.point));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public List<LineString> getVisionBlockingBoundarySegments(

@Override
public @Nullable AreaContainer getDeepestContainerAt(Point2D point) {
if (meta != null && !meta.area.contains(point)) {
if (meta != null && !meta.contains(point)) {
// Point not contained within this ocean, so nothing to return.
return null;
}
Expand All @@ -97,6 +97,6 @@ public void addIsland(AreaIsland island) {

@Override
public Area getBounds() {
return meta != null ? meta.area : null;
return meta != null ? meta.getBounds() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private void digest(Area area) {
List<AreaIsland> islandList = new ArrayList<AreaIsland>();

// Break the big area into independent areas
float[] coords = new float[6];
double[] coords = new double[6];
AreaMeta areaMeta = new AreaMeta();
for (PathIterator iter = area.getPathIterator(null); !iter.isDone(); iter.next()) {
int type = iter.currentSegment(coords);
Expand Down

0 comments on commit d20dd45

Please sign in to comment.