/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 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.
*
* CityDoctor2 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 CityDoctor2. If not, see .
*/
package de.hft.stuttgart.citydoctor2.math;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
/**
* A two dimensional polygon. Has an 2d exterior ring and interior rings
*
* @author Matthias Betz
*
*/
public class Polygon2d {
private Ring2d exterior;
private List innerRings;
public static Polygon2d withProjection(Polygon poly) {
Vector3d normal = poly.calculateNormalNormalized();
int[] axis = getProjectionAxis(normal);
return projectTo2D(poly, axis);
}
public static Polygon2d withProjection(MovedPolygon poly, int[] projectionAxis) {
return projectTo2D(poly, projectionAxis);
}
public static Polygon2d withProjection(Polygon poly, int[] projectionAxis) {
return projectTo2D(poly, projectionAxis);
}
private static Polygon2d projectTo2D(Polygon p, int[] axis) {
List interior = new ArrayList<>();
Ring2d exterior = projectRing(p.getExteriorRing(), axis);
for (LinearRing innerRing : p.getInnerRings()) {
interior.add(projectRing(innerRing, axis));
}
return new Polygon2d(exterior, interior);
}
private static Polygon2d projectTo2D(MovedPolygon p, int[] axis) {
List interior = new ArrayList<>();
Ring2d exterior = projectRing(p.getExteriorRing(), axis);
for (MovedRing innerRing : p.getInnerRings()) {
interior.add(projectRing(innerRing, axis));
}
return new Polygon2d(exterior, interior);
}
public static int[] getProjectionAxis(Vector3d normal) {
double nx = Math.abs(normal.getX());
double ny = Math.abs(normal.getY());
double nz = Math.abs(normal.getZ());
int[] axis;
if (nx >= ny && nx >= nz) {
axis = new int[] { 1, 2 };
} else if (ny >= nx && ny >= nz) {
axis = new int[] { 0, 2 };
} else {
axis = new int[] { 0, 1 };
}
return axis;
}
private static Ring2d projectRing(LinearRing r, int[] axis) {
List projectedVertices = new ArrayList<>();
for (Vertex v : r.getVertices()) {
projectedVertices.add(new Vector2d(v.getCoordinate(axis[0]), v.getCoordinate(axis[1])));
}
return new Ring2d(projectedVertices, r);
}
private static Ring2d projectRing(MovedRing r, int[] axis) {
List projectedVertices = new ArrayList<>();
for (Vector3d v : r.getVertices()) {
projectedVertices.add(new Vector2d(v.getCoordinate(axis[0]), v.getCoordinate(axis[1])));
}
return new Ring2d(projectedVertices, r.getOriginal());
}
public static Polygon2d withRotationMatrix(Polygon poly) {
Matrix3x3d rotMatrix = PolygonUtils.calculateRotationMatrix(poly);
LinearRing lr = poly.getExteriorRing();
Ring2d exterior = createRing2d(rotMatrix, lr);
List innerRings = new ArrayList<>();
for (LinearRing innerRing : poly.getInnerRings()) {
innerRings.add(createRing2d(rotMatrix, innerRing));
}
return new Polygon2d(exterior, innerRings);
}
/**
* Converts a Polygon to a 2d polygon. This will change the area of the polygon.
*
* @param ring a linear ring
*/
private Polygon2d(Ring2d exterior, List interior) {
if (interior == null) {
interior = Collections.emptyList();
}
this.exterior = exterior;
this.innerRings = interior;
}
private static Ring2d createRing2d(Matrix3x3d rotMatrix, LinearRing lr) {
List ringVertices = new ArrayList<>();
for (Vertex v : lr.getVertices()) {
Vector3d rotated = rotMatrix.mult(v);
Vector2d v2d = new Vector2d(rotated.getX(), rotated.getY());
ringVertices.add(v2d);
}
return new Ring2d(ringVertices, lr);
}
public Ring2d getExterior() {
return exterior;
}
public List getInteriorRings() {
if (innerRings == null) {
return Collections.emptyList();
}
return innerRings;
}
}