package org.osm2world.core.math;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class PolygonWithHolesXZ {
private final SimplePolygonXZ outerPolygon;
private final List<SimplePolygonXZ> holes;
public PolygonWithHolesXZ(SimplePolygonXZ outerPolygon,
List<SimplePolygonXZ> holes) {
this.outerPolygon = outerPolygon;
this.holes = holes;
/**
* forces the early computation of the area to avoid the creation of an invalid one. This is a temporary kludge as
* calling a method that can be overridden in a constructor is a bad practice. Moreover, the late call of the method
* calculateArea() was originally intended to avoid unnecessary computations but as it is necessary for intersection
* tests even before any rendering attempts, it no longer makes sense. A better solution would consist in running the
* computation of the area as soon as possible
* */
this.getArea();
}
public SimplePolygonXZ getOuter() {
return outerPolygon;
}
public List<SimplePolygonXZ> getHoles() {
return holes;
}
/**
* returns a list that contains the outer polygon and all holes
*/
public List<SimplePolygonXZ> getPolygons() {
if (holes.isEmpty()) {
return Collections.singletonList(outerPolygon);
} else {
List<SimplePolygonXZ> result = new ArrayList<SimplePolygonXZ>(holes.size()+1);
result.add(outerPolygon);
result.addAll(holes);
return result;
}
}
public TriangleXZ asTriangleXZ() {
if (!holes.isEmpty()) {
throw new InvalidGeometryException("polygon has holes, it cannot be used as a triangle");
}
return outerPolygon.asTriangleXZ();
}
public boolean contains(SimplePolygonXZ boundary) {
//FIXME currently returns true if boundary intersects one of the holes!
if (!outerPolygon.contains(boundary)) {
return false;
} else {
for (SimplePolygonXZ hole : holes) {
if (hole.contains(boundary)) {
return false;
}
}
return true;
}
}
public boolean contains(VectorXZ v) {
if (!outerPolygon.contains(v)) {
return false;
} else {
for (SimplePolygonXZ hole : holes) {
if (hole.contains(v)) {
return false;
}
}
return true;
}
}
public boolean contains(LineSegmentXZ lineSegment) {
if (!this.contains(lineSegment.p1)
|| !this.contains(lineSegment.p2)) {
return false;
} else {
for (SimplePolygonXZ hole : holes) {
if (hole.intersects(lineSegment.p1, lineSegment.p2)) {
return false;
}
}
}
return true;
}
//TODO (duplicate code): do something like intersects(geometricObject)
public boolean intersects(LineSegmentXZ lineSegment) {
for (SimplePolygonXZ hole : holes) {
if (hole.intersects(lineSegment)) {
return true;
}
}
return outerPolygon.intersects(lineSegment);
}
public boolean intersects(SimplePolygonXZ other) {
for (SimplePolygonXZ hole : holes) {
if (hole.intersects(other)) {
return true;
}
}
return outerPolygon.intersects(other);
}
public List<VectorXZ> intersectionPositions(LineSegmentXZ lineSegment) {
List<VectorXZ> intersectionPositions = new ArrayList<VectorXZ>();
for (SimplePolygonXZ hole : holes) {
intersectionPositions.addAll(hole.intersectionPositions(lineSegment));
}
intersectionPositions.addAll(outerPolygon.intersectionPositions(lineSegment));
return intersectionPositions;
}
public Collection<VectorXZ> intersectionPositions(PolygonWithHolesXZ p2) {
List<VectorXZ> intersectionPositions = new ArrayList<VectorXZ>();
for (SimplePolygonXZ simplePoly : p2.getPolygons()) {
for (LineSegmentXZ lineSegment : simplePoly.getSegments()) {
intersectionPositions.addAll(
intersectionPositions(lineSegment));
}
}
return intersectionPositions;
}
public double getArea() {
//FIXME incorrect for overlapping holes
double area = outerPolygon.getArea();
for (SimplePolygonXZ hole : holes) {
area -= hole.getArea();
}
return area;
}
}