/*-
* 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.datastructure;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator;
import de.hft.stuttgart.citydoctor2.utils.CopyHandler;
import de.hft.stuttgart.citydoctor2.utils.Copyable;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
/**
* Representation of a geometry containing the polygons and edges
*
* @author Matthias Betz
*
*/
public class Geometry extends GmlElement {
private static final long serialVersionUID = 2539031030917731575L;
private static Random r = new Random();
private GeometryType type;
private Lod lod;
private CityObject parent;
private List polygons = new ArrayList<>();
private List edges;
private Map, Edge> edgeMap;
private List vertices;
public Geometry(GeometryType type, Lod lod) {
this.lod = lod;
this.type = type;
}
void setParent(CityObject parent) {
this.parent = parent;
}
public CityObject getParent() {
return parent;
}
public void setType(GeometryType type) {
this.type = type;
}
public GeometryType getType() {
return type;
}
public Lod getLod() {
return lod;
}
public List getPolygons() {
return polygons;
}
public void addPolygon(Polygon cdPoly) {
polygons.add(cdPoly);
cdPoly.setParent(this);
// set part of boundary surface or building installation or both
if (parent instanceof BoundarySurface) {
BoundarySurface bs = (BoundarySurface) parent;
cdPoly.setPartOfSurface(bs);
if (bs.getParent() instanceof BuildingInstallation) {
cdPoly.setPartOfInstallation((BuildingInstallation) bs.getParent());
}
} else if (parent instanceof BuildingInstallation) {
cdPoly.setPartOfInstallation((BuildingInstallation) parent);
}
}
public void updateEdges() {
edges = new ArrayList<>();
edgeMap = new HashMap<>();
Map duplicacyMap = new HashMap<>();
for (Polygon p : polygons) {
LinearRing extRing = p.getExteriorRing();
createEdgesFromRing(duplicacyMap, p, extRing);
for (LinearRing inner : p.getInnerRings()) {
createEdgesFromRing(duplicacyMap, p, inner);
}
}
}
void anonymize() {
setGmlId(GmlId.generateId());
for (Polygon p : polygons) {
p.anonymize();
}
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double minZ = Double.MAX_VALUE;
for (Vertex v : vertices) {
if (v.getX() < minX) {
minX = v.getX();
}
if (v.getY() < minY) {
minY = v.getY();
}
if (v.getZ() < minZ) {
minZ = v.getZ();
}
}
double varX = r.nextDouble() * 10;
double varY = r.nextDouble() * 10;
double varZ = r.nextDouble() * 10;
for (Vertex v : vertices) {
v.setX(v.getX() - minX + varX);
v.setY(v.getY() - minY + varY);
v.setZ(v.getZ() - minZ + varZ);
}
}
private void createEdgesFromRing(Map duplicacyMap, Polygon p, LinearRing ring) {
if (ring == null) {
return;
}
// only go to size -1 as ring should be closed
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v0 = ring.getVertices().get(i + 0);
Vertex v1 = ring.getVertices().get(i + 1);
Edge tempEdge = new Edge(v0, v1);
Edge e = duplicacyMap.get(tempEdge);
if (e == null) {
// no duplicate found, enter edges
Edge oppositeEdge = new Edge(v1, v0);
duplicacyMap.put(tempEdge, tempEdge);
duplicacyMap.put(oppositeEdge, tempEdge);
e = tempEdge;
edges.add(e);
edgeMap.put(new SerializablePair<>(v0, v1), e);
edgeMap.put(new SerializablePair<>(v1, v0), e);
} else {
e.addHalfEdge(v0, v1);
}
e.addAdjacentPolygon(p);
e.addAdjacentRing(ring);
}
}
public Vector3d getCenter() {
return calculateBoundingBox().getCenter();
}
/**
* This will calculate two points where every other point in the geometry is
* between those two. If the geometry does not contain points or is null, an
* empty array is returned. This is considered an axis aligned bounding box.
* Only the exterior rings of the polygons is used as inner rings should be
* within the exterior or they are faulty.
*
* @return an array of two points. The first contains the lowest coordinates,
* the second contains the highest coordinates
*/
public BoundingBox calculateBoundingBox() {
return BoundingBoxCalculator.calculateBoundingBox(polygons);
}
public double calculateVolume() {
double sum = 0;
Vector3d center = calculateBoundingBox().getCenter();
for (Polygon p : polygons) {
TesselatedPolygon tesselatedPolygon = p.tesselate();
for (Triangle3d t : tesselatedPolygon.getTriangles()) {
Vector3d p1 = t.getP1().minus(center);
Vector3d p2 = t.getP2().minus(center);
Vector3d p3 = t.getP3().minus(center);
sum += det(p1.getCoordinates(), p2.getCoordinates(), p3.getCoordinates());
}
}
return sum / 6d;
}
private double det(double[] a, double[] b, double[] c) {
return -c[0] * b[1] * a[2] + b[0] * c[1] * a[2] + c[0] * a[1] * b[2] - a[0] * c[1] * b[2] - b[0] * a[1] * c[2]
+ a[0] * b[1] * c[2];
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (Polygon p : polygons) {
if (p.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
for (Polygon p : polygons) {
p.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List errors) {
super.collectContainedErrors(errors);
for (Polygon p : polygons) {
p.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (Polygon p : polygons) {
if (p.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (Polygon p : polygons) {
p.accept(c);
}
}
@Override
public String toString() {
return "Geometry [type=" + type + ", lod=" + lod + ", id=" + getGmlId() + "]";
}
public List getVertices() {
return vertices;
}
public List getEdges() {
return edges;
}
public List getEdgesAdjacentTo(Vertex v) {
List adjacentEdges = new ArrayList<>();
for (Edge e : edges) {
if (e.getFrom() == v || e.getTo() == v) {
adjacentEdges.add(e);
}
}
return adjacentEdges;
}
public Edge getEdge(Vertex v1, Vertex v2) {
return edgeMap.get(new SerializablePair<>(v1, v2));
}
public void updateEdgesAndVertices() {
updateVertices();
updateEdges();
}
public void updateVertices() {
Set vertexSet = new HashSet<>();
for (Polygon p : getPolygons()) {
updateRing(vertexSet, p.getExteriorRing());
for (LinearRing inner : p.getInnerRings()) {
updateRing(vertexSet, inner);
}
}
vertices = new ArrayList<>(vertexSet);
}
private void updateRing(Set vertexSet, LinearRing ring) {
if (ring == null) {
return;
}
for (Vertex v : ring.getVertices()) {
if (vertexSet.add(v)) {
// new vertex, clear adjacent rings in case new rings have been added
v.clearAdjacentRings(this);
}
// add ring to adjacent rings of vertex
v.addAdjacentRing(ring, this);
}
}
public void removePolygon(Polygon p) {
p.removeRings();
polygons.remove(p);
if (p.isLink()) {
Polygon original = p.getOriginal();
original.getParent().polygons.remove(original);
} else if (p.isLinkedTo()) {
Polygon link = p.getLinkedFromPolygon();
link.getParent().polygons.remove(link);
}
}
public void replacePolygon(Polygon p, ConcretePolygon... polygons) {
removePolygon(p);
if (p.isLink()) {
Geometry geom2 = p.getOriginal().getParent();
for (ConcretePolygon newPoly : polygons) {
geom2.addPolygon(newPoly);
addPolygon(new LinkedPolygon(newPoly, this));
}
} else if (p.isLinkedTo()) {
Geometry geom2 = p.getLinkedFromPolygon().getParent();
for (ConcretePolygon newPoly : polygons) {
addPolygon(newPoly);
geom2.addPolygon(new LinkedPolygon(newPoly, geom2));
}
} else {
for (Polygon newPoly : polygons) {
addPolygon(newPoly);
}
}
}
public List getEdgesAdjacentTo(Polygon p) {
List list = new ArrayList<>();
for (Edge e : edges) {
if (e.getAdjacentPolygons().contains(p)) {
list.add(e);
}
}
return list;
}
public boolean containsPolygon(Polygon other) {
for (Polygon p : polygons) {
if (p.getGmlId().equals(other.getGmlId())) {
return true;
}
}
return false;
}
@Override
public void prepareForChecking() {
updateEdgesAndVertices();
}
@Override
public void clearMetaInformation() {
if (vertices != null) {
for (Vertex v : vertices) {
v.clearAdjacentRings();
}
vertices = null;
}
edges = null;
edgeMap = null;
}
@Override
public void collectInstances(CopyHandler handler) {
handler.addInstance(parent);
handler.addInstance(polygons);
handler.addInstance(edges);
handler.addInstance(vertices);
if (edgeMap != null) {
for (Entry, Edge> e : edgeMap.entrySet()) {
handler.addInstance(e.getKey().getValue0());
handler.addInstance(e.getKey().getValue1());
handler.addInstance(e.getValue());
}
}
}
@Override
public Copyable createCopyInstance() {
return new Geometry(type, lod);
}
@Override
public void fillValues(Copyable original, CopyHandler handler) {
super.fillValues(original, handler);
Geometry originalGeometry = (Geometry) original;
for (Polygon originalPoly : originalGeometry.polygons) {
polygons.add(handler.getCopyInstance(originalPoly));
}
if (originalGeometry.edges != null) {
edges = new ArrayList<>(originalGeometry.edges.size());
for (Edge originalEdge : originalGeometry.edges) {
edges.add(handler.getCopyInstance(originalEdge));
}
}
if (originalGeometry.edgeMap != null) {
edgeMap = new HashMap<>(originalGeometry.edgeMap.size());
for (Entry, Edge> originalEntry : originalGeometry.edgeMap.entrySet()) {
Vertex copyV0 = handler.getCopyInstance(originalEntry.getKey().getValue0());
Vertex copyV1 = handler.getCopyInstance(originalEntry.getKey().getValue1());
SerializablePair pair = new SerializablePair<>(copyV0, copyV1);
Edge copyEdge = handler.getCopyInstance(originalEntry.getValue());
edgeMap.put(pair, copyEdge);
}
}
if (originalGeometry.vertices != null) {
vertices = new ArrayList<>(originalGeometry.vertices.size());
for (Vertex originalVertex : originalGeometry.vertices) {
vertices.add(handler.getCopyInstance(originalVertex));
}
}
parent = handler.getCopyInstance(originalGeometry.parent);
}
}