/**
Ported by David Turner from Visilibity, by Karl J. Obermeyer
This port undoubtedly introduced a number of bugs (and removed some features).
Bug reports should be directed to the OpenTripPlanner project, unless they
can be reproduced in the original VisiLibity.
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.opentripplanner.visibility;
import java.util.ArrayList;
import java.util.List;
public class VLPolygon {
public ArrayList<VLPoint> vertices;
public VLPolygon() {
vertices = new ArrayList<VLPoint>();
}
double boundary_distance(VLPoint point_temp) {
return point_temp.boundary_distance(this);
}
double boundary_distance(LineSegment line_segment) {
return line_segment.boundary_distance(this);
}
public VLPolygon(List<VLPoint> vertices_temp) {
vertices = new ArrayList<VLPoint>(vertices_temp);
}
public int n() {
return vertices.size();
}
public VLPolygon(VLPoint point0, VLPoint point1, VLPoint point2) {
vertices = new ArrayList<VLPoint>();
vertices.add(point0);
vertices.add(point1);
vertices.add(point2);
}
public int r() {
int r_count = 0;
if (vertices.size() > 1) {
// Use cross product to count right turns.
for (int i = 0; i <= n() - 1; i++)
if ((get(i + 1).x - get(i).x) * (get(i + 2).y - get(i).y)
- (get(i + 1).y - get(i).y) * (get(i + 2).x - get(i).x) < 0)
r_count++;
if (area() < 0) {
r_count = n() - r_count;
}
}
return r_count;
}
public boolean is_simple(double epsilon) {
if (n() == 0 || n() == 1 || n() == 2)
return false;
// Make sure adjacent edges only intersect at a single point.
for (int i = 0; i <= n() - 1; i++)
if (new LineSegment(get(i), get(i + 1)).intersection(
new LineSegment(get(i + 1), get(i + 2)), epsilon).size() > 1)
return false;
// Make sure nonadjacent edges do not intersect.
for (int i = 0; i < n() - 2; i++)
for (int j = i + 2; j <= n() - 1; j++)
if (0 != (j + 1) % vertices.size()
&& new LineSegment(get(i), get(i + 1)).distance(new LineSegment(get(j),
get(j + 1))) <= epsilon)
return false;
return true;
}
public boolean is_in_standard_form() {
if (vertices.size() > 1) // if more than one point in the polygon.
for (int i = 1; i < vertices.size(); i++)
if (vertices.get(0).compareTo(vertices.get(i)) > 0)
return false;
return true;
}
public double boundary_length() {
double length_temp = 0;
if (n() == 0 || n() == 1)
return 0;
for (int i = 0; i < n() - 1; i++)
length_temp += vertices.get(i).distance(vertices.get(i + 1));
length_temp += vertices.get(n() - 1).distance(vertices.get(0));
return length_temp;
}
public double area() {
double area_temp = 0;
if (n() == 0)
return 0;
for (int i = 0; i <= n() - 1; i++)
area_temp += get(i).x * get(i + 1).y - get(i + 1).x * get(i).y;
return area_temp / 2.0;
}
public VLPoint centroid() {
assert (vertices.size() > 0);
double area_temp = area();
assert (area_temp != 0);
double x_temp = 0;
for (int i = 0; i <= n() - 1; i++)
x_temp += (get(i).x + get(i + 1).x)
* (get(i).x * get(i + 1).y - get(i + 1).x * get(i).y);
double y_temp = 0;
for (int i = 0; i <= n() - 1; i++)
y_temp += (get(i).y + get(i + 1).y)
* (get(i).x * get(i + 1).y - get(i + 1).x * get(i).y);
return new VLPoint(x_temp / (6 * area_temp), y_temp / (6 * area_temp));
}
public double diameter() {
// Precondition: nonempty Polygon.
assert (n() > 0);
double running_max = 0;
for (int i = 0; i < n() - 1; i++) {
for (int j = i + 1; j < n(); j++) {
if (get(i).distance(get(j)) > running_max)
running_max = get(i).distance(get(j));
}
}
return running_max;
}
public BoundingBox bbox() {
// Precondition: nonempty Polygon.
assert (vertices.size() > 0);
BoundingBox bounding_box = new BoundingBox();
double x_min = vertices.get(0).x, x_max = vertices.get(0).x, y_min = vertices.get(0).y, y_max = vertices
.get(0).y;
for (int i = 1; i < vertices.size(); i++) {
if (x_min > vertices.get(i).x) {
x_min = vertices.get(i).x;
}
if (x_max < vertices.get(i).x) {
x_max = vertices.get(i).x;
}
if (y_min > vertices.get(i).y) {
y_min = vertices.get(i).y;
}
if (y_max < vertices.get(i).y) {
y_max = vertices.get(i).y;
}
}
bounding_box.x_min = x_min;
bounding_box.x_max = x_max;
bounding_box.y_min = y_min;
bounding_box.y_max = y_max;
return bounding_box;
}
ArrayList<VLPoint> random_points(int count, double epsilon) {
// Precondition: nonempty Polygon.
assert (vertices.size() > 0);
BoundingBox bounding_box = bbox();
ArrayList<VLPoint> pts_in_polygon = new ArrayList<VLPoint>(count);
VLPoint pt_temp = new VLPoint(
Util.uniform_random_sample(bounding_box.x_min, bounding_box.x_max),
Util.uniform_random_sample(bounding_box.y_min, bounding_box.y_max));
while (pts_in_polygon.size() < count) {
while (!pt_temp.in(this, epsilon)) {
pt_temp.set_x(Util.uniform_random_sample(bounding_box.x_min, bounding_box.x_max));
pt_temp.set_y(Util.uniform_random_sample(bounding_box.y_min, bounding_box.y_max));
}
pts_in_polygon.add(pt_temp);
pt_temp.set_x(Util.uniform_random_sample(bounding_box.x_min, bounding_box.x_max));
pt_temp.set_y(Util.uniform_random_sample(bounding_box.y_min, bounding_box.y_max));
}
return pts_in_polygon;
}
public void enforce_standard_form() {
int point_count = vertices.size();
if (point_count > 1) { // if more than one point in the polygon.
ArrayList<VLPoint> vertices_temp = new ArrayList<VLPoint>(point_count);
// Find index of lexicographically smallest point.
int index_of_smallest = 0;
int i; // counter.
for (i = 1; i < point_count; i++)
if (vertices.get(i).compareTo(vertices.get(index_of_smallest)) < 0)
index_of_smallest = i;
// Fill vertices_temp starting with lex. smallest.
for (i = index_of_smallest; i < point_count; i++)
vertices_temp.add(vertices.get(i));
for (i = 0; i < index_of_smallest; i++)
vertices_temp.add(vertices.get(i));
vertices = vertices_temp;
}
}
public void eliminate_redundant_vertices(double epsilon) {
// Degenerate case.
if (vertices.size() < 4)
return;
// Store new minimal length list of vertices.
ArrayList<VLPoint> vertices_temp = new ArrayList<VLPoint>(vertices.size());
// Place holders.
int first = 0;
int second = 1;
int third = 2;
while (third <= vertices.size()) {
// if second is redundant
if (new LineSegment(get(first), get(third)).distance(get(second)) <= epsilon) {
// =>skip it
second = third;
third++;
}
// else second not redundant
else {
// =>add it
vertices_temp.add(get(second));
first = second;
second = third;
third++;
}
}
// decide whether to add original first point
if (new LineSegment(vertices_temp.get(0), vertices_temp.get(vertices_temp.size() - 1))
.distance(vertices.get(0)) > epsilon)
vertices_temp.add(vertices.get(0));
// Update list of vertices.
vertices = vertices_temp;
}
public void reverse() {
int n = n();
if (n > 2) {
// reverse, leaving the first point in its place
for (int i = 1; i < (n+1) / 2; ++i) {
VLPoint temp = vertices.get(i);
vertices.set(i, vertices.get((n - i)));
vertices.set((n - i), temp);
}
}
}
public boolean equals(Object o) {
if (!(o instanceof VLPolygon)) {
return false;
}
VLPolygon polygon2 = (VLPolygon) o;
if (n() != polygon2.n() || n() == 0 || polygon2.n() == 0)
return false;
for (int i = 0; i < n(); i++)
if (!get(i).equals(polygon2.get(i)))
return false;
return true;
}
public int hashCode() {
return vertices.hashCode() + 1;
}
public VLPoint get(int i) {
return vertices.get(i % vertices.size());
}
boolean equivalent(VLPolygon polygon2, double epsilon) {
if (n() == 0 || polygon2.n() == 0)
return false;
if (n() != polygon2.n())
return false;
// Try all cyclic matches
int n = n();// =polygon2.n()
for (int offset = 0; offset < n; offset++) {
boolean successful_match = true;
for (int i = 0; i < n; i++) {
if (get(i).distance(polygon2.get(i + offset)) > epsilon) {
successful_match = false;
break;
}
}
if (successful_match)
return true;
}
return false;
}
double boundary_distance(VLPolygon polygon2) {
assert (n() > 0 && polygon2.n() > 0);
// Handle single point degeneracy.
if (n() == 1)
return get(0).boundary_distance(polygon2);
else if (polygon2.n() == 1)
return polygon2.get(0).boundary_distance(this);
// Handle cases where each polygon has at least 2 points.
// Initialize to an upper bound.
double running_min = get(0).boundary_distance(polygon2);
double distance_temp;
// Loop over all possible pairs of line segments.
for (int i = 0; i <= n() - 1; i++) {
for (int j = 0; j <= polygon2.n() - 1; j++) {
distance_temp = new LineSegment(get(i), get(i + 1)).distance(new LineSegment(
polygon2.get(j), polygon2.get(j + 1)));
if (distance_temp < running_min)
running_min = distance_temp;
}
}
return running_min;
}
public String toString() {
String outs = "";
for (int i = 0; i < n(); i++)
outs += get(i) + "\n";
return outs;
}
public boolean hasPointInside(VLPolygon container) {
for (VLPoint point : vertices) {
if (point.in(container)) {
return true;
}
}
return false;
}
}