/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3;
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(Math.atan2((origin.getY() - target.getY()),(target.getX() - origin.getX())));
if (angle < 0) {
angle += 360;
}
return angle;
}
public static double getAngleDelta(double sourceAngle, double targetAngle) {
// Normalize
targetAngle -= sourceAngle;
if (targetAngle > 180) {
targetAngle -= 360;
}
if (targetAngle < -180) {
targetAngle += 360;
}
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 getDistance(Point2D p1, Point2D p2) {
double a = p2.getX() - p1.getX();
double b = p2.getY() - p1.getY();
return Math.abs(Math.sqrt(a+b));
}
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);
}
}