/*
* Copyright 2016 Nathan Howard
*
* This file is part of OpenGrave
*
* OpenGrave 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.
*
* OpenGrave 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 OpenGrave. If not, see <http://www.gnu.org/licenses/>.
*/
package com.opengrave.common.pathing;
import java.util.ArrayList;
import java.util.Collections;
public class Polygon {
protected ArrayList<Point> points = new ArrayList<Point>();
private ArrayList<Line> lines = new ArrayList<Line>();
private Point center = null;
double maxX = Double.NaN, minX = Double.NaN, maxY = Double.NaN, minY = Double.NaN;
public Polygon() {
}
public Polygon(ArrayList<Point> points) {
this.points = points;
checkWind();
makeBoundingBox();
makeLines();
setCenter();
}
private void checkWind() {
int i = 0;
for (int j = 0; j < points.size(); j++) {
int n = j + 1;
if (n == points.size()) {
n = 0;
}
i += (points.get(j).x * points.get(n).y - points.get(n).x * points.get(j).y);
}
if (i > 0) {
Collections.reverse(points);
}
}
private void setCenter() {
center = new Point(0, 0, points.get(0).z);
for (Point point : getPoints()) {
center.add(point);
}
center.divide(getPoints().size());
}
public void makeBoundingBox() {
maxX = Double.NaN;
maxY = Double.NaN;
minX = Double.NaN;
minY = Double.NaN;
for (Point point : points) {
if (Double.isNaN(minX) || point.x < minX) {
minX = point.x;
}
if (Double.isNaN(maxX) || point.x > maxX) {
maxX = point.x;
}
if (Double.isNaN(minY) || point.y < minY) {
minY = point.y;
}
if (Double.isNaN(maxY) || point.y > maxY) {
maxY = point.y;
}
}
}
/*
* public void appendPoint(PathingPoint point) { points.add(point); if
* (points.size() > 2 && !checkSimple()) { points.remove(points.size() - 1);
* // Remove the last point if it // makes it non-simple }
* makeBoundingBox(); makeLines(); }
*/
/**
* Check if the polygon is simple and convex
*
* @return
*/
public boolean checkSimple() {
if (points.size() < 4) {
return true;
}
boolean sign = false;
int n = points.size();
for (int i = 0; i < n; i++) {
Point p1 = points.get((i + 2) % n);
Point p2 = points.get((i + 1) % n);
Point p3 = points.get(i);
double dx1 = p1.x - p2.x;
double dy1 = p1.y - p2.y;
double dx2 = p3.x - p2.x;
double dy2 = p3.y - p2.y;
double zcross = dx1 * dy2 - dy1 * dx2;
if (i == 0) {
sign = zcross > 0;
} else {
if (sign != (zcross > 0)) {
return false;
}
}
}
return true;
}
public boolean isPointInside(Point l) {
if (points.size() == 0) {
return false;
}
if (l.x < minX || l.x > maxX || l.y < minY || l.y > maxY) {
return false;
} // Exclude outside boundraries. Quickest exclusion
if (points.contains(l)) {
return true;
} // Include vertices. Quickest inclusion
for (Line line : lines) {
if (line.isPointOnLine(l, 0.001)) {
return true;
} // Include edges. Also returns true if it's within a tolerance of 0.001 of edge
}
int i = 0, j = points.size() - 1; // So it's not a dead give-away. Raytrace a line to find out
boolean c = false;
for (i = 0; i < points.size(); j = i++) {
Point point1 = points.get(i);
Point point2 = points.get(j);
if (((point1.y > l.y) != (point2.y > l.y)) && (l.x < (point2.x - point1.x) * (l.y - point1.y) / (point2.y - point1.y) + point1.x)) {
c = !c;
}
}
return c;
}
public void makeLines() {
lines.clear();
for (int i = 0; i < points.size(); i++) {
int i2 = i - 1;
if (i2 < 0) {
i2 = points.size() - 1;
}
lines.add(new Line(points.get(i), points.get(i2), true));
}
}
public ArrayList<Point> getPoints() {
ArrayList<Point> list = new ArrayList<Point>();
for (Point i : points) {
list.add(i);
}
return list;
}
public ArrayList<Line> getLines() {
return lines;
}
public Point getCenter() {
return center;
}
public boolean hasLine(Line line) {
return lines.contains(line);
}
}