Commit 5d40c7b6 authored by Matthias Betz's avatar Matthias Betz
Browse files

CityDoctor2 validation open source release

parents
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<cd:error_report xmlns:cd="http://www.citydoctor.eu"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.citydoctor.eu ../CityDoctorValidation/errorReportSchema.xsd ">
<cd:header>
<cd:file>cd:file</cd:file>
<cd:environment>
<cd:city_doctor_validation_version>cd:city_doctor_validation_version</cd:city_doctor_validation_version>
<cd:java_vm_version>cd:java_vm_version</cd:java_vm_version>
<cd:java_vm_vendor>cd:java_vm_vendor</cd:java_vm_vendor>
<cd:java_version>cd:java_version</cd:java_version>
<cd:os_name>cd:os_name</cd:os_name>
<cd:os_arch>cd:os_arch</cd:os_arch>
</cd:environment>
<cd:date>2001-12-31T12:00:00+01:00</cd:date>
</cd:header>
<cd:validation_plan>
<cd:numberOfRoundingPlaces>8</cd:numberOfRoundingPlaces>
<cd:check activated="true" name="">
<cd:parameter name="">cd:parameter</cd:parameter>
</cd:check>
</cd:validation_plan>
<cd:global_statistics>
<cd:model_statistics>
<cd:num_buildings>0</cd:num_buildings>
<cd:num_vegetation>0</cd:num_vegetation>
<cd:num_transportation>0</cd:num_transportation>
<cd:num_land_objects>0</cd:num_land_objects>
<cd:num_bridge_objects>0</cd:num_bridge_objects>
<cd:num_water_objects>0</cd:num_water_objects>
</cd:model_statistics>
<cd:global_error_statistics>
<cd:num_error_buildings>0</cd:num_error_buildings>
<cd:num_error_vegetation>0</cd:num_error_vegetation>
<cd:num_error_transportation>0</cd:num_error_transportation>
<cd:num_error_land_objects>0</cd:num_error_land_objects>
<cd:num_error_bridge_objects>0</cd:num_error_bridge_objects>
<cd:num_error_water_objects>0</cd:num_error_water_objects>
</cd:global_error_statistics>
<cd:errors>
<cd:error name="">0</cd:error>
</cd:errors>
</cd:global_statistics>
<cd:validation_results>
<cd:building_report gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:building_report>
<cd:vegetation_report gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:vegetation_report>
<cd:transportation_object_report
gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:transportation_object_report>
<cd:land_object_report gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:land_object_report>
<cd:bridge_object_report gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:bridge_object_report>
<cd:water_object_report gml_id="">
<cd:statistics>
<cd:error_statistics>
<cd:error name="">0</cd:error>
</cd:error_statistics>
</cd:statistics>
<cd:errors>
<cd:error id="" status="ERROR">
<cd:feature feature_type="" gml_id="" />
<cd:error_details />
</cd:error>
</cd:errors>
</cd:water_object_report>
</cd:validation_results>
</cd:error_report>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.7.0-SNAPSHOT</version>
</parent>
<artifactId>CityDoctorEdge</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorModel</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class BaseEntity {
private List<BaseEntity> parents = new ArrayList<>(2);
private List<BaseEntity> children = new ArrayList<>(2);
public void addChild(BaseEntity e) {
children.add(e);
e.parents.add(this);
}
@SuppressWarnings("unchecked")
public <T> List<T> getParents(Class<T> clazz) {
Set<T> result = new HashSet<>();
for (BaseEntity b : parents) {
if (clazz.isAssignableFrom(b.getClass())) {
result.add((T) b);
}
}
return new ArrayList<>(result);
}
@SuppressWarnings("unchecked")
public <T> List<T> getChildren(Class<T> clazz) {
List<T> result = new ArrayList<>();
for (BaseEntity b : children) {
if (b.getClass() == clazz) {
result.add((T) b);
}
}
return result;
}
public void removeChild(BaseEntity pEntity) {
children.remove(pEntity);
// check for multiple relationships
pEntity.parents.remove(this);
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class Box2d {
private Point2d mMin;
private Point2d mMax;
public Box2d(Point2d min, Point2d max) {
mMin = min;
mMax = max;
}
public boolean isPointInside(Point2d crPoint) {
return ( ( crPoint.getX() >= mMin.getX()) &&
( crPoint.getX() <= mMax.getX()) &&
( crPoint.getY() >= mMin.getY()) &&
( crPoint.getY() <= mMax.getY()));
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List;
public class CDPolygonNs extends PolygonNs {
private List<List<HalfEdge>> innerHalfEdges = new ArrayList<>();
public CDPolygonNs(List<List<HalfEdge>> halfEdges) {
super(halfEdges.get(0));
for (int i = 1; i < halfEdges.size(); i++) {
List<HalfEdge> loopEdges = halfEdges.get(i);
for (HalfEdge e : loopEdges) {
addChild(e);
}
innerHalfEdges.add(loopEdges);
}
}
public List<List<HalfEdge>> getInnerHalfEdges() {
return innerHalfEdges;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class Coordinate2d extends BaseEntity {
private Point2d point;
public Coordinate2d(Point2d point) {
this.point = point;
}
public Point2d getPoint() {
return point;
}
public void setPoint(Point2d point) {
this.point = point;
}
@Override
public String toString() {
return "Coordinate2d [point=" + point + "]";
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Coordinate3d extends BaseEntity {
private Point3d point;
public Coordinate3d(Point3d point) {
this.point = point;
}
public Coordinate3d(double x, double y, double z) {
this(new Point3d(x, y, z));
}
public Point3d getPoint() {
return point;
}
public List<EdgePolygon> getPolygons() {
List<HalfEdge> halfEdges = getParents(HalfEdge.class);
Set<EdgePolygon> polygons = new HashSet<>();
for (HalfEdge he : halfEdges) {
List<EdgePolygon> localPolygons = he.getParents(EdgePolygon.class);
polygons.addAll(localPolygons);
}
return new ArrayList<>(polygons);
}
@Override
public String toString() {
return "Coordinate3d [point=" + point + "]";
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.text.NumberFormat;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class DebugUtils {
private static AtomicInteger polygon3dCounter = new AtomicInteger(1);
private static AtomicInteger coordListCounter = new AtomicInteger(1);
private DebugUtils() {
}
public static void printPolyLineSegment2dList(List<PolyLineSegment2d> rSegments) {
Locale.setDefault(Locale.US);
for (PolyLineSegment2d seg : rSegments) {
Point2d start = seg.getStart().getPoint();
Point2d end = seg.getEnd().getPoint();
System.out.println(String.format(
"segmentList.push_back(new PolyLineSegment2d(new Coordinate2d(%f, %f), new Coordinate2d(%f, %f)));",
start.getX(), start.getY(), end.getX(), end.getY()));
}
}
public static void printPolygon2d(Polygon2d mcpPolygon1) {
Locale.setDefault(Locale.US);
System.out.println("Coordinate2dList coords;");
HalfEdge2d firstHalfEdge = mcpPolygon1.getFirstHalfEdge();
Point2d start = firstHalfEdge.getStart().getPoint();
System.out.println(String.format("coords.push_back(new Coordinate2d(%f, %f));", start.getX(), start.getY()));
HalfEdge2d next = firstHalfEdge.getNext();
while (next != null && next != firstHalfEdge) {
start = next.getStart().getPoint();
System.out.println(String.format("coords.push_back(new Coordinate2d(%f, %f));", start.getX(), start.getY()));
next = next.getNext();
}
System.out.println("Polygon2d* mcpPolygon1 = new Polygon2dNs(coords);");
}
public static void printPolygon3d(EdgePolygon p) {
Locale.setDefault(Locale.US);
int counter = polygon3dCounter.getAndIncrement();
System.out.println("Coordinate3dList coords" + counter + ";");
HalfEdge firstHalfEdge = p.getFirstHalfEdge();
Point3d start = firstHalfEdge.getStart().getPoint();
System.out.println(String.format("coords%d.push_back(new Coordinate3d(%f, %f, %f));", counter, start.getX(), start.getY(), start.getZ()));
HalfEdge next = firstHalfEdge.getNext();
while (next != null && next != firstHalfEdge) {
start = next.getStart().getPoint();
System.out.println(String.format("coords%d.push_back(new Coordinate3d(%f, %f, %f));", counter, start.getX(), start.getY(), start.getZ()));
next = next.getNext();
}
System.out.println("PolygonNs* p" + counter + " = new PolygonNs(coords" + counter+ ");");
}
public static void printPolygon3d(CDPolygonNs edgePoly1, CDPolygonNs edgePoly2) {
Locale.setDefault(Locale.US);
Map<Point3d, String> pointMap = new IdentityHashMap<>();
int startCounter = 1;
startCounter = printPolygon3d(edgePoly1, pointMap, startCounter);
printPolygon3d(edgePoly2, pointMap, startCounter);
}
public static void printGeoknechtPolygon(CDPolygonNs... polys) {
Locale.setDefault(Locale.US);
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(30);
for (CDPolygonNs poly : polys) {
List<Coordinate3d> coordinates = poly.getCoordinates();
System.out.print("polygon(");
for (Coordinate3d coord : coordinates) {
Point3d point = coord.getPoint();
writeGeoknechtPoint(nf, point);
}
writeGeoknechtPoint(nf, coordinates.get(0).getPoint());
System.out.println(")");
for (List<HalfEdge> innerRing : poly.getInnerHalfEdges()) {
System.out.print("polygon(");
for (HalfEdge he : innerRing) {
Point3d point = he.getStart().getPoint();
writeGeoknechtPoint(nf, point);
}
writeGeoknechtPoint(nf, innerRing.get(innerRing.size() - 1).getEnd().getPoint());
System.out.println(")");
}
}
}
private static void writeGeoknechtPoint(NumberFormat nf, Point3d point) {
System.out.print(nf.format(point.getX()));
System.out.print("|");
System.out.print(nf.format(point.getY()));
System.out.print("|");
System.out.print(nf.format(point.getZ()));
System.out.print(" ");
}
private static int printPolygon3d(CDPolygonNs p, Map<Point3d, String> pointMap, int startCounter) {
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(30);
int counter = polygon3dCounter.getAndIncrement();
int listCounter = coordListCounter.getAndIncrement();
int firstListCounter = listCounter;
HalfEdge firstHalfEdge = p.getFirstHalfEdge();
int[] counterArr = new int[] {startCounter};
System.out.println("Coordinate3dList coords" + firstListCounter + ";");
writeRing(pointMap, nf, listCounter, firstHalfEdge, counterArr);
for (List<HalfEdge> innerRing : p.getInnerHalfEdges()) {
listCounter = coordListCounter.getAndIncrement();
System.out.println("Coordinate3dList coords" + listCounter + ";");
writeRing(pointMap, nf, listCounter, innerRing.get(0), counterArr);
}
int lastListCounter = coordListCounter.get();
System.out.println("std::list<Coordinate3dList> coordLists" + counter + ";");
for (int i = firstListCounter; i < lastListCounter; i++) {
System.out.println("coordLists" + counter + ".push_back(coords" + i +");");
}
System.out.println("CDPolygonNs* p" + counter + " = new CDPolygonNs(coordLists" + counter+ ");");
return counterArr[0];
}
private static void writeRing(Map<Point3d, String> pointMap, NumberFormat nf, int counter, HalfEdge firstHalfEdge,
int[] counterArr) {
final Point3d start = firstHalfEdge.getStart().getPoint();
String coordVarName = getCoordName(pointMap, nf, start, counterArr);
System.out.println(String.format("coords%d.push_back(%s);", counter, coordVarName));
HalfEdge next = firstHalfEdge.getNext();
while (next != null && next != firstHalfEdge) {
Point3d nextPoint = next.getStart().getPoint();
coordVarName = getCoordName(pointMap, nf, nextPoint, counterArr);
System.out.println(String.format("coords%d.push_back(%s);", counter, coordVarName));
next = next.getNext();
}
}
private static String getCoordName(Map<Point3d, String> pointMap, NumberFormat nf, final Point3d start,
int[] counterArr) {
String coordVarName = pointMap.computeIfAbsent(start, k -> {
System.out.println(String.format("Coordinate3d* c%d = new Coordinate3d(%s, %s, %s);", counterArr[0], nf.format(start.getX()), nf.format(start.getY()), nf.format(start.getZ())));
String name = "c" + counterArr[0];
counterArr[0]++;
return name;
});
return coordVarName;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class EdgePolygon extends BaseEntity {
private List<HalfEdge> halfEdges;
public EdgePolygon(List<HalfEdge> halfEdges) {
for (HalfEdge he : halfEdges) {
addChild(he);
}
this.halfEdges = halfEdges;
}
public GmPlane getPlane() {
Point3d midPoint = getMidPoint();
List<HalfEdge> children = getChildren(HalfEdge.class);
Vector3d averageNormalVector = new Vector3d(0.0, 0.0, 0.0);
for (HalfEdge he : children) {
Point3d start = he.getStart().getPoint();
Point3d end = he.getEnd().getPoint();
Vector3d mid2Start = start.minus(midPoint);
Vector3d mid2End = end.minus(midPoint);
averageNormalVector = averageNormalVector.plus(mid2Start.cross(mid2End));
}
UnitVector3d normalVector = averageNormalVector.toUnitVector();
return new GmPlane(midPoint, normalVector);
}
private Point3d getMidPoint() {
Point3d midPoint = new Point3d(0, 0, 0);
List<HalfEdge> children = getChildren(HalfEdge.class);
for (HalfEdge he : children) {
midPoint = midPoint.plus(he.getStart().getPoint());
}
return (midPoint.div(children.size()));
}
public List<HalfEdge> getHalfEdges() {
return halfEdges;
}
public List<Coordinate3d> getCoordinates() {
List<Coordinate3d> coords = new ArrayList<>();
HalfEdge firstHE = Objects.requireNonNull(getFirstHalfEdge());
HalfEdge currHE = firstHE;
do {
coords.add(currHE.getStart());
currHE = currHE.getNext();
} while (currHE != firstHE);
return coords;
}
public HalfEdge getFirstHalfEdge() {
if (!halfEdges.isEmpty()) {
return halfEdges.get(0);
}
return null;
}
/**
* Test whether the given point is lying in- or outside of the non self
* intersecting, planar polygon. It's believed, that point and polygon are lying
* in the same plane. <br>
* <br>
* It's assumed, that the polygon is non self intersecting and planar.
* Additionally it is assumed, that the point and the polygon lying in the same
* plane. The given point is projected on the plane of the polygon. If the point
* is lying on an edge of the polygon it's supposed that the point is inside of
* the polygon
*
* @param rcPoint The point that should be checked
*
* @return true, if the point is inside the polygon, false otherwise
*
*/
public boolean isPointInsidePolygon(Point3d rcPoint, double eps) {
// Project the coordinate of the point on the plane
GmPlane plane = getPlane();
Point2d projectedPoint = plane.project(rcPoint);
Polygon2d pP = plane.projectOn2dPolygon(this);
return pP.isPointInsidePolygon(projectedPoint, eps);
}
public int getNrHalfEdges() {
return halfEdges.size();
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.Comparator;
public class Global {
private static final double DBL_EPSILON = 2.2204460492503131e-16;
private static double mZeroAngleCosinus = 1.0e-9;
private static double mTolVectorsParallel = 1e-9;
private static double mHighAccuracyTol = DBL_EPSILON * 100;
private static double mTolPointsEqual = 1e-3;
private Global() {
}
public static double getTolPointsEquals() {
return mTolPointsEqual;
}
public static double getHighAccuracyTolerance() {
return mHighAccuracyTol;
}
public static Comparator<Double> getDoubleTolCompare(double epsilon) {
return (v1, v2) -> {
double dif = v1 - v2;
if (Math.abs(dif) < epsilon) {
return 0;
}
return Double.compare(v1, v2);
};
}
public static double getZeroAngleCosinus() {
return mZeroAngleCosinus;
}
public static double getTolVectorsParallel() {
return mTolVectorsParallel;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class GmBoundedStraight extends GmStraight {
private Point3d target;
private double length;
public static GmBoundedStraight of(Point3d point, Vector3d vector) {
GmBoundedStraight straight = new GmBoundedStraight(point, vector.toUnitVector());
straight.length = vector.getLength();
straight.target = point.plus(vector);
return straight;
}
public static GmBoundedStraight of(Point3d start, Point3d end) {
Vector3d vec = end.minus(start);
UnitVector3d dir = vec.toUnitVector();
GmBoundedStraight straight = new GmBoundedStraight(start, dir);
straight.target = end;
straight.length = vec.getLength();
return straight;
}
private GmBoundedStraight(Point3d start, UnitVector3d dir) {
super(start, dir);
}
public Point3d getTarget() {
return target;
}
public double getLength() {
return length;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class GmBoundedStraight2d extends GmStraight2d {
private double length;
private Point2d target;
public static GmBoundedStraight2d of(Point2d start, Vector2d dir) {
GmBoundedStraight2d straight = new GmBoundedStraight2d(start, dir);
straight.target = straight.getOrigin().plus(dir);
straight.length = dir.getLength();
return straight;
}
public static GmBoundedStraight2d of(Point2d from, Point2d to) {
Vector2d dir = to.minus(from);
GmBoundedStraight2d straight = new GmBoundedStraight2d(from, dir);
straight.target = to;
straight.length = dir.getLength();
return straight;
}
private GmBoundedStraight2d(Point2d start, Vector2d dir) {
super(start, dir);
}
public Point2d getTarget() {
return target;
}
public boolean isWithinBoundaries(double parameter, double eps) {
if (Math.abs(parameter) < eps) {
parameter = 0.0;
}
if (Math.abs(parameter - length) < eps) {
parameter = length;
}
if (0 <= parameter && parameter <= length) {
return true;
}
return false;
}
public double getLength() {
return length;
}
public boolean isWithinBoundaries(double parameter) {
return isWithinBoundaries(parameter, Global.getHighAccuracyTolerance());
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List;
public class GmPlane {
private Point3d x0;
private UnitVector3d r1;
private UnitVector3d r2;
private UnitVector3d n;
private double d;
public GmPlane(Point3d point, UnitVector3d normalVector) {
x0 = point;
n = normalVector;
d = new Vector3d(point).dot(n);
r1 = UnitVector3d.of(n.getZ(), n.getX(), n.getY());
r2 = n.cross(r1).toUnitVector();
// r1 ist nicht in allen Faellen rechtwinklig zur Flaechennormalen
// daher nochmal eine neu Berechnung;
// ----------------------------------------------------------------
r1 = n.cross(r2).toUnitVector();
}
public double getDistance() {
return d;
}
public Vector3d getNormal() {
return n;
}
public Point3d evaluate(Point2d point) {
return evaluate(point.getX(), point.getY());
}
private Point3d evaluate(double x, double y) {
Vector3d vectorU = r1.mult(x);
Vector3d vectorV = r2.mult(y);
return new Point3d(x0.plus(vectorU).plus(vectorV));
}
public GmStraight2d projectOn2dStraight(GmStraight crStraight) {
Point2d start = project(crStraight.getOrigin());
Point2d end = project(crStraight.getOrigin().plus(crStraight.getDir()));
return new GmStraight2d(start, end);
}
public GmBoundedStraight2d projectOn2dStraight(GmBoundedStraight crBoundedStraight) {
Point2d start = project(crBoundedStraight.getOrigin());
Point2d end = project(crBoundedStraight.getTarget());
return GmBoundedStraight2d.of(start, end);
}
public Point2d project(Point3d crPoint) {
GmStraight firstAxis = new GmStraight(x0, r1);
GmStraight secondAxis = new GmStraight(x0, r2);
double u = firstAxis.project(crPoint).getParameter();
double v = secondAxis.project(crPoint).getParameter();
return new Point2d(u, v);
}
public GmStraight planeIntersection(GmPlane other) {
// get plane normals and distances
// -------------------------------
Vector3d normal1 = getNormal();
Vector3d normal2 = other.getNormal();
double dist1 = getDistance();
double dist2 = other.getDistance();
// planes (normal vectors) parallel? -> good bye
// ---------------------------------------------
if (Vector3d.areParallel(normal1, normal2) || Vector3d.areAntiParallel(normal1, normal2)) {
return null; // but rSuccess is false
}
// get direction and origin for straight
// -------------------------------------
Vector3d dir = normal1.cross(normal2);
Matrix3d matrix = new Matrix3d();
matrix.set(0, 0, normal1.getX());
matrix.set(0, 1, normal1.getY());
matrix.set(0, 2, normal1.getZ());
matrix.set(1, 0, normal2.getX());
matrix.set(1, 1, normal2.getY());
matrix.set(1, 2, normal2.getZ());
matrix.set(2, 0, dir.getX());
matrix.set(2, 1, dir.getY());
matrix.set(2, 2, dir.getZ());
double[] b = new double[] { dist1, dist2, 0.0 };
double[] org = new double[3];
matrix.gauss(b, org);
return new GmStraight(new Point3d(org), dir);
}
public Polygon2d projectOn2dPolygon(EdgePolygon cpPolygon) {
HalfEdge startHE = cpPolygon.getFirstHalfEdge();
HalfEdge currHE = startHE;
int numHEs = cpPolygon.getNrHalfEdges();
Coordinate2d[] coords = new Coordinate2d[numHEs];
int i = 0;
do {
Coordinate3d coord = currHE.getStart();
Point2d p = project(coord.getPoint());
coords[i++] = new Coordinate2d(p);
currHE = currHE.getNext();
} while (currHE != startHE);
List<HalfEdge2d> halfedges = new ArrayList<>();
for (i = 0; i < numHEs; ++i) {
HalfEdge2d he = new HalfEdge2d(coords[i], coords[(i + 1) % (numHEs)]);
halfedges.add(he);
}
return new Polygon2d(halfedges);
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight {
private Point3d org;
private UnitVector3d dir;
public GmStraight(Point3d org, Vector3d dir) {
this.org = org;
this.dir = dir.toUnitVector();
}
public GmStraight(Point3d org, UnitVector3d dir) {
this.org = org;
this.dir = dir;
}
public ProjectedPoint3d project(Point3d origin) {
Vector3d v2Origin = origin.minus(org);
double length = v2Origin.getLength();
if (length < Global.getTolPointsEquals()) {
return new ProjectedPoint3d(origin, 0.0);
}
double parameter = dir.dot(UnitVector3d.of(v2Origin)) * length;
Point3d foot = evaluate(parameter);
return new ProjectedPoint3d(foot, parameter);
}
public Point3d evaluate(double param) {
return org.plus(dir.mult(param));
}
public UnitVector3d getDir() {
return dir;
}
public boolean isColinear(GmStraight straight2, double angleEpsilon, double epsilon) {
UnitVector3d rDir1 = getDir();
UnitVector3d rDir2 = straight2.getDir();
if ((!Vector3d.areParallel(rDir1, rDir2, angleEpsilon))
&& (!Vector3d.areAntiParallel(rDir1, rDir2, angleEpsilon))) {
return false;
}
Point3d rOrigin1 = getOrigin();
Point3d foot1 = straight2.project(rOrigin1).getPoint();
if ((foot1.minus(rOrigin1)).getLength() > epsilon) {
return false;
}
Point3d rOrigin2 = straight2.getOrigin();
Point3d foot2 = project(rOrigin2).getPoint();
if ((foot2.minus(rOrigin2)).getLength() > epsilon) {
return false;
}
return true;
}
public Point3d getOrigin() {
return org;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight2d {
private Point2d origin;
private UnitVector2d direction;
public GmStraight2d(Point2d org, UnitVector2d dir) {
this.direction = dir;
this.origin = org;
}
public GmStraight2d(Point2d org, Vector2d dir) {
this.origin = org;
this.direction = dir.normalize();
}
public GmStraight2d(Point2d from, Point2d to) {
origin = from;
direction = to.minus(from).normalize();
}
/**
* Just intersects two GmStraights2d.
*
* <br>
* <br>
* If the two straights are parallel the method will return false, so the user
* has to determine, if the straight are just parallel or identical
*
* @param other First Straight
*
* @return intersection result
*/
public GmStraight2dIntersectionResult intersect(GmStraight2d other) {
Point2d p1 = origin;
UnitVector2d r1 = direction;
Point2d p2 = other.getOrigin();
UnitVector2d r2 = other.getDirection();
Vector2d q = p2.minus(p1);
Vector2d r2Perpendicular = r2.getPerpendicularVector();
double diff = r1.dot(r2Perpendicular);
if (Math.abs(diff) < Global.getZeroAngleCosinus()) {
return GmStraight2dIntersectionResult.parallel(this, other);
} else {
double invR1DotPerpR2 = 1.0 / diff;
double qDotPerpR1 = q.dot(r1.getPerpendicularVector());
double qDotPerpR2 = q.dot(r2Perpendicular);
double rParam1 = qDotPerpR2 * invR1DotPerpR2;
double rParam2 = qDotPerpR1 * invR1DotPerpR2;
return GmStraight2dIntersectionResult.intersecting(rParam1, rParam2, this, other);
}
}
public UnitVector2d getDirection() {
return direction;
}
public Point2d getOrigin() {
return origin;
}
public Point2d evaluate(double param) {
return origin.plus(direction.mult(param));
}
@Override
public String toString() {
return "GmStraight2d [origin=" + origin + ", direction=" + direction + "]";
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight2dIntersectionResult {
private double paramHE;
private double paramInt;
private GmStraight2d straightHE;
private GmStraight2d straightInt;
private boolean areParallel;
public static GmStraight2dIntersectionResult parallel(GmStraight2d s1, GmStraight2d s2) {
return new GmStraight2dIntersectionResult(0, 0, s1, s2, true);
}
public static GmStraight2dIntersectionResult intersecting(double rParam1, double rParam2, GmStraight2d s1,
GmStraight2d s2) {
return new GmStraight2dIntersectionResult(rParam1, rParam2, s1, s2, false);
}
private GmStraight2dIntersectionResult(double paramHE, double paramInt, GmStraight2d straightHE,
GmStraight2d straightInt, boolean areParallel) {
this.paramHE = paramHE;
this.paramInt = paramInt;
this.straightHE = straightHE;
this.straightInt = straightInt;
this.areParallel = areParallel;
}
public double getParamHE() {
return paramHE;
}
public double getParamInt() {
return paramInt;
}
public GmStraight2d getStraightHE() {
return straightHE;
}
public GmStraight2d getStraightInt() {
return straightInt;
}
public boolean areParallel() {
return areParallel;
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.List;
public class HalfEdge extends BaseEntity {
private Coordinate3d start;
private Coordinate3d end;
private HalfEdge partner;
private HalfEdge next;
public HalfEdge(Coordinate3d start, Coordinate3d end, EdgePolygon parent) {
this.start = start;
this.end = end;
List<HalfEdge> halfEdgesToStart = start.getParents(HalfEdge.class);
List<HalfEdge> halfEdgesToEnd = end.getParents(HalfEdge.class);
HalfEdge tempPartner;
if (parent == null) {
tempPartner = findPartner(halfEdgesToStart, halfEdgesToEnd);
} else {
tempPartner = findPartner(parent, halfEdgesToStart, halfEdgesToEnd);
}
if (tempPartner != null) {
setPartner(tempPartner);
}
addChild(start);
addChild(end);
}
public HalfEdge(Coordinate3d start, Coordinate3d end) {
this(start, end, null);
}
private HalfEdge findPartner(EdgePolygon parent, List<HalfEdge> halfEdgesToStart, List<HalfEdge> halfEdgesToEnd) {
HalfEdge tempPartner = null;
for (HalfEdge heStart : halfEdgesToStart) {
for (HalfEdge heEnd : halfEdgesToEnd) {
if (heStart == heEnd) {
tempPartner = heStart;
if (tempPartner.getPartner() == null) {
EdgePolygon partnerParent = tempPartner.getPolygon();
if (partnerParent != null || partnerParent != parent) {
return tempPartner;
}
}
}
}
}
return tempPartner;
}
private HalfEdge findPartner(List<HalfEdge> halfEdgesToStart, List<HalfEdge> halfEdgesToEnd) {
HalfEdge tempPartner = null;
for (HalfEdge heStart : halfEdgesToStart) {
for (HalfEdge heEnd : halfEdgesToEnd) {
if (heStart == heEnd) {
tempPartner = heStart;
if (tempPartner.getPartner() == null) {
EdgePolygon partnerParent = tempPartner.getPolygon();
if (partnerParent != null) {
return tempPartner;
}
}
}
}
}
return tempPartner;
}
public void setPartner(HalfEdge partner) {
this.partner = partner;
partner.partner = this;
}
public HalfEdge getPartner() {
return partner;
}
public EdgePolygon getPolygon() {
List<EdgePolygon> polygons = getParents(EdgePolygon.class);
if (!polygons.isEmpty()) {
return polygons.get(0);
}
return null;
}
public GmBoundedStraight getStraight() {
return GmBoundedStraight.of(start.getPoint(), end.getPoint());
}
public Coordinate3d getStart() {
return start;
}
public Coordinate3d getEnd() {
return end;
}
public HalfEdge getNext() {
return next;
}
public void setNext(HalfEdge next) {
this.next = next;
}
@Override
public String toString() {
return "HalfEdge [start=" + start + ", end=" + end + "]";
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.List;
public class HalfEdge2d extends BaseEntity {
private Coordinate2d start;
private Coordinate2d end;
private HalfEdge2d partner;
private HalfEdge2d next;
private HalfEdge2d previous;
public HalfEdge2d(Coordinate2d c1, Coordinate2d c2) {
this(c1, c2, false);
}
public HalfEdge2d(Coordinate2d pStart, Coordinate2d pEnd, boolean setPartner) {
start = pStart;
end = pEnd;
if (setPartner) {
List<HalfEdge2d> halfEdgesToStart = pStart.getParents(HalfEdge2d.class);
List<HalfEdge2d> halfEdgesToEnd = pEnd.getParents(HalfEdge2d.class);
HalfEdge2d pPartner = findPartner(halfEdgesToStart, halfEdgesToEnd);
if (pPartner != null) {
setPartner(pPartner);
}
} else {
partner = null;
}
addChild(start);
addChild(end);
}
private HalfEdge2d findPartner(List<HalfEdge2d> halfEdgesToStart, List<HalfEdge2d> halfEdgesToEnd) {
HalfEdge2d pPartner = null;
for (HalfEdge2d heStart : halfEdgesToStart) {
for (HalfEdge2d heEnd : halfEdgesToEnd) {
if (heStart == heEnd) {
pPartner = heStart;
if (pPartner.getPartner() == null) {
return pPartner;
}
}
}
}
return pPartner;
}
private HalfEdge2d setPartner(HalfEdge2d pPartner) {
if (partner != null) {
if (pPartner != null) {
throw new IllegalStateException("cannot overwrite existing partner-connection");
} else {
// remove the existing connection
partner.partner = null;
partner = null;
}
} else {
if (pPartner != null) {
if (pPartner.partner != null) {
// TODO : das riecht nach einer Ringverzeigerung : 3 Polygonraender treffen
// aufeinander
throw new IllegalStateException("cannot overwrite existing partner-connection");
} else {
partner = pPartner;
pPartner.partner = this;
}
}
}
return pPartner;
}
public HalfEdge2d getPartner() {
return partner;
}
public void setNext(HalfEdge2d next) {
this.next = next;
}
public void setPrevious(HalfEdge2d previous) {
this.previous = previous;
}
public Polygon2d getPolygon() {
List<Polygon2d> parents = getParents(Polygon2d.class);
if (!parents.isEmpty()) {
return parents.get(0);
}
return null;
}
public GmBoundedStraight2d getStraight() {
return GmBoundedStraight2d.of(start.getPoint(), end.getPoint());
}
public Coordinate2d getStart() {
return start;
}
public HalfEdge2d getNext() {
return next;
}
public Coordinate2d getEnd() {
return end;
}
public HalfEdge2d getPrevious() {
return previous;
}
@Override
public String toString() {
return "HalfEdge2d [start=" + start + ", end=" + end + "]";
}
}
/*-
* 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 <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
public class IntersectPlanarPolygons {
private static final double FULLY_EMBEDDED_TOLERANCE = Global.getHighAccuracyTolerance() * 1e2;
private static final double PROJECTED_POINT_DISTANCE_EPSILON = 1e-8;
public static List<PolygonPolygonIntersection> intersectPolygons(EdgePolygon p1, EdgePolygon p2, double epsilon,
double angleEpsilon) {
GmPlane plane1 = p1.getPlane();
GmPlane plane2 = p2.getPlane();
GmStraight straight = plane1.planeIntersection(plane2);
if (straight == null) {
// planes are parallel
double dist1 = plane1.getDistance();
double dist2 = plane2.getDistance();
if (Vector3d.areAntiParallel(plane1.getNormal(), plane2.getNormal())) {
dist2 *= -1;
}
if (Math.abs(dist1 - dist2) > epsilon) {
// not in the same plane
return Collections.emptyList();
} else {
// planes are equal
return handlePolygonsInSamePlane(plane1, p1, p2, epsilon);
}
}
// plane is intersecting
// handle embedded edges
List<PolygonPolygonIntersection> intersections = new ArrayList<>();
handleEmbeddedEdges(p1, p2, straight, intersections, epsilon, angleEpsilon);
List<Interval> intervals1 = calculateIntersectionIntervals2d(p1, p1, p2, straight, epsilon, angleEpsilon);
if (intervals1.isEmpty()) {
// no intersection
return intersections;
}
List<Interval> intervals2 = calculateIntersectionIntervals2d(p2, p1, p2, straight, epsilon, angleEpsilon);
if (intervals2.isEmpty()) {
// no intersection
return intersections;
}
createIntersectionResults(p1, p2, intervals1, intervals2, straight, intersections, epsilon);
return intersections;
}
private static void createIntersectionResults(EdgePolygon cpPolygon1, EdgePolygon cpPolygon2,
List<Interval> intervals1, List<Interval> intervals2, GmStraight straight,
List<PolygonPolygonIntersection> intersections, double epsilon) {
for (Interval in1 : intervals1) {
for (Interval in2 : intervals2) {
if (in1.isOverlapping(in2)) {
Interval overlap = in1.overlap(in2);
Point3d start = straight.evaluate(overlap.getStart());
if (overlap.getLength() < Global.getHighAccuracyTolerance() * 1e6) {
checkAndAddRealIntersectionPoint(cpPolygon1, cpPolygon2, start, intersections, epsilon);
} else {
Point3d end = straight.evaluate(overlap.getEnd());
PolyLine pPL = new PolyLine(new Coordinate3d(start), new Coordinate3d(end));
if (!isPolyLineASharedEdge(cpPolygon1, pPL)) {
PolygonPolygonIntersection pPPI = new PolygonPolygonIntersection(cpPolygon1, cpPolygon2,
pPL);
intersections.add(pPPI);
}
}
} else if (Math.abs(in1.getStart() - in2.getEnd()) < Global.getHighAccuracyTolerance()) {
// check if the overlaps with a tolerance (numeric errors)
Point3d point = straight.evaluate(in1.getStart());
checkAndAddRealIntersectionPoint(cpPolygon1, cpPolygon2, point, intersections, epsilon);
} else if (Math.abs(in1.getEnd() - in2.getStart()) < Global.getHighAccuracyTolerance()) {
Point3d point = straight.evaluate(in1.getEnd());
checkAndAddRealIntersectionPoint(cpPolygon1, cpPolygon2, point, intersections, epsilon);
}
}
}
}
private static boolean isPolyLineASharedEdge(EdgePolygon p1, PolyLine polyLine) {
// get coordinates of the poly line
Point3d polyLineStart = polyLine.getStart().getPoint();
Point3d polyLineEnd = polyLine.getEnd().getPoint();
// check if there exists a HalfEdge with same coordinates
List<HalfEdge> polyHEs = p1.getHalfEdges();
for (HalfEdge he : polyHEs) {
// only check half edges with partners. NOTE: we are just looking
// for half edges, shared by both polygons!
if (he.getPartner() != null) {
Point3d start = he.getStart().getPoint();
Point3d end = he.getEnd().getPoint();
if ((start.isAlmostEqual(polyLineStart, 1e-6) && end.isAlmostEqual(polyLineEnd, 1e-6))
|| (start.isAlmostEqual(polyLineEnd, 1e-6) && end.isAlmostEqual(polyLineStart, 1e-6))) {
// current half edge has a partner and the same start and
// end points of the polyline -> intersection is the edge
// which is shared by both polygons
return true;
}
}
}
return false;
}
/**
*
* Checks if the given point is a "real" intersection point and add's it to the
* intersection segments member variable. Real means, that this point isn't
* shared by both polygons as corner point.
*
* @param point A possible intersection point
*/
private static void checkAndAddRealIntersectionPoint(EdgePolygon p1, EdgePolygon p2, Point3d point,
List<PolygonPolygonIntersection> intersections, double epsilon) {
// check if both polygons have this point in common
if (!sharingBothPolygonsThisPoint(p1, p2, point, epsilon)) {
PolyLine pPL = new PolyLine(new Coordinate3d(point), new Coordinate3d(point));
PolygonPolygonIntersection pPPI = new PolygonPolygonIntersection(p1, p2, pPL);
intersections.add(pPPI);
}
}
/**
*
* This method checks if the point is a border point of one of the polygons,
* which should be intersected. If this check is successfull, the method tries
* to find the same point in the other polygon.
*
* <br>
* Equality in this case means, both polygons sharing the same instance of
* corner point! Otherwise this is just a util method to purge the code.
*
* @param point A computed intersection point
*
* @return true, if the point is shared by the member variables, holding the
* given polygons
*
*/
private static boolean sharingBothPolygonsThisPoint(EdgePolygon p1, EdgePolygon p2, Point3d point, double epsilon) {
// search for point in one Polygon
Coordinate3d pointWithSameCoordinates = null;
List<HalfEdge> hl1 = p1.getHalfEdges();
for (HalfEdge he1 : hl1) {
Point3d currHEPoint = he1.getStart().getPoint();
if ((currHEPoint.minus(point)).getLength() < epsilon) {
pointWithSameCoordinates = he1.getStart();
}
}
boolean bothPolygonsSharingThisPoint = false;
// check if the second polygon shares the same point (same instance!!)
if (null != pointWithSameCoordinates) {
List<EdgePolygon> polygons = pointWithSameCoordinates.getPolygons();
for (EdgePolygon poly : polygons) {
if (p2 == poly) {
bothPolygonsSharingThisPoint = true;
break;
}
}
}
return bothPolygonsSharingThisPoint;
}
private static List<Interval> calculateIntersectionIntervals2d(EdgePolygon p, EdgePolygon cpPolygon1,
EdgePolygon cpPolygon2, GmStraight intersectingStraight, double epsilon, double angleEpsilon) {
List<Interval> intersectionIntervals = new ArrayList<>();
TreeSet<Double> intersectionValues = new TreeSet<>(Global.getDoubleTolCompare(epsilon));
TreeSet<Double> intersectedPolygonPoints = new TreeSet<>(Global.getDoubleTolCompare(epsilon));
GmPlane polyPlane = p.getPlane();
GmStraight2d intersectingStraight2d = polyPlane.projectOn2dStraight(intersectingStraight);
EdgePolygon otherPolygon;
if (cpPolygon1 == p) {
otherPolygon = cpPolygon2;
} else {
otherPolygon = cpPolygon1;
}
List<HalfEdge> he = p.getHalfEdges();
for (HalfEdge e : he) {
if (e.getPartner() != null && e.getPartner().getPolygon() == otherPolygon) {
// both polygons sharing this edge, no need to intersect it
// NOTE: it's assumed, that the polygons aren't self intersecting
continue;
}
GmBoundedStraight heStraight = e.getStraight();
// Straights are colinear; checked in the handleEmdeddedEdges method
if (heStraight.isColinear(intersectingStraight, angleEpsilon, epsilon)) {
continue;
}
// Check if the current HalfEdge shares the start or end coordinate with
// the other polygon
boolean currHESharesAPntWithTheOtherPolygon = isHalfEdgeSharingAPointWithOtherPolygon(otherPolygon,
e.getStart().getPolygons());
if (!currHESharesAPntWithTheOtherPolygon) {
List<EdgePolygon> endPolygons = e.getEnd().getPolygons();
currHESharesAPntWithTheOtherPolygon = isHalfEdgeSharingAPointWithOtherPolygon(otherPolygon,
endPolygons);
}
// check if the straight of the HalfEdge is collinear to the intersection
// straight; this might result in an embedded edge intersection result
// TODO MW: really needed? this check is also made some lines above
if (heStraight.isColinear(intersectingStraight, angleEpsilon, epsilon)) {
currHESharesAPntWithTheOtherPolygon = false;
}
// The current HalfEdge shares a point with the other polygon and is not
// collinear with the intersection straight. Hence, there is now way, that
// this HalfEdge can intersect the other one, because both are planar.
// if (currHESharesAPntWithTheOtherPolygon) {
// continue;
// }
// project straight on plane
GmBoundedStraight2d heStraight2d = polyPlane.projectOn2dStraight(heStraight);
GmStraight2dIntersectionResult intersectionResult = heStraight2d.intersect(intersectingStraight2d);
if (intersectionResult.areParallel()) {
Vector2d dir = intersectingStraight2d.getDirection();
Vector2d diffVec = heStraight2d.getOrigin().minus(intersectingStraight2d.getOrigin());
double diffVecDotPerpDir = diffVec.dot(dir.getPerpendicularVector());
if (Math.abs(diffVecDotPerpDir) < Global.getZeroAngleCosinus()) {
// Straights are identical
Point2d p1 = heStraight2d.getOrigin();
Point2d p2 = heStraight2d.getTarget();
Point2d orig = intersectingStraight2d.getOrigin();
double[] params = new double[2];
if (Math.abs(dir.getX()) > Global.getHighAccuracyTolerance()) {
params[0] = (p1.getX() - orig.getX()) / dir.getX();
params[1] = (p2.getX() - orig.getX()) / dir.getX();
} else if (Math.abs(dir.getY()) > Global.getHighAccuracyTolerance()) {
params[0] = (p1.getY() - orig.getY()) / dir.getY();
params[1] = (p2.getY() - orig.getY()) / dir.getY();
} else {
throw new IllegalStateException(
"Directional vector of identical straights is equal to the zero vector " + dir);
}
assignParameterToCorrectList(params[0], intersectionValues, intersectedPolygonPoints);
assignParameterToCorrectList(params[1], intersectionValues, intersectedPolygonPoints);
}
} else {
if (heStraight2d.isWithinBoundaries(intersectionResult.getParamHE(), 1e-9)) {
assignParameterToCorrectList(intersectionResult.getParamInt(), intersectionValues,
intersectedPolygonPoints);
}
}
}
processIntersectionIntervals(p, intersectingStraight, intersectionValues, intersectedPolygonPoints,
intersectionIntervals);
return intersectionIntervals;
}
/**
* This methods computes the intervals of the intersection between the given
* straight and polygon. It gets only parameters where the straight hits the
* polygon. <br>
* The additional set of intersected polygon points is necessary, because each
* intersection of a corner point with the straight results in the computation
* of the same intersection parameter twice, because at every corner point of
* the polygon two edges are joining. These points has to be treated special.
*
* @param pcPolygon A polygon
*
* @param intersectingStraight The straight which have been intersected with
* the given polygon
*
* @param intersectionValues The calculated parameters of the intersection
*
* @param intersectedPolygonPoints A set of values, containing parameter values
* where the straight intersects the polygon in
* a corner point.
*
* @param intersectionIntervals A list of computed intersection intervals of
* the straight with the polygon
*/
private static void processIntersectionIntervals(EdgePolygon pcPolygon, GmStraight intersectingStraight,
TreeSet<Double> intersectionValues, TreeSet<Double> intersectedPolygonPoints,
List<Interval> intersectionIntervals) {
// insert points as intervals with length 0
// insert a interval with zero length for every crossed point
for (Double d : intersectedPolygonPoints) {
intersectionIntervals.add(new Interval(d, d));
}
// create other intervals
// create list for easier random access
List<Double> valuesList = new ArrayList<>(intersectionValues);
for (int i = 0; i < valuesList.size(); i++) {
double i1 = valuesList.get(i);
i = i + 1;
if (i < valuesList.size()) {
double i2 = valuesList.get(i);
// check if the double values are corner points of the polygon
boolean gotPolygonPoint = false;
if (intersectedPolygonPoints.contains(i1) || intersectedPolygonPoints.contains(i2)) {
gotPolygonPoint = true;
}
if (gotPolygonPoint) {
// maybe an interval
// check if the point between the two parameters is inside the
// polygon or outside ( i.e. i1 and i2 are both corner points of
// a concave polygon, so the connection line don't need to lie
// inside the polygon )
Point3d pnt = intersectingStraight.evaluate((i1 + i2) / 2);
if (pcPolygon.isPointInsidePolygon(pnt, 1e-9)) {
Interval newLineInt = new Interval(i1, i2);
// there is already at least one point interval present. We need
// to remove this first, otherwise we would get a normal interval
// and one or two point intervals at the end of the normal int.
Iterator<Interval> intervalIterator = intersectionIntervals.iterator();
while (intervalIterator.hasNext() && !intersectionIntervals.isEmpty()) {
for (Interval inter = intervalIterator.next(); intervalIterator
.hasNext(); inter = intervalIterator.next()) {
if (Math.abs(inter.getLength()) < Global.getHighAccuracyTolerance()) {
if (Math.abs(inter.getStart() - i1) < 1e-9
|| Math.abs(inter.getStart() - i2) < 1e-9) {
intervalIterator.remove();
intervalIterator = intersectionIntervals.iterator();
break;
}
}
}
}
intersectionIntervals.add(newLineInt);
} else {
i--;
}
} else {
intersectionIntervals.add(new Interval(i1, i2));
}
}
// it's possible, that 'it' equals 'endIt' at the end of the loop.
// this would lead to an increment of an iterator pointing to the end
// iterator. don't ask me why, but incrementing such an iterator
// is forbidden and you get an assertion.
if (i >= valuesList.size()) {
break;
}
}
}
/**
* Util method. Checks if the given parameter hase been allready inserted as
* intersction parameter. If this is true, the parameter will be added as
* intersected polygon corner point. Otherwise it's just a normal intersection
* parameter till now.
*
* @param param Calculated intersection parameter
*
* @param intersectionValues Set of intersection parameters
*
* @param intersectedPolygonPoints Set of intersected polygon corner points
*
*/
private static void assignParameterToCorrectList(double param, TreeSet<Double> intersectionValues,
TreeSet<Double> intersectedPolygonPoints) {
if (!intersectionValues.add(param)) {
// value is allready present ==> this must be a point of the polygon
// note, it's assumed, that the polygon do not intersect itself
intersectedPolygonPoints.add(param);
}
}
private static boolean isHalfEdgeSharingAPointWithOtherPolygon(EdgePolygon otherPolygon, List<EdgePolygon> polys) {
boolean currHESharesAPntWithTheOtherPolygon = false;
for (EdgePolygon polyIt : polys) {
if (polyIt == otherPolygon) {
currHESharesAPntWithTheOtherPolygon = true;
break;
}
}
return currHESharesAPntWithTheOtherPolygon;
}
private static void handleEmbeddedEdges(EdgePolygon p1, EdgePolygon p2, GmStraight straight,
List<PolygonPolygonIntersection> intersections, double epsilon, double angleEpsilon) {
// merge both lists
List<HalfEdge> heList = new ArrayList<>(p1.getHalfEdges());
heList.addAll(p2.getHalfEdges());
List<HalfEdge> heListColinear = new ArrayList<>();
for (HalfEdge he : heList) {
if (null != he.getPartner()) {
// this half edge is shared by both polygons ==> no intersection
// NOTE: The issue of more than 2 half edges per edge is solved by
// circular pointer, so we have to cycle threw all partners
HalfEdge pStartHE = he;
HalfEdge pPartnerHE = pStartHE.getPartner();
boolean bothPolysShareThisEdge = false;
while (pStartHE != pPartnerHE && false == bothPolysShareThisEdge) {
if (pPartnerHE.getPolygon() == p1 || pPartnerHE.getPolygon() == p2) {
bothPolysShareThisEdge = true;
}
pPartnerHE = pPartnerHE.getPartner();
}
if (bothPolysShareThisEdge) {
continue;
}
}
GmBoundedStraight straightHe = he.getStraight();
Point3d origin = straightHe.getOrigin();
Point3d target = straightHe.getTarget();
boolean straightsAreColinear = IntersectPlanarPolygons.areStraightsColinear(straightHe, straight, epsilon,
angleEpsilon);
ProjectedPoint3d projPoint1 = straight.project(origin);
ProjectedPoint3d projPoint2 = straight.project(target);
boolean originLiesOnIntStraight = projPoint1.getPoint().isAlmostEqual(origin,
PROJECTED_POINT_DISTANCE_EPSILON);
boolean targetLiesOnIntStraight = projPoint2.getPoint().isAlmostEqual(target,
PROJECTED_POINT_DISTANCE_EPSILON);
if (straightsAreColinear && (originLiesOnIntStraight || targetLiesOnIntStraight)) {
heListColinear.add(he);
}
}
for (HalfEdge he : heListColinear) {
// 1.2) determine if fully or partially or not at all embedded
// create parameter interval of the first projected half edge
Point3d startPoint1 = he.getStraight().getOrigin();
Point3d endPoint1 = he.getStraight().getTarget();
ProjectedPoint3d projP1 = straight.project(startPoint1);
ProjectedPoint3d projP2 = straight.project(endPoint1);
Interval int1 = new Interval(projP1.getParameter(), projP2.getParameter());
for (HalfEdge he2 : heListColinear) {
if (he == he2) {
continue;
}
Point3d startPoint2 = he2.getStraight().getOrigin();
Point3d endPoint2 = he2.getStraight().getTarget();
ProjectedPoint3d proj2P1 = straight.project(startPoint2);
ProjectedPoint3d proj2P2 = straight.project(endPoint2);
Interval int2 = new Interval(proj2P1.getParameter(), proj2P2.getParameter());
Interval intOverlap = int1.overlap(int2);
double intOverlapLength = intOverlap.getLength();
if (intOverlapLength < Global.getHighAccuracyTolerance()) {
// no overlap
continue;
}
Coordinate3d polyLineStart = new Coordinate3d(straight.evaluate(intOverlap.getStart()));
Coordinate3d polyLineEnd = new Coordinate3d(straight.evaluate(intOverlap.getEnd()));
PolyLine polyLine = new PolyLine(polyLineStart, polyLineEnd);
double int1Length = int1.getLength();
double int2Length = int2.getLength();
IntersectionType type;
if (Math.abs(intOverlapLength - int1Length) < FULLY_EMBEDDED_TOLERANCE
|| Math.abs(intOverlapLength - int2Length) < FULLY_EMBEDDED_TOLERANCE) {
// fully embedded
type = IntersectionType.FULLY_EMBEDDED_EDGE;
} else {
type = IntersectionType.PARTIALLY_EMBEDDED_EDGE;
}
PolygonPolygonIntersection intersection = new PolygonPolygonIntersection(p1, p2, polyLine, type);
intersections.add(intersection);
}
}
}
private static boolean areStraightsColinear(GmBoundedStraight straight1, GmStraight straight2, double epsilon,
double angleEpsilon) {
UnitVector3d rDir1 = straight1.getDir();
UnitVector3d rDir2 = straight2.getDir();
if ((!Vector3d.areParallel(rDir1, rDir2, angleEpsilon))
&& (!Vector3d.areAntiParallel(rDir1, rDir2, angleEpsilon))) {
return false;
}
Point3d rOrigin = straight1.getOrigin();
ProjectedPoint3d foot1 = straight2.project(rOrigin);
Point3d rTarget = straight1.getTarget();
ProjectedPoint3d foot2 = straight2.project(rTarget);
if ((foot1.getPoint().minus(rOrigin)).getLength() > epsilon
|| (foot2.getPoint().minus(rTarget)).getLength() > epsilon) {
return false;
}
return true;
}
private static List<PolygonPolygonIntersection> handlePolygonsInSamePlane(GmPlane plane, EdgePolygon p1,
EdgePolygon p2, double epsilon) {
Map<Coordinate3d, Coordinate2d> coordMap = new IdentityHashMap<>();
List<Coordinate2d> poly2dCoords1 = new ArrayList<>();
List<Coordinate3d> polyCoords1 = p1.getCoordinates();
for (Coordinate3d c : polyCoords1) {
Point2d proj = plane.project(c.getPoint());
Coordinate2d pc2d = new Coordinate2d(proj);
poly2dCoords1.add(pc2d);
coordMap.put(c, pc2d);
}
List<Coordinate2d> poly2dCoords2 = new ArrayList<>();
List<Coordinate3d> polyCoords2 = p2.getCoordinates();
for (Coordinate3d c : polyCoords2) {
Coordinate2d pc2d = coordMap.get(c);
if (pc2d == null) {
Point2d proj = plane.project(c.getPoint());
pc2d = new Coordinate2d(proj);
}
poly2dCoords2.add(pc2d);
}
Polygon2d poly2d1 = new Polygon2dNs(poly2dCoords1, true);
Polygon2d poly2d2 = new Polygon2dNs(poly2dCoords2, true);
List<PolygonPolygonIntersection> result = new ArrayList<>();
List<Polygon2dPolygon2dInt> ppi2ds = IntersectPolygon2d.getIntersections(poly2d1, poly2d2, epsilon);
for (Polygon2dPolygon2dInt ppi2d : ppi2ds) {
PolyLine2d pl2d = ppi2d.getPolyLine2d();
List<Coordinate2d> coords2d = pl2d.getCoordinates();
List<Coordinate3d> coords3d = new ArrayList<>();
for (Coordinate2d coord2d : coords2d) {
Point3d pnt3d = plane.evaluate(coord2d.getPoint());
coords3d.add(new Coordinate3d(pnt3d));
}
PolyLine pl = new PolyLine(coords3d, false);
PolygonPolygonIntersection ppi = new PolygonPolygonIntersection(p1, p2, pl, ppi2d.getIntType());
result.add(ppi);
}
return result;
}
private IntersectPlanarPolygons() {
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment