/**
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;
/**
* \brief environment represented by simple polygonal outer boundary with simple polygonal holes
*
* \remarks For methods to work correctly, the outer boundary vertices must be listed ccw and the
* hole vertices cw
*/
public class Environment {
VLPolygon outer_boundary;
ArrayList<VLPolygon> holes = new ArrayList<VLPolygon>();
ArrayList<pair<Integer, Integer>> flattened_index_key = new ArrayList<pair<Integer, Integer>>();
public Environment(List<VLPolygon> polygons) {
outer_boundary = polygons.get(0);
for (int i = 1; i < polygons.size(); i++)
holes.add(polygons.get(i));
update_flattened_index_key();
}
public Environment(VLPolygon polygon_temp) {
outer_boundary = polygon_temp;
update_flattened_index_key();
}
VLPoint kth_point(int k) {
pair<Integer, Integer> ij = flattened_index_key.get(k);
return get(ij.first()).get(ij.second());
}
int n() {
int n_count = 0;
n_count = outer_boundary.n();
for (int i = 0; i < h(); i++)
n_count += holes.get(i).n();
return n_count;
}
int r() {
int r_count = 0;
r_count = outer_boundary.r();
for (int i = 0; i < h(); i++) {
VLPolygon polygon_temp = holes.get(i);
r_count += polygon_temp.n() - polygon_temp.r();
}
return r_count;
}
int h() {
return holes.size();
}
boolean is_in_standard_form() {
if (outer_boundary.is_in_standard_form() == false || outer_boundary.area() < 0)
return false;
for (int i = 0; i < holes.size(); i++)
if (holes.get(i).is_in_standard_form() == false || holes.get(i).area() > 0)
return false;
return true;
}
public boolean is_valid(double epsilon) {
if (n() <= 2)
return false;
// Check all Polygons are simple.
if (!outer_boundary.is_simple(epsilon)) {
/*
* std::cerr << std::endl << "\x1b[31m" << "The outer boundary is not simple." <<
* "\x1b[0m" << std::endl;
*/
return false;
}
for (int i = 0; i < h(); i++)
if (!holes.get(i).is_simple(epsilon)) {
/*
* std::cerr << std::endl << "\x1b[31m" << "Hole " << i << " is not simple." <<
* "\x1b[0m" << std::endl;
*/
return false;
}
// Check none of the Polygons' boundaries intersect w/in epsilon.
for (int i = 0; i < h(); i++)
if (outer_boundary.boundary_distance(holes.get(i)) <= epsilon) {
/*
* std::cerr << std::endl << "\x1b[31m" <<
* "The outer boundary intersects the boundary of hole " << i << "." << "\x1b[0m" <<
* std::endl;
*/
return false;
}
for (int i = 0; i < h(); i++)
for (int j = i + 1; j < h(); j++)
if (holes.get(i).boundary_distance(holes.get(j)) <= epsilon) {
/*
* std::cerr << std::endl << "\x1b[31m" << "The boundary of hole " << i <<
* " intersects the boundary of hole " << j << "." << "\x1b[0m" << std::endl;
*/
return false;
}
// Check that the vertices of each hole are in the outside_boundary
// and not in any other holes.
// Loop over holes.
for (int i = 0; i < h(); i++) {
// Loop over vertices of a hole
for (int j = 0; j < holes.get(i).n(); j++) {
if (!holes.get(i).get(j).in(outer_boundary, epsilon)) {
/*
* std::cerr << std::endl << "\x1b[31m" << "Vertex " << j << " of hole " << i <<
* " is not within the outer boundary." << "\x1b[0m" << std::endl;
*/
return false;
}
// Second loop over holes.
for (int k = 0; k < h(); k++)
if (i != k && holes.get(i).get(j).in(holes.get(k), epsilon)) {
/*
* std::cerr << std::endl << "\x1b[31m" << "Vertex " << j << " of hole " <<
* i << " is in hole " << k << "." << "\x1b[0m" << std::endl;
*/
return false;
}
}
}
// Check outer_boundary is ccw and holes are cw.
if (outer_boundary.area() <= 0) {
/*
* std::cerr << std::endl << "\x1b[31m" <<
* "The outer boundary vertices are not listed ccw." << "\x1b[0m" << std::endl;
*/
return false;
}
for (int i = 0; i < h(); i++)
if (holes.get(i).area() >= 0) {
/*
* std::cerr << std::endl << "\x1b[31m" << "The vertices of hole " << i <<
* " are not listed cw." << "\x1b[0m" << std::endl;
*/
return false;
}
return true;
}
double boundary_length() {
// Precondition: nonempty Environment.
assert (outer_boundary.n() > 0);
double length_temp = outer_boundary.boundary_length();
for (int i = 0; i < h(); i++)
length_temp += holes.get(i).boundary_length();
return length_temp;
}
double area() {
double area_temp = outer_boundary.area();
for (int i = 0; i < h(); i++)
area_temp += holes.get(i).area();
return area_temp;
}
ArrayList<VLPoint> random_points(int count, double epsilon) {
assert (area() > 0);
BoundingBox bounding_box = bbox();
ArrayList<VLPoint> pts_in_environment = 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_environment.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_environment.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_environment;
}
BoundingBox bbox() {
return outer_boundary.bbox();
}
public VLPolygon get(int i) {
if (i == 0) {
return outer_boundary;
} else {
return holes.get(i - 1);
}
}
public void enforce_standard_form() {
if (outer_boundary.area() < 0)
outer_boundary.reverse();
outer_boundary.enforce_standard_form();
for (int i = 0; i < h(); i++) {
if (holes.get(i).area() > 0)
holes.get(i).reverse();
holes.get(i).enforce_standard_form();
}
}
void eliminate_redundant_vertices(double epsilon) {
outer_boundary.eliminate_redundant_vertices(epsilon);
for (int i = 0; i < holes.size(); i++)
holes.get(i).eliminate_redundant_vertices(epsilon);
update_flattened_index_key();
}
void reverse_holes() {
for (int i = 0; i < holes.size(); i++)
holes.get(i).reverse();
}
void update_flattened_index_key() {
flattened_index_key.clear();
for (int i = 0; i <= h(); i++) {
for (int j = 0; j < get(i).n(); j++) {
pair<Integer, Integer> pair_temp = new pair<Integer, Integer>(i, j);
flattened_index_key.add(pair_temp);
}
}
}
pair<Integer, Integer> one_to_two(int k) {
pair<Integer, Integer> two = new pair<Integer, Integer>(0, 0);
// Strategy: add up vertex count of each Polygon (outer boundary +
// holes) until greater than k
int current_polygon_index = 0;
int vertex_count_up_to_current_polygon = get(0).n();
int vertex_count_up_to_last_polygon = 0;
while (k >= vertex_count_up_to_current_polygon && current_polygon_index < h()) {
current_polygon_index++;
two.first = two.first + 1;
vertex_count_up_to_last_polygon = vertex_count_up_to_current_polygon;
vertex_count_up_to_current_polygon += get(current_polygon_index).n();
}
two.second = k - vertex_count_up_to_last_polygon;
return two;
}
public String toString() {
String outs = "//Environment Model\n";
outs += "//Outer Boundary\n" + get(0);
for (int i = 1; i <= h(); i++) {
outs += "//Hole\n " + get(i);
}
// outs << "//EOF marker";
return outs;
}
double boundary_distance(VLPoint point_temp) {
return point_temp.boundary_distance(this);
}
}