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

CityDoctor2 validation open source release

Showing with 2150 additions and 0 deletions
+2150 -0
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() {
}
}
Supports Markdown
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