/*
* 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.client.ui.zone;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Set;
import com.t3.GeometryUtil;
import com.t3.GeometryUtil.PointNode;
public class AreaMeta {
Area area;
Point2D centerPoint;
Set<AreaFace> faceSet;
// Only used during construction
boolean isHole;
PointNode pointNodeList;
GeneralPath path;
PointNode lastPointNode;
public AreaMeta() {
}
public Point2D getCenterPoint() {
if (centerPoint == null) {
centerPoint = new Point2D.Double(area.getBounds().x + area.getBounds().width / 2, area.getBounds().y + area.getBounds().height / 2);
}
return centerPoint;
}
public Set<AreaFace> getFrontFaces(Point2D origin) {
Set<AreaFace> faces = new HashSet<AreaFace>();
for (AreaFace face : faceSet) {
double originAngle = GeometryUtil.getAngle(origin, face.getMidPoint());
double delta = GeometryUtil.getAngleDelta(originAngle, face.getFacing());
if (Math.abs(delta) < 90) {
continue;
}
faces.add(face);
}
return faces;
}
public Area getArea() {
return new Area(area);
}
public boolean isHole() {
return isHole;
}
public void addPoint(float x, float y) {
PointNode pointNode = new PointNode(new Point2D.Double(x, y));
// Don't add if we haven't moved
if (lastPointNode != null) {
if (lastPointNode.point.equals(pointNode.point)) {
return;
}
}
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;
}
computeIsHole();
computeFaces();
// Don't need point list anymore
pointNodeList = null;
path = null;
}
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 computeFaces() {
faceSet = new HashSet<AreaFace>();
PointNode node = pointNodeList;
do {
faceSet.add(new AreaFace(node.point, node.previous.point));
node = node.next;
} while (!node.point.equals(pointNodeList.point));
}
}