Commit 97a65114 authored by Riegel's avatar Riegel
Browse files

Merge branch 'dev' into 'master'

Version 3.15.0

See merge request !8
parents 99c8f6a8 5950ea5f
Pipeline #10106 passed with stage
in 3 minutes and 15 seconds
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List; import java.util.ArrayList;
import java.util.Map; import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.math.MovedPolygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.MovedRing; import de.hft.stuttgart.citydoctor2.math.MovedPolygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.MovedRing;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class CDPolygonNs extends PolygonNs {
public class CDPolygonNs extends PolygonNs {
private List<List<HalfEdge>> innerHalfEdges = new ArrayList<>();
private final List<List<HalfEdge>> innerHalfEdges = new ArrayList<>();
public static CDPolygonNs of(Polygon p, Map<Vector3d, Coordinate3d> pointMap) {
List<List<Coordinate3d>> loopCoordinates = new ArrayList<>(); public static CDPolygonNs of(Polygon p, Map<Vector3d, Coordinate3d> pointMap) {
List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, p.getExteriorRing().getVertices()); List<List<Coordinate3d>> loopCoordinates = new ArrayList<>();
loopCoordinates.add(edgeExtRing); List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, p.getExteriorRing().getVertices());
loopCoordinates.add(edgeExtRing);
for (LinearRing innerRing : p.getInnerRings()) {
List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices()); for (LinearRing innerRing : p.getInnerRings()) {
loopCoordinates.add(edgeInnerRing); List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices());
} loopCoordinates.add(edgeInnerRing);
}
List<List<HalfEdge>> halfEdges = new ArrayList<>();
for (List<Coordinate3d> ringCoordinates : loopCoordinates) { List<List<HalfEdge>> halfEdges = new ArrayList<>();
List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates); for (List<Coordinate3d> ringCoordinates : loopCoordinates) {
halfEdges.add(currHeList); List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates);
} halfEdges.add(currHeList);
}
return new CDPolygonNs(halfEdges, p);
} return new CDPolygonNs(halfEdges, p);
}
public static CDPolygonNs of(MovedPolygon mp, Map<Vector3d, Coordinate3d> pointMap) {
List<List<Coordinate3d>> loopCoordinates = new ArrayList<>(); public static CDPolygonNs of(MovedPolygon mp, Map<Vector3d, Coordinate3d> pointMap) {
List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, mp.getExteriorRing().getVertices()); List<List<Coordinate3d>> loopCoordinates = new ArrayList<>();
loopCoordinates.add(edgeExtRing); List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, mp.getExteriorRing().getVertices());
loopCoordinates.add(edgeExtRing);
for (MovedRing innerRing : mp.getInnerRings()) {
List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices()); for (MovedRing innerRing : mp.getInnerRings()) {
loopCoordinates.add(edgeInnerRing); List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices());
} loopCoordinates.add(edgeInnerRing);
}
List<List<HalfEdge>> halfEdges = new ArrayList<>();
for (List<Coordinate3d> ringCoordinates : loopCoordinates) { List<List<HalfEdge>> halfEdges = new ArrayList<>();
List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates); for (List<Coordinate3d> ringCoordinates : loopCoordinates) {
halfEdges.add(currHeList); List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates);
} halfEdges.add(currHeList);
}
return new CDPolygonNs(halfEdges, mp.getOriginal());
} return new CDPolygonNs(halfEdges, mp.getOriginal());
}
private static List<HalfEdge> createHalfEdgesFromCoordinates(List<Coordinate3d> ringCoordinates) {
List<HalfEdge> currHeList = new ArrayList<>(); private static List<HalfEdge> createHalfEdgesFromCoordinates(List<Coordinate3d> ringCoordinates) {
HalfEdge prevHalfEdge = null; List<HalfEdge> currHeList = new ArrayList<>();
for (int currCoordIndex = 1; currCoordIndex < ringCoordinates.size(); currCoordIndex++) { HalfEdge prevHalfEdge = null;
int prevCoordIndex = currCoordIndex - 1; for (int currCoordIndex = 1; currCoordIndex < ringCoordinates.size(); currCoordIndex++) {
Coordinate3d currCoord = ringCoordinates.get(currCoordIndex); int prevCoordIndex = currCoordIndex - 1;
Coordinate3d prevCoord = ringCoordinates.get(prevCoordIndex); Coordinate3d currCoord = ringCoordinates.get(currCoordIndex);
HalfEdge e = new HalfEdge(prevCoord, currCoord); Coordinate3d prevCoord = ringCoordinates.get(prevCoordIndex);
if (prevHalfEdge != null) { HalfEdge e = new HalfEdge(prevCoord, currCoord);
prevHalfEdge.setNext(e); if (prevHalfEdge != null) {
} prevHalfEdge.setNext(e);
currHeList.add(e); }
prevHalfEdge = e; currHeList.add(e);
} prevHalfEdge = e;
if (prevHalfEdge == null) { }
throw new IllegalStateException("No half edges were created"); if (prevHalfEdge == null) {
} throw new IllegalStateException("No half edges were created");
Coordinate3d start = ringCoordinates.get(0); }
Coordinate3d end = ringCoordinates.get(ringCoordinates.size() - 1); Coordinate3d start = ringCoordinates.get(0);
HalfEdge e = new HalfEdge(end, start); Coordinate3d end = ringCoordinates.get(ringCoordinates.size() - 1);
prevHalfEdge.setNext(e); HalfEdge e = new HalfEdge(end, start);
e.setNext(currHeList.get(0)); prevHalfEdge.setNext(e);
currHeList.add(e); e.setNext(currHeList.get(0));
return currHeList; currHeList.add(e);
} return currHeList;
}
private static List<Coordinate3d> createCoordinatesFromRing(Map<Vector3d, Coordinate3d> pointMap,
List<? extends Vector3d> vertices) { private static List<Coordinate3d> createCoordinatesFromRing(Map<Vector3d, Coordinate3d> pointMap,
List<Coordinate3d> edgeRing = new ArrayList<>(); List<? extends Vector3d> vertices) {
for (int i = 0; i < vertices.size() - 1; i++) { List<Coordinate3d> edgeRing = new ArrayList<>();
Vector3d v = vertices.get(i); for (int i = 0; i < vertices.size() - 1; i++) {
Coordinate3d c = pointMap.computeIfAbsent(v, key -> new Coordinate3d(key.getX(), key.getY(), key.getZ())); Vector3d v = vertices.get(i);
edgeRing.add(c); Coordinate3d c = pointMap.computeIfAbsent(v, key -> new Coordinate3d(key.getX(), key.getY(), key.getZ()));
} edgeRing.add(c);
return edgeRing; }
} return edgeRing;
}
public CDPolygonNs(List<List<HalfEdge>> halfEdges, Polygon original) {
super(halfEdges.get(0), original); public CDPolygonNs(List<List<HalfEdge>> halfEdges, Polygon original) {
super(halfEdges.get(0), original);
for (int i = 1; i < halfEdges.size(); i++) {
List<HalfEdge> loopEdges = halfEdges.get(i); for (int i = 1; i < halfEdges.size(); i++) {
for (HalfEdge e : loopEdges) { List<HalfEdge> loopEdges = halfEdges.get(i);
addChild(e); for (HalfEdge e : loopEdges) {
} addChild(e);
innerHalfEdges.add(loopEdges); }
} innerHalfEdges.add(loopEdges);
} }
}
public List<List<HalfEdge>> getInnerHalfEdges() {
return innerHalfEdges; public List<List<HalfEdge>> getInnerHalfEdges() {
} return innerHalfEdges;
}
}
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.HashSet; import java.util.ArrayList;
import java.util.List; import java.util.HashSet;
import java.util.Set; import java.util.List;
import java.util.Set;
public class Coordinate3d extends BaseEntity {
public class Coordinate3d extends BaseEntity {
private Point3d point;
private final Point3d point;
public Coordinate3d(Point3d point) {
this.point = point; public Coordinate3d(Point3d point) {
} this.point = point;
}
public Coordinate3d(double x, double y, double z) {
this(new Point3d(x, y, z)); public Coordinate3d(double x, double y, double z) {
} this(new Point3d(x, y, z));
}
public Point3d getPoint() {
return point; public Point3d getPoint() {
} return point;
}
public List<EdgePolygon> getPolygons() {
List<HalfEdge> halfEdges = getParents(HalfEdge.class); public List<EdgePolygon> getPolygons() {
List<HalfEdge> halfEdges = getParents(HalfEdge.class);
Set<EdgePolygon> polygons = new HashSet<>();
for (HalfEdge he : halfEdges) { Set<EdgePolygon> polygons = new HashSet<>();
List<EdgePolygon> localPolygons = he.getParents(EdgePolygon.class); for (HalfEdge he : halfEdges) {
polygons.addAll(localPolygons); List<EdgePolygon> localPolygons = he.getParents(EdgePolygon.class);
} polygons.addAll(localPolygons);
}
return new ArrayList<>(polygons);
} return new ArrayList<>(polygons);
}
@Override
public String toString() { @Override
return "Coordinate3d [point=" + point + "]"; public String toString() {
} return "Coordinate3d [point=" + point + "]";
}
}
}
...@@ -210,7 +210,84 @@ public class DebugUtils { ...@@ -210,7 +210,84 @@ public class DebugUtils {
System.out.println("CDPolygonNs* p" + counter + " = new CDPolygonNs(coords" + firstListCounter + ");"); System.out.println("CDPolygonNs* p" + counter + " = new CDPolygonNs(coords" + firstListCounter + ");");
return counterArr[0]; return counterArr[0];
} }
public static void printFPSelfIntersectionTestCaseCode(PolygonPolygonIntersection... intersectionResults){
for (PolygonPolygonIntersection intersection : intersectionResults) {
printFPSelfIntersectionTestCaseCode(intersection.getPolygon1(), intersection.getPolygon2());
}
}
public static void printFPSelfIntersectionTestCaseCode(EdgePolygon polygon1, EdgePolygon polygon2) {
Locale.setDefault(Locale.US);
System.out.println("-----------------------------------------------------");
System.out.println("");
System.out.println("@Test");
System.out.println("public void testFalsePositiveCASENAME() {");
printTabbedLine("Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);");
System.out.println("");
printTestCasePolygonCode(polygon1, polygon2);
printTabbedLine("CDPolygonNs edgePoly1 = CDPolygonNs.of(p1, new HashMap<>());");
printTabbedLine("CDPolygonNs edgePoly2 = CDPolygonNs.of(p2, new HashMap<>());");
printTabbedLine("List<PolygonPolygonIntersection> intersectPolygons;");
printTabbedLine("intersectPolygons = IntersectPlanarPolygons.intersectPolygons(edgePoly1, edgePoly2, 0.000001, 0.001);");
printTabbedLine("assertNotNull(intersectPolygons);");
printTabbedLine("assertTrue(intersectPolygons.isEmpty());");
System.out.println("");
printTabbedLine("intersectPolygons = IntersectPlanarPolygons.intersectPolygons(edgePoly2, edgePoly1, 0.000001, 0.001);");
printTabbedLine("assertNotNull(intersectPolygons);");
printTabbedLine("assertTrue(intersectPolygons.isEmpty());");
System.out.println("}");
}
private static void printTabbedLine(String line){
Locale.setDefault(Locale.US);
String format = "\t%s\n";
System.out.format(format, line);
}
private static void printTestCasePolygonCode(EdgePolygon polygon1, EdgePolygon polygon2) {
printTabbedLine("Polygon p1 = new ConcretePolygon();");
printTabbedLine("geom.addPolygon(p1);");
printTabbedLine("LinearRing ext = new LinearRing(LinearRingType.EXTERIOR);");
printTabbedLine("p1.setExteriorRing(ext);");
printTabbedLine("List<Vertex> vertices= new ArrayList<>();");
printTestCasePolygonVertices(polygon1);
System.out.println("");
printTabbedLine("Polygon p2 = new ConcretePolygon();");
printTabbedLine("geom.addPolygon(p2);");
printTabbedLine("ext = new LinearRing(LinearRingType.EXTERIOR);");
printTabbedLine("p2.setExteriorRing(ext);");
printTabbedLine("vertices= new ArrayList<>();");
printTestCasePolygonVertices(polygon2);
System.out.println("");
}
private static void printTestCasePolygonVertices(EdgePolygon polygon) {
List<Coordinate3d> coordList = polygon.getCoordinates();
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(30);
String vertexFormat = "vertices.add(new Vertex(%s,%s,%s));";
for (Coordinate3d coordinate3d : coordList) {
Point3d point = coordinate3d.getPoint();
printTabbedLine(String.format(vertexFormat,
nf.format(point.getX()), nf.format(point.getY()), nf.format(point.getZ())));
}
// close ring
Point3d point = coordList.get(0).getPoint();
printTabbedLine(String.format(vertexFormat,point.getX(),point.getY(),point.getZ()));
printTabbedLine("ext.addAllVertices(vertices);");
}
public static int printCityDoctorPolygons(Polygon... polygons) { public static int printCityDoctorPolygons(Polygon... polygons) {
Locale.setDefault(Locale.US); Locale.setDefault(Locale.US);
NumberFormat nf = NumberFormat.getNumberInstance(); NumberFormat nf = NumberFormat.getNumberInstance();
......
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List; import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
public class EdgePolygon extends BaseEntity {
public class EdgePolygon extends BaseEntity {
private Polygon original;
private List<HalfEdge> halfEdges; private final Polygon original;
private final List<HalfEdge> halfEdges;
public EdgePolygon(List<HalfEdge> halfEdges, Polygon original) {
for (HalfEdge he : halfEdges) { public EdgePolygon(List<HalfEdge> halfEdges, Polygon original) {
addChild(he); for (HalfEdge he : halfEdges) {
} addChild(he);
this.halfEdges = halfEdges; }
this.original = original; this.halfEdges = halfEdges;
} this.original = original;
}
public Polygon getOriginal() {
return original; public Polygon getOriginal() {
} return original;
}
public GmPlane getPlane() {
Point3d midPoint = getMidPoint(); public GmPlane getPlane() {
Point3d midPoint = getMidPoint();
List<HalfEdge> children = getChildren(HalfEdge.class);
Vector3d averageNormalVector = new Vector3d(0.0, 0.0, 0.0); List<HalfEdge> children = getChildren(HalfEdge.class);
Vector3d averageNormalVector = new Vector3d(0.0, 0.0, 0.0);
for (HalfEdge he : children) {
for (HalfEdge he : children) {
Point3d start = he.getStart().getPoint();
Point3d end = he.getEnd().getPoint(); Point3d start = he.getStart().getPoint();
Point3d end = he.getEnd().getPoint();
Vector3d mid2Start = start.minus(midPoint);
Vector3d mid2End = end.minus(midPoint); Vector3d mid2Start = start.minus(midPoint);
Vector3d mid2End = end.minus(midPoint);
averageNormalVector = averageNormalVector.plus(mid2Start.cross(mid2End));
} averageNormalVector = averageNormalVector.plus(mid2Start.cross(mid2End));
}
UnitVector3d normalVector = averageNormalVector.toUnitVector();
return new GmPlane(midPoint, normalVector); UnitVector3d normalVector = averageNormalVector.toUnitVector();
} return new GmPlane(midPoint, normalVector);
}
private Point3d getMidPoint() {
Point3d midPoint = new Point3d(0, 0, 0); private Point3d getMidPoint() {
List<HalfEdge> children = getChildren(HalfEdge.class); Point3d midPoint = new Point3d(0, 0, 0);
List<HalfEdge> children = getChildren(HalfEdge.class);
for (HalfEdge he : children) {
midPoint = midPoint.plus(he.getStart().getPoint()); for (HalfEdge he : children) {
} midPoint = midPoint.plus(he.getStart().getPoint());
return (midPoint.div(children.size())); }
} return (midPoint.div(children.size()));
}
public List<HalfEdge> getHalfEdges() {
return halfEdges; public List<HalfEdge> getHalfEdges() {
} return halfEdges;
}
public List<Coordinate3d> getCoordinates() {
List<Coordinate3d> coords = new ArrayList<>(); public List<Coordinate3d> getCoordinates() {
for (HalfEdge he : halfEdges) { List<Coordinate3d> coords = new ArrayList<>();
coords.add(he.getStart()); for (HalfEdge he : halfEdges) {
} coords.add(he.getStart());
// HalfEdge firstHE = Objects.requireNonNull(getFirstHalfEdge()); }
// HalfEdge currHE = firstHE; return coords;
// do { }
// coords.add(currHE.getStart());
// currHE = currHE.getNext(); public HalfEdge getFirstHalfEdge() {
// } while (currHE != firstHE); if (!halfEdges.isEmpty()) {
return coords; return halfEdges.get(0);
} }
return null;
public HalfEdge getFirstHalfEdge() { }
if (!halfEdges.isEmpty()) {
return halfEdges.get(0); /**
} * Test whether the given point is lying inside the non-self-
return null; * 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, and that the point is
* Test whether the given point is lying in- or outside of the non self * lying in the same plane. The given point is projected on the plane of the polygon. If the point is lying on an edge
* intersecting, planar polygon. It's believed, that point and polygon are lying * of the polygon, it is considered to be inside.
* in the same plane. <br> *
* <br> * @param rcPoint The point that should be checked
* 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 * @return true, if the point is inside the polygon, false otherwise
* 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 public boolean isPointInsidePolygon(Point3d rcPoint, double eps) {
* // Project the coordinate of the point on the plane
* @param rcPoint The point that should be checked GmPlane plane = getPlane();
* Point2d projectedPoint = plane.project(rcPoint);
* @return true, if the point is inside the polygon, false otherwise Polygon2d pP = plane.projectOn2dPolygon(this);
* return pP.isPointInsidePolygon(projectedPoint, eps);
*/ }
public boolean isPointInsidePolygon(Point3d rcPoint, double eps) {
// Project the coordinate of the point on the plane public int getNrHalfEdges() {
GmPlane plane = getPlane(); return halfEdges.size();
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 * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.Comparator;
import java.util.Comparator;
public class Global {
public class Global {
private static final double DBL_EPSILON = 2.2204460492503131e-16;
private static final double DBL_EPSILON = 2.2204460492503131e-16;
private static double mZeroAngleCosinus = 1.0e-9;
private static double mTolVectorsParallel = 1e-9; private static final double M_ZERO_ANGLE_COSINE = 1.0e-9;
private static double mHighAccuracyTol = DBL_EPSILON * 5; private static final double M_TOL_VECTORS_PARALLEL = 1e-9;
private static double mTolPointsEqual = 1e-3; private static final double M_HIGH_ACCURACY_TOL = DBL_EPSILON * 5;
private static final double M_TOL_POINTS_EQUAL = 1e-3;
private Global() {
} private Global() {
}
public static double getTolPointsEquals() {
return mTolPointsEqual; public static double getTolPointsEquals() {
} return M_TOL_POINTS_EQUAL;
}
public static double getHighAccuracyTolerance() {
return mHighAccuracyTol; public static double getHighAccuracyTolerance() {
} return M_HIGH_ACCURACY_TOL;
}
public static Comparator<Double> getDoubleTolCompare(double epsilon) {
return (v1, v2) -> { public static Comparator<Double> getDoubleTolCompare(double epsilon) {
double dif = v1 - v2; return (v1, v2) -> {
if (Math.abs(dif) < epsilon) { double dif = v1 - v2;
return 0; if (Math.abs(dif) < epsilon) {
} return 0;
return Double.compare(v1, v2); }
}; return Double.compare(v1, v2);
} };
}
public static double getZeroAngleCosinus() {
return mZeroAngleCosinus; public static double getZeroAngleCosine() { return M_ZERO_ANGLE_COSINE; }
}
public static double getTolVectorsParallel() {
public static double getTolVectorsParallel() { return M_TOL_VECTORS_PARALLEL;
return mTolVectorsParallel; }
}
}
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class GmBoundedStraight2d extends GmStraight2d {
public class GmBoundedStraight2d extends GmStraight2d {
private double length;
private Point2d target; private double length;
private Point2d target;
public static GmBoundedStraight2d of(Point2d start, Vector2d dir) {
GmBoundedStraight2d straight = new GmBoundedStraight2d(start, dir); public static GmBoundedStraight2d of(Point2d start, Vector2d dir) {
straight.target = straight.getOrigin().plus(dir); GmBoundedStraight2d straight = new GmBoundedStraight2d(start, dir);
straight.length = dir.getLength(); straight.target = straight.getOrigin().plus(dir);
return straight; straight.length = dir.getLength();
} return straight;
}
public static GmBoundedStraight2d of(Point2d from, Point2d to) {
Vector2d dir = to.minus(from); public static GmBoundedStraight2d of(Point2d from, Point2d to) {
GmBoundedStraight2d straight = new GmBoundedStraight2d(from, dir); Vector2d dir = to.minus(from);
straight.target = to; GmBoundedStraight2d straight = new GmBoundedStraight2d(from, dir);
straight.length = dir.getLength(); straight.target = to;
return straight; straight.length = dir.getLength();
} return straight;
}
private GmBoundedStraight2d(Point2d start, Vector2d dir) {
super(start, dir); private GmBoundedStraight2d(Point2d start, Vector2d dir) {
} super(start, dir);
}
public Point2d getTarget() {
return target; public Point2d getTarget() {
} return target;
}
public boolean isWithinBoundaries(double parameter, double eps) {
if (Math.abs(parameter) < eps) { public boolean isWithinBoundaries(double parameter, double eps) {
parameter = 0.0; if (Math.abs(parameter) < eps) {
} parameter = 0.0;
if (Math.abs(parameter - length) < eps) { }
parameter = length; if (Math.abs(parameter - length) < eps) {
} parameter = length;
}
if (0 <= parameter && parameter <= length) {
return true; return 0 <= parameter && parameter <= length;
} }
return false;
} public double getLength() {
return length;
public double getLength() { }
return length;
} public boolean isWithinBoundaries(double parameter) {
return isWithinBoundaries(parameter, Global.getHighAccuracyTolerance());
public boolean isWithinBoundaries(double parameter) { }
return isWithinBoundaries(parameter, Global.getHighAccuracyTolerance());
} }
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List; import java.util.ArrayList;
import java.util.List;
public class GmPlane {
public class GmPlane {
private Point3d x0;
private final Point3d x0;
private UnitVector3d r1;
private UnitVector3d r2; private final UnitVector3d r1;
private final UnitVector3d r2;
private UnitVector3d n;
private double d; private final UnitVector3d n;
private final double d;
public GmPlane(Point3d point, UnitVector3d normalVector) {
x0 = point; public GmPlane(Point3d point, UnitVector3d normalVector) {
n = normalVector; x0 = point;
d = new Vector3d(point).dot(n); n = normalVector;
d = new Vector3d(point).dot(n);
r1 = UnitVector3d.of(n.getZ(), n.getX(), n.getY());
r2 = n.cross(r1).toUnitVector(); UnitVector3d r = UnitVector3d.of(n.getZ(), n.getX(), n.getY());
r2 = n.cross(r).toUnitVector();
// r1 ist nicht in allen Faellen rechtwinklig zur Flaechennormalen
// daher nochmal eine neu Berechnung;
// ---------------------------------------------------------------- // r is not always perpendicular to the plane's normal vector,
r1 = n.cross(r2).toUnitVector(); // recalculate using r2
// ----------------------------------------------------------------
} r1 = n.cross(r2).toUnitVector();
public double getDistance() { }
return d;
} public double getDistance() {
return d;
public Vector3d getNormal() { }
return n;
} public Vector3d getNormal() {
return n;
public Point3d evaluate(Point2d point) { }
return evaluate(point.getX(), point.getY());
} 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); private Point3d evaluate(double x, double y) {
return new Point3d(x0.plus(vectorU).plus(vectorV)); 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())); public GmStraight2d projectOn2dStraight(GmStraight crStraight) {
return new GmStraight2d(start, end); 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()); public GmBoundedStraight2d projectOn2dStraight(GmBoundedStraight crBoundedStraight) {
return GmBoundedStraight2d.of(start, end); 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); public Point2d project(Point3d crPoint) {
GmStraight firstAxis = new GmStraight(x0, r1);
double u = firstAxis.project(crPoint).getParameter(); GmStraight secondAxis = new GmStraight(x0, r2);
double v = secondAxis.project(crPoint).getParameter();
double u = firstAxis.project(crPoint).getParameter();
return new Point2d(u, v); double v = secondAxis.project(crPoint).getParameter();
}
return new Point2d(u, v);
public GmStraight planeIntersection(GmPlane other) { }
// get plane normals and distances
// ------------------------------- public GmStraight planeIntersection(GmPlane other) {
Vector3d normal1 = getNormal(); // get plane normals and distances
Vector3d normal2 = other.getNormal(); // -------------------------------
double dist1 = getDistance(); Vector3d normal1 = getNormal();
double dist2 = other.getDistance(); Vector3d normal2 = other.getNormal();
double dist1 = getDistance();
// planes (normal vectors) parallel? -> good bye double dist2 = other.getDistance();
// ---------------------------------------------
if (Vector3d.areParallel(normal1, normal2) || Vector3d.areAntiParallel(normal1, normal2)) { // planes (normal vectors) parallel? -> good bye
return null; // but rSuccess is false // ---------------------------------------------
} 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); // get direction and origin for straight
// -------------------------------------
Matrix3d matrix = new Matrix3d(); Vector3d dir = normal1.cross(normal2);
matrix.set(0, 0, normal1.getX()); Matrix3d matrix = new Matrix3d();
matrix.set(0, 1, normal1.getY());
matrix.set(0, 2, normal1.getZ()); matrix.set(0, 0, normal1.getX());
matrix.set(0, 1, normal1.getY());
matrix.set(1, 0, normal2.getX()); matrix.set(0, 2, normal1.getZ());
matrix.set(1, 1, normal2.getY());
matrix.set(1, 2, normal2.getZ()); matrix.set(1, 0, normal2.getX());
matrix.set(1, 1, normal2.getY());
matrix.set(2, 0, dir.getX()); matrix.set(1, 2, normal2.getZ());
matrix.set(2, 1, dir.getY());
matrix.set(2, 2, dir.getZ()); matrix.set(2, 0, dir.getX());
matrix.set(2, 1, dir.getY());
double[] b = new double[] { dist1, dist2, 0.0 }; matrix.set(2, 2, dir.getZ());
double[] org = new double[3];
matrix.gauss(b, org); double[] b = new double[] { dist1, dist2, 0.0 };
double[] org = new double[3];
return new GmStraight(new Point3d(org), dir); matrix.gauss(b, org);
}
return new GmStraight(new Point3d(org), dir);
public Polygon2d projectOn2dPolygon(EdgePolygon cpPolygon) { }
HalfEdge startHE = cpPolygon.getFirstHalfEdge();
HalfEdge currHE = startHE; public Polygon2d projectOn2dPolygon(EdgePolygon cpPolygon) {
int numHEs = cpPolygon.getNrHalfEdges(); HalfEdge startHE = cpPolygon.getFirstHalfEdge();
Coordinate2d[] coords = new Coordinate2d[numHEs]; HalfEdge currHE = startHE;
int i = 0; int numHEs = cpPolygon.getNrHalfEdges();
do { Coordinate2d[] coords = new Coordinate2d[numHEs];
Coordinate3d coord = currHE.getStart(); int i = 0;
Point2d p = project(coord.getPoint()); do {
coords[i++] = new Coordinate2d(p); Coordinate3d coord = currHE.getStart();
currHE = currHE.getNext(); Point2d p = project(coord.getPoint());
} while (currHE != startHE); coords[i++] = new Coordinate2d(p);
currHE = currHE.getNext();
List<HalfEdge2d> halfedges = new ArrayList<>(); } while (currHE != startHE);
for (i = 0; i < numHEs; ++i) {
HalfEdge2d he = new HalfEdge2d(coords[i], coords[(i + 1) % (numHEs)]); List<HalfEdge2d> halfedges = new ArrayList<>();
halfedges.add(he); for (i = 0; i < numHEs; ++i) {
} HalfEdge2d he = new HalfEdge2d(coords[i], coords[(i + 1) % (numHEs)]);
halfedges.add(he);
return new Polygon2d(halfedges); }
}
return new Polygon2d(halfedges);
public Point3d getPoint() { }
return x0;
} public Point3d getPoint() {
return x0;
} }
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight {
public class GmStraight {
private Point3d org;
private UnitVector3d dir; private final Point3d org;
private final UnitVector3d dir;
public GmStraight(Point3d org, Vector3d dir) {
this.org = org; public GmStraight(Point3d org, Vector3d dir) {
this.dir = dir.toUnitVector(); this.org = org;
} this.dir = dir.toUnitVector();
}
public GmStraight(Point3d org, UnitVector3d dir) {
this.org = org; public GmStraight(Point3d org, UnitVector3d dir) {
this.dir = dir; this.org = org;
} this.dir = dir;
}
public ProjectedPoint3d project(Point3d origin) {
Vector3d v2Origin = origin.minus(org); public ProjectedPoint3d project(Point3d origin) {
Vector3d v2Origin = origin.minus(org);
double length = v2Origin.getLength();
if (length < Global.getTolPointsEquals()) { double length = v2Origin.getLength();
return new ProjectedPoint3d(origin, 0.0); if (length < Global.getTolPointsEquals()) {
} return new ProjectedPoint3d(origin, 0.0);
}
double parameter = dir.dot(UnitVector3d.of(v2Origin)) * length;
Point3d foot = evaluate(parameter); double parameter = dir.dot(UnitVector3d.of(v2Origin)) * length;
return new ProjectedPoint3d(foot, parameter); Point3d foot = evaluate(parameter);
} return new ProjectedPoint3d(foot, parameter);
}
public Point3d evaluate(double param) {
return org.plus(dir.mult(param)); public Point3d evaluate(double param) {
} return org.plus(dir.mult(param));
}
public UnitVector3d getDir() {
return dir; public UnitVector3d getDir() {
} return dir;
}
public boolean isColinear(GmStraight straight2, double angleEpsilon, double epsilon) {
UnitVector3d rDir1 = getDir(); public boolean isCollinear(GmStraight straight2, double angleEpsilon, double epsilon) {
UnitVector3d rDir2 = straight2.getDir(); UnitVector3d rDir1 = getDir();
UnitVector3d rDir2 = straight2.getDir();
if ((!Vector3d.areParallel(rDir1, rDir2, angleEpsilon))
&& (!Vector3d.areAntiParallel(rDir1, rDir2, angleEpsilon))) { if ((!Vector3d.areParallel(rDir1, rDir2, angleEpsilon))
&& (!Vector3d.areAntiParallel(rDir1, rDir2, angleEpsilon))) {
return false;
} return false;
}
Point3d rOrigin1 = getOrigin();
Point3d rOrigin1 = getOrigin();
Point3d foot1 = straight2.project(rOrigin1).getPoint();
if ((foot1.minus(rOrigin1)).getLength() > epsilon) { Point3d foot1 = straight2.project(rOrigin1).getPoint();
return false; if ((foot1.minus(rOrigin1)).getLength() > epsilon) {
} return false;
}
Point3d rOrigin2 = straight2.getOrigin();
Point3d rOrigin2 = straight2.getOrigin();
Point3d foot2 = project(rOrigin2).getPoint();
if ((foot2.minus(rOrigin2)).getLength() > epsilon) { Point3d foot2 = project(rOrigin2).getPoint();
return false; return ((foot2.minus(rOrigin2)).getLength() <= epsilon);
} }
return true; public Point3d getOrigin() {
} return org;
}
public Point3d getOrigin() {
return org; }
}
}
...@@ -18,10 +18,12 @@ ...@@ -18,10 +18,12 @@
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight2d { public class GmStraight2d {
private Point2d origin; private final Point2d origin;
private UnitVector2d direction; private final UnitVector2d direction;
public GmStraight2d(Point2d org, UnitVector2d dir) { public GmStraight2d(Point2d org, UnitVector2d dir) {
this.direction = dir; this.direction = dir;
...@@ -39,14 +41,13 @@ public class GmStraight2d { ...@@ -39,14 +41,13 @@ public class GmStraight2d {
} }
/** /**
* Just intersects two GmStraights2d. * Intersects this straight with another straight.
*
* <br> * <br>
* <br> * <br>
* If the two straights are parallel the method will return false, so the user * If the two straights are parallel, areParallel in the returned IntersectionResult will be true.
* has to determine, if the straight are just parallel or identical * This method will not differentiate between parallel and identical straights.
* *
* @param other First Straight * @param other the other straight
* *
* @return intersection result * @return intersection result
*/ */
...@@ -60,7 +61,7 @@ public class GmStraight2d { ...@@ -60,7 +61,7 @@ public class GmStraight2d {
Vector2d r2Perpendicular = r2.getPerpendicularVector(); Vector2d r2Perpendicular = r2.getPerpendicularVector();
double diff = r1.dot(r2Perpendicular); double diff = r1.dot(r2Perpendicular);
if (Math.abs(diff) < Global.getZeroAngleCosinus()) { if (Math.abs(diff) < Global.getZeroAngleCosine()) {
return GmStraight2dIntersectionResult.parallel(this, other); return GmStraight2dIntersectionResult.parallel(this, other);
} else { } else {
double invR1DotPerpR2 = 1.0 / diff; double invR1DotPerpR2 = 1.0 / diff;
......
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class GmStraight2dIntersectionResult {
public record GmStraight2dIntersectionResult(double paramHE, double paramInt, GmStraight2d straightHE,
private double paramHE; GmStraight2d straightInt, boolean areParallel) {
private double paramInt;
public static GmStraight2dIntersectionResult parallel(GmStraight2d s1, GmStraight2d s2) {
private GmStraight2d straightHE; return new GmStraight2dIntersectionResult(0, 0, s1, s2, true);
private GmStraight2d straightInt; }
private boolean areParallel; public static GmStraight2dIntersectionResult intersecting(double paramHE, double paramInt, GmStraight2d straightHE,
GmStraight2d straightInt) {
public static GmStraight2dIntersectionResult parallel(GmStraight2d s1, GmStraight2d s2) { return new GmStraight2dIntersectionResult(paramHE, paramInt, straightHE, straightInt, false);
return new GmStraight2dIntersectionResult(0, 0, s1, s2, true); }
}
public static GmStraight2dIntersectionResult intersecting(double paramHE, double paramInt, GmStraight2d straightHE, }
GmStraight2d straightInt) {
return new GmStraight2dIntersectionResult(paramHE, paramInt, straightHE, straightInt, 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;
}
}
...@@ -108,7 +108,7 @@ public class HalfEdge extends BaseEntity { ...@@ -108,7 +108,7 @@ public class HalfEdge extends BaseEntity {
HalfEdge pNextPartner = this.partner; HalfEdge pNextPartner = this.partner;
while (pNextPartner != this) { while (pNextPartner != this) {
if (pNextPartner == partner) { if (pNextPartner == partner) {
logger.debug("(HalfEdge " + partner + " already exits in chain"); logger.debug(String.format("(HalfEdge %s already exits in chain", partner));
return; return;
} }
pNextPartner = pNextPartner.partner; pNextPartner = pNextPartner.partner;
...@@ -141,7 +141,7 @@ public class HalfEdge extends BaseEntity { ...@@ -141,7 +141,7 @@ public class HalfEdge extends BaseEntity {
pPreviousPartner.partner = this; pPreviousPartner.partner = this;
this.partner = partner; this.partner = partner;
} else { } else {
/** /*
* TODO : das riecht nach einer Ringverzeigerung : 3 Polygonraender treffen auf * TODO : das riecht nach einer Ringverzeigerung : 3 Polygonraender treffen auf
* einander * einander
*/ */
......
...@@ -22,8 +22,8 @@ import java.util.List; ...@@ -22,8 +22,8 @@ import java.util.List;
public class HalfEdge2d extends BaseEntity { public class HalfEdge2d extends BaseEntity {
private Coordinate2d start; private final Coordinate2d start;
private Coordinate2d end; private final Coordinate2d end;
private HalfEdge2d partner; private HalfEdge2d partner;
...@@ -71,7 +71,7 @@ public class HalfEdge2d extends BaseEntity { ...@@ -71,7 +71,7 @@ public class HalfEdge2d extends BaseEntity {
return pPartner; return pPartner;
} }
private HalfEdge2d setPartner(HalfEdge2d pPartner) { private void setPartner(HalfEdge2d pPartner) {
if (partner != null) { if (partner != null) {
if (pPartner != null) { if (pPartner != null) {
throw new IllegalStateException("cannot overwrite existing partner-connection"); throw new IllegalStateException("cannot overwrite existing partner-connection");
...@@ -92,7 +92,6 @@ public class HalfEdge2d extends BaseEntity { ...@@ -92,7 +92,6 @@ public class HalfEdge2d extends BaseEntity {
} }
} }
} }
return pPartner;
} }
public HalfEdge2d getPartner() { public HalfEdge2d getPartner() {
......
...@@ -82,20 +82,7 @@ public class IntersectPlanarPolygons { ...@@ -82,20 +82,7 @@ public class IntersectPlanarPolygons {
for (Interval in2 : intervals2) { for (Interval in2 : intervals2) {
if (in1.isOverlapping(in2)) { if (in1.isOverlapping(in2)) {
Interval overlap = in1.overlap(in2); Interval overlap = in1.overlap(in2);
Point3d start = straight.evaluate(overlap.getStart()); handleIntersectionIntervalOverlap(cpPolygon1, cpPolygon2, straight, intersections, epsilon, overlap);
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()) { } else if (Math.abs(in1.getStart() - in2.getEnd()) < Global.getHighAccuracyTolerance()) {
// check if the overlaps with a tolerance (numeric errors) // check if the overlaps with a tolerance (numeric errors)
Point3d point = straight.evaluate(in1.getStart()); Point3d point = straight.evaluate(in1.getStart());
...@@ -108,6 +95,25 @@ public class IntersectPlanarPolygons { ...@@ -108,6 +95,25 @@ public class IntersectPlanarPolygons {
} }
} }
private static void handleIntersectionIntervalOverlap(EdgePolygon cpPolygon1, EdgePolygon cpPolygon2,
GmStraight straight, List<PolygonPolygonIntersection> intersections, double epsilon, Interval overlap) {
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);
}
}
}
private static boolean isPolyLineASharedEdge(EdgePolygon p1, PolyLine polyLine) { private static boolean isPolyLineASharedEdge(EdgePolygon p1, PolyLine polyLine) {
// get coordinates of the poly line // get coordinates of the poly line
Point3d polyLineStart = polyLine.getStart().getPoint(); Point3d polyLineStart = polyLine.getStart().getPoint();
...@@ -136,17 +142,20 @@ public class IntersectPlanarPolygons { ...@@ -136,17 +142,20 @@ public class IntersectPlanarPolygons {
/** /**
* *
* Checks if the given point is a "real" intersection point and add's it to the * Checks if the given point is a 'real' intersection point and adds it to the
* intersection segments member variable. Real means, that this point isn't * intersection segments member variable. 'Real' meaning, that this point isn't
* shared by both polygons as corner point. * a shared corner point of both polygons.
* *
* @param p1 First polygon
* @param p2 Second polygon
* @param point A possible intersection point * @param point A possible intersection point
*/ */
private static void checkAndAddRealIntersectionPoint(EdgePolygon p1, EdgePolygon p2, Point3d point, private static void checkAndAddRealIntersectionPoint(EdgePolygon p1, EdgePolygon p2, Point3d point,
List<PolygonPolygonIntersection> intersections, double epsilon) { List<PolygonPolygonIntersection> intersections, double epsilon) {
// check if both polygons have this point in common // check if both polygons have this point in common
if (!sharingBothPolygonsThisPoint(p1, p2, point, epsilon)) { if (!sharingBothPolygonsThisPoint(p1, p2, point, epsilon)) {
PolyLine pPL = new PolyLine(new Coordinate3d(point), new Coordinate3d(point)); Coordinate3d realPointCoordinate = new Coordinate3d(point);
PolyLine pPL = new PolyLine(realPointCoordinate, realPointCoordinate);
PolygonPolygonIntersection pPPI = new PolygonPolygonIntersection(p1, p2, pPL); PolygonPolygonIntersection pPPI = new PolygonPolygonIntersection(p1, p2, pPL);
intersections.add(pPPI); intersections.add(pPPI);
} }
...@@ -200,7 +209,6 @@ public class IntersectPlanarPolygons { ...@@ -200,7 +209,6 @@ public class IntersectPlanarPolygons {
* with the exception, that the straight and the half edges of the polygon will * with the exception, that the straight and the half edges of the polygon will
* be projected on the plane of the polygon, so the calculation of the * be projected on the plane of the polygon, so the calculation of the
* intersection will be reduced to a 2d problem. * intersection will be reduced to a 2d problem.
*
* The intersection intervals contains pairs of parameters from the intersection * The intersection intervals contains pairs of parameters from the intersection
* straight. If an intersection between the straight and the polygon contains * straight. If an intersection between the straight and the polygon contains
* only a point, the according interval will contain the same parameter twice * only a point, the according interval will contain the same parameter twice
...@@ -239,7 +247,7 @@ public class IntersectPlanarPolygons { ...@@ -239,7 +247,7 @@ public class IntersectPlanarPolygons {
GmBoundedStraight heStraight = e.getStraight(); GmBoundedStraight heStraight = e.getStraight();
// Straights are colinear; checked in the handleEmdeddedEdges method // Straights are colinear; checked in the handleEmdeddedEdges method
if (heStraight.isColinear(intersectingStraight, angleEpsilon, epsilon)) { if (heStraight.isCollinear(intersectingStraight, angleEpsilon, epsilon)) {
continue; continue;
} }
...@@ -256,7 +264,7 @@ public class IntersectPlanarPolygons { ...@@ -256,7 +264,7 @@ public class IntersectPlanarPolygons {
// check if the straight of the HalfEdge is collinear to the intersection // check if the straight of the HalfEdge is collinear to the intersection
// straight; this might result in an embedded edge intersection result // straight; this might result in an embedded edge intersection result
// TODO MW: really needed? this check is also made some lines above // TODO MW: really needed? this check is also made some lines above
if (heStraight.isColinear(intersectingStraight, angleEpsilon, epsilon)) { if (heStraight.isCollinear(intersectingStraight, angleEpsilon, epsilon)) {
currHESharesAPntWithTheOtherPolygon = false; currHESharesAPntWithTheOtherPolygon = false;
} }
...@@ -271,39 +279,7 @@ public class IntersectPlanarPolygons { ...@@ -271,39 +279,7 @@ public class IntersectPlanarPolygons {
GmBoundedStraight2d heStraight2d = polyPlane.projectOn2dStraight(heStraight); GmBoundedStraight2d heStraight2d = polyPlane.projectOn2dStraight(heStraight);
GmStraight2dIntersectionResult intersectionResult = heStraight2d.intersect(intersectingStraight2d); GmStraight2dIntersectionResult intersectionResult = heStraight2d.intersect(intersectingStraight2d);
if (intersectionResult.areParallel()) { analyseProjectedIntersection(intersectionResult, intersectingStraight2d, heStraight2d, intersectionValues, intersectedPolygonPoints);
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, processIntersectionIntervals(p, intersectingStraight, intersectionValues, intersectedPolygonPoints,
...@@ -312,6 +288,42 @@ public class IntersectPlanarPolygons { ...@@ -312,6 +288,42 @@ public class IntersectPlanarPolygons {
} }
private static void analyseProjectedIntersection(GmStraight2dIntersectionResult intersectionResult, GmStraight2d intersectingStraight2d, GmBoundedStraight2d heStraight2d, TreeSet<Double> intersectionValues, TreeSet<Double> intersectedPolygonPoints) {
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.getZeroAngleCosine()) {
// 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.paramHE(), 1e-9)) {
assignParameterToCorrectList(intersectionResult.paramInt(), intersectionValues,
intersectedPolygonPoints);
}
}
}
/** /**
* This methods computes the intervals of the intersection between the given * This methods computes the intervals of the intersection between the given
* straight and polygon. It gets only parameters where the straight hits the * straight and polygon. It gets only parameters where the straight hits the
...@@ -350,45 +362,18 @@ public class IntersectPlanarPolygons { ...@@ -350,45 +362,18 @@ public class IntersectPlanarPolygons {
List<Double> valuesList = new ArrayList<>(intersectionValues); List<Double> valuesList = new ArrayList<>(intersectionValues);
for (int i = 0; i < valuesList.size(); i++) { for (int i = 0; i < valuesList.size(); i++) {
double i1 = valuesList.get(i); double i1 = valuesList.get(i);
i = i + 1; int j = i + 1;
if (i < valuesList.size()) { if (j < valuesList.size()) {
double i2 = valuesList.get(i); double i2 = valuesList.get(j);
// check if the double values are corner points of the polygon // check if the double values are corner points of the polygon
boolean gotPolygonPoint = false; boolean gotPolygonPoint = intersectedPolygonPoints.contains(i1) ||
if (intersectedPolygonPoints.contains(i1) || intersectedPolygonPoints.contains(i2)) { 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;
}
}
}
}
if (gotPolygonPoint) {
Interval newLineInt = checkIntersectionInvervalPoints(pcPolygon, intersectingStraight,
intersectionIntervals, i1, i2);
if (newLineInt != null) {
intersectionIntervals.add(newLineInt); intersectionIntervals.add(newLineInt);
} else {
i--;
} }
} else { } else {
intersectionIntervals.add(new Interval(i1, i2)); intersectionIntervals.add(new Interval(i1, i2));
...@@ -405,6 +390,37 @@ public class IntersectPlanarPolygons { ...@@ -405,6 +390,37 @@ public class IntersectPlanarPolygons {
} }
} }
private static Interval checkIntersectionInvervalPoints(EdgePolygon pcPolygon, GmStraight intersectingStraight, List<Interval> intersectionIntervals, double i1, double i2) {
// 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() &&
(Math.abs(inter.getStart() - i1) < 1e-9 || Math.abs(inter.getStart() - i2) < 1e-9)) {
intervalIterator.remove();
intervalIterator = intersectionIntervals.iterator();
break;
}
}
}
return newLineInt;
}
return null;
}
/** /**
* Util method. Checks if the given parameter has already been inserted as * Util method. Checks if the given parameter has already been inserted as
* intersection parameter. If this is true, the parameter will be added as * intersection parameter. If this is true, the parameter will be added as
...@@ -444,46 +460,9 @@ public class IntersectPlanarPolygons { ...@@ -444,46 +460,9 @@ public class IntersectPlanarPolygons {
List<HalfEdge> heList = new ArrayList<>(p1.getHalfEdges()); List<HalfEdge> heList = new ArrayList<>(p1.getHalfEdges());
heList.addAll(p2.getHalfEdges()); heList.addAll(p2.getHalfEdges());
List<HalfEdge> heListColinear = new ArrayList<>(); List<HalfEdge> heListCollinear = getCollinearHalfEdgeList(p1, p2, straight, epsilon, angleEpsilon, heList);
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 && !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) { for (HalfEdge he : heListCollinear) {
// 1.2) determine if fully or partially or not at all embedded // 1.2) determine if fully or partially or not at all embedded
// create parameter interval of the first projected half edge // create parameter interval of the first projected half edge
Point3d startPoint1 = he.getStraight().getOrigin(); Point3d startPoint1 = he.getStraight().getOrigin();
...@@ -491,7 +470,7 @@ public class IntersectPlanarPolygons { ...@@ -491,7 +470,7 @@ public class IntersectPlanarPolygons {
ProjectedPoint3d projP1 = straight.project(startPoint1); ProjectedPoint3d projP1 = straight.project(startPoint1);
ProjectedPoint3d projP2 = straight.project(endPoint1); ProjectedPoint3d projP2 = straight.project(endPoint1);
Interval int1 = new Interval(projP1.getParameter(), projP2.getParameter()); Interval int1 = new Interval(projP1.getParameter(), projP2.getParameter());
for (HalfEdge he2 : heListColinear) { for (HalfEdge he2 : heListCollinear) {
if (he == he2) { if (he == he2) {
continue; continue;
} }
...@@ -528,8 +507,63 @@ public class IntersectPlanarPolygons { ...@@ -528,8 +507,63 @@ public class IntersectPlanarPolygons {
} }
} }
private static boolean areStraightsColinear(GmBoundedStraight straight1, GmStraight straight2, double epsilon, private static List<HalfEdge> getCollinearHalfEdgeList(EdgePolygon p1, EdgePolygon p2, GmStraight straight, double epsilon, double angleEpsilon, List<HalfEdge> heList) {
double angleEpsilon) { List<HalfEdge> heListCollinear = new ArrayList<>();
for (HalfEdge he : heList) {
if (isHalfEdgeSharedByPolygons(he, p1, p2)){
// If the HalfEdge is shared, it is not collinear.
// Ignore it and continue with next HalfEdge.
continue;
}
GmBoundedStraight straightHe = he.getStraight();
Point3d origin = straightHe.getOrigin();
Point3d target = straightHe.getTarget();
boolean straightsAreCollinear = IntersectPlanarPolygons.areStraightsCollinear(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 (straightsAreCollinear && (originLiesOnIntStraight || targetLiesOnIntStraight)) {
heListCollinear.add(he);
}
}
return heListCollinear;
}
/**
* Checks whether a specific HalfEdge is shared by two EdgePolygons.
*
*
* @param he The HalfEdge in question
* @param p1 First Polygon
* @param p2 Second Polygon
* @return true if the HalfEdge is shared
*/
private static boolean isHalfEdgeSharedByPolygons(HalfEdge he, EdgePolygon p1, EdgePolygon p2) {
boolean bothPolysShareThisEdge = false;
if (he.getPartner() != null) {
// 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 pPartnerHE = he.getPartner();
while (he != pPartnerHE && !bothPolysShareThisEdge) {
if (pPartnerHE.getPolygon() == p1 || pPartnerHE.getPolygon() == p2) {
bothPolysShareThisEdge = true;
}
pPartnerHE = pPartnerHE.getPartner();
}
}
return bothPolysShareThisEdge;
}
private static boolean areStraightsCollinear(GmBoundedStraight straight1, GmStraight straight2, double epsilon,
double angleEpsilon) {
UnitVector3d rDir1 = straight1.getDir(); UnitVector3d rDir1 = straight1.getDir();
UnitVector3d rDir2 = straight2.getDir(); UnitVector3d rDir2 = straight2.getDir();
...@@ -543,12 +577,9 @@ public class IntersectPlanarPolygons { ...@@ -543,12 +577,9 @@ public class IntersectPlanarPolygons {
ProjectedPoint3d foot1 = straight2.project(rOrigin); ProjectedPoint3d foot1 = straight2.project(rOrigin);
Point3d rTarget = straight1.getTarget(); Point3d rTarget = straight1.getTarget();
ProjectedPoint3d foot2 = straight2.project(rTarget); ProjectedPoint3d foot2 = straight2.project(rTarget);
if ((foot1.getPoint().minus(rOrigin)).getLength() > epsilon return !(((foot1.getPoint().minus(rOrigin)).getLength() > epsilon)
|| (foot2.getPoint().minus(rTarget)).getLength() > epsilon) { || ((foot2.getPoint().minus(rTarget)).getLength() > epsilon));
return false; }
}
return true;
}
private static List<PolygonPolygonIntersection> handlePolygonsInSamePlane(GmPlane plane, EdgePolygon p1, private static List<PolygonPolygonIntersection> handlePolygonsInSamePlane(GmPlane plane, EdgePolygon p1,
EdgePolygon p2, double epsilon) { EdgePolygon p2, double epsilon) {
...@@ -576,15 +607,7 @@ public class IntersectPlanarPolygons { ...@@ -576,15 +607,7 @@ public class IntersectPlanarPolygons {
Polygon2d poly2d1 = new Polygon2dNs(poly2dCoords1, true); Polygon2d poly2d1 = new Polygon2dNs(poly2dCoords1, true);
Polygon2d poly2d2 = new Polygon2dNs(poly2dCoords2, true); Polygon2d poly2d2 = new Polygon2dNs(poly2dCoords2, true);
List<HalfEdge2d> halfEdges = poly2d1.getHalfEdges();
for (HalfEdge2d he : halfEdges) {
if (he.getPartner() == null) {
}
}
halfEdges = poly2d2.getHalfEdges();
List<PolygonPolygonIntersection> result = new ArrayList<>(); List<PolygonPolygonIntersection> result = new ArrayList<>();
List<Polygon2dPolygon2dInt> ppi2ds = IntersectPolygon2d.getIntersections(poly2d1, poly2d2, epsilon); List<Polygon2dPolygon2dInt> ppi2ds = IntersectPolygon2d.getIntersections(poly2d1, poly2d2, epsilon);
for (Polygon2dPolygon2dInt ppi2d : ppi2ds) { for (Polygon2dPolygon2dInt ppi2d : ppi2ds) {
......
...@@ -100,11 +100,11 @@ public class IntersectPolygonAndStraight2d { ...@@ -100,11 +100,11 @@ public class IntersectPolygonAndStraight2d {
assignParameterToCorrectList(params[1], intersectionValues, intersectedPolygonPoints); assignParameterToCorrectList(params[1], intersectionValues, intersectedPolygonPoints);
} }
} else { } else {
if (hEStraight.isWithinBoundaries(res.getParamHE())) { if (hEStraight.isWithinBoundaries(res.paramHE())) {
// Point2d pnt = mIntStraight.evaluate( paramInt ); // Point2d pnt = mIntStraight.evaluate( paramInt );
// cout << "got ( " << pnt.getU() << ", " << pnt.getV() << " ) as int point with // cout << "got ( " << pnt.getU() << ", " << pnt.getV() << " ) as int point with
// param " << paramInt << endl; // param " << paramInt << endl;
assignParameterToCorrectList(res.getParamInt(), intersectionValues, intersectedPolygonPoints); assignParameterToCorrectList(res.paramInt(), intersectionValues, intersectedPolygonPoints);
} }
} }
} }
...@@ -164,12 +164,10 @@ public class IntersectPolygonAndStraight2d { ...@@ -164,12 +164,10 @@ public class IntersectPolygonAndStraight2d {
for (Double d : crIntersectedPolygonPoints) { for (Double d : crIntersectedPolygonPoints) {
boolean pntIsPartOfExistingInterval = false; boolean pntIsPartOfExistingInterval = false;
for (Interval interval : mIntersectionIntervals) { for (Interval interval : mIntersectionIntervals) {
if (pntIsPartOfExistingInterval) { if (interval.getStart() == d || interval.getEnd() == d) {
break; pntIsPartOfExistingInterval = true;
} break;
if (interval.getStart() == d || interval.getEnd() == d) { }
pntIsPartOfExistingInterval = true;
}
} }
// the current value is not part of an existing interval, so insert it // the current value is not part of an existing interval, so insert it
......
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class Interval {
public class Interval {
private static final Interval INVALID_INTERVAL = new Interval(1, 0);
private static final Interval INVALID_INTERVAL = new Interval(1, 0);
private double start;
private double end; private final double start;
private final double end;
public Interval(double start, double end) {
this.start = start; public Interval(double start, double end) {
this.end = end; this.start = start;
} this.end = end;
}
public Interval overlap(Interval other) {
if (isValid() && other.isValid() && isOverlapping(other)) { public Interval overlap(Interval other) {
return new Interval(Math.max(start, other.start), Math.min(end, other.end)); if (isValid() && other.isValid() && isOverlapping(other)) {
} return new Interval(Math.max(start, other.start), Math.min(end, other.end));
return INVALID_INTERVAL; }
} return INVALID_INTERVAL;
}
public boolean isOverlapping(Interval other) {
return !(end < other.start || other.end < start); public boolean isOverlapping(Interval other) {
} return !(end < other.start || other.end < start);
}
public boolean isValid() {
return end >= start; public boolean isValid() {
} return end >= start;
}
public double getLength() {
return end - start; public double getLength() {
} return end - start;
}
public double getStart() {
return start; public double getStart() {
} return start;
}
public double getEnd() {
return end; public double getEnd() {
} return end;
}
@Override
public String toString() { @Override
return "Interval [start=" + start + ", end=" + end + "]"; public String toString() {
} return "Interval [start=" + start + ", end=" + end + "]";
}
}
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.HashMap; import java.util.ArrayList;
import java.util.List; import java.util.HashMap;
import java.util.Map; import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.math.MovedPolygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.MovedPolygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class MeshSurface {
public record MeshSurface(List<CDPolygonNs> polygons) {
private List<CDPolygonNs> polygons;
public static MeshSurface of(Geometry geom) {
public static MeshSurface of(Geometry geom) { List<CDPolygonNs> polygonList = new ArrayList<>();
List<CDPolygonNs> polygonList = new ArrayList<>();
Map<Vector3d, Coordinate3d> pointMap = new HashMap<>();
Map<Vector3d, Coordinate3d> pointMap = new HashMap<>(); Vector3d moveBy = geom.calculateBoundingBox().getBox()[0];
Vector3d moveBy = geom.calculateBoundingBox().getBox()[0]; for (Polygon p : geom.getPolygons()) {
for (Polygon p : geom.getPolygons()) { MovedPolygon mp = MovedPolygon.ofPolygon(p, moveBy);
MovedPolygon mp = MovedPolygon.ofPolygon(p, moveBy); CDPolygonNs poly = CDPolygonNs.of(mp, pointMap);
CDPolygonNs poly = CDPolygonNs.of(mp, pointMap); polygonList.add(poly);
polygonList.add(poly); }
} return new MeshSurface(polygonList);
return new MeshSurface(polygonList); }
}
public MeshSurface(List<CDPolygonNs> polygons) { }
this.polygons = polygons;
}
public List<CDPolygonNs> getPolygons() {
return polygons;
}
}
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List; import java.util.ArrayList;
import java.util.Objects; import java.util.List;
import java.util.Objects;
public class MeshSurfaceUtils {
public class MeshSurfaceUtils {
public static List<PolygonPolygonIntersection> selfIntersects(MeshSurface ms, double epsilon, double angleEpsilon) {
Objects.requireNonNull(ms); public static List<PolygonPolygonIntersection> selfIntersects(MeshSurface ms, double epsilon, double angleEpsilon) {
List<CDPolygonNs> polygons = ms.getPolygons(); Objects.requireNonNull(ms);
List<PolygonPolygonIntersection> intersections = new ArrayList<>(); List<CDPolygonNs> polygons = ms.polygons();
for (int i = 0; i < polygons.size() - 1; i++) { List<PolygonPolygonIntersection> intersections = new ArrayList<>();
EdgePolygon p1 = polygons.get(i); for (int i = 0; i < polygons.size() - 1; i++) {
for (int j = i + 1; j < polygons.size(); j++) { EdgePolygon p1 = polygons.get(i);
EdgePolygon p2 = polygons.get(j); for (int j = i + 1; j < polygons.size(); j++) {
List<PolygonPolygonIntersection> result = IntersectPlanarPolygons.intersectPolygons(p1, p2, epsilon, angleEpsilon); EdgePolygon p2 = polygons.get(j);
intersections.addAll(result); List<PolygonPolygonIntersection> result = IntersectPlanarPolygons.intersectPolygons(p1, p2, epsilon, angleEpsilon);
} intersections.addAll(result);
} }
return intersections; }
} return intersections;
}
private MeshSurfaceUtils() {
} private MeshSurfaceUtils() {
}
}
}
...@@ -20,8 +20,8 @@ package de.hft.stuttgart.citydoctor2.edge; ...@@ -20,8 +20,8 @@ package de.hft.stuttgart.citydoctor2.edge;
public class Point2d { public class Point2d {
private double x; private final double x;
private double y; private final double y;
public Point2d(double x, double y) { public Point2d(double x, double y) {
this.x = x; this.x = x;
......
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* CityDoctor2 is distributed in the hope that it will be useful, * CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>. * along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.hft.stuttgart.citydoctor2.edge; package de.hft.stuttgart.citydoctor2.edge;
public class Point3d {
public class Point3d {
private double x;
private double y; private final double x;
private double z; private final double y;
private final double z;
public Point3d(double x, double y, double z) {
this.x = x; public Point3d(double x, double y, double z) {
this.y = y; this.x = x;
this.z = z; this.y = y;
} this.z = z;
}
public Point3d(double[] values) {
this(values[0], values[1], values[2]); public Point3d(double[] values) {
} this(values[0], values[1], values[2]);
}
public Point3d(Point3d p) {
this(p.getX(), p.getY(), p.getZ()); public Point3d(Point3d p) {
} this(p.getX(), p.getY(), p.getZ());
}
public boolean isAlmostEqual(Point3d origin, double eps) {
Vector3d vec = minus(origin); public boolean isAlmostEqual(Point3d origin, double eps) {
double localEps = eps * eps; Vector3d vec = minus(origin);
if (localEps < Global.getHighAccuracyTolerance()) { double localEps = eps * eps;
localEps = Global.getHighAccuracyTolerance(); if (localEps < Global.getHighAccuracyTolerance()) {
} localEps = Global.getHighAccuracyTolerance();
}
return vec.getLength2() < localEps;
} return vec.getLength2() < localEps;
}
public Vector3d minus(Point3d org) {
return new Vector3d(x - org.getX(), y - org.getY(), z - org.getZ()); public Vector3d minus(Point3d org) {
} return new Vector3d(x - org.getX(), y - org.getY(), z - org.getZ());
}
public Point3d plus(Vector3d v) {
return new Point3d(x + v.getX(), y + v.getY(), z + v.getZ()); public Point3d plus(Vector3d v) {
} return new Point3d(x + v.getX(), y + v.getY(), z + v.getZ());
}
public double getX() {
return x; public double getX() {
} return x;
}
public double getY() {
return y; public double getY() {
} return y;
}
public double getZ() {
return z; public double getZ() {
} return z;
}
public Point3d plus(Point3d point) {
return new Point3d(x + point.getX(), y + point.getY(), z + point.getZ()); public Point3d plus(Point3d point) {
} return new Point3d(x + point.getX(), y + point.getY(), z + point.getZ());
}
public Point3d div(double d) {
return new Point3d(x / d, y /d, z / d); public Point3d div(double d) {
} return new Point3d(x / d, y /d, z / d);
}
@Override
public String toString() { @Override
return "Point3d [x=" + x + ", y=" + y + ", z=" + z + "]"; public String toString() {
} return "Point3d [x=" + x + ", y=" + y + ", z=" + z + "]";
}
@Override
public int hashCode() { @Override
final int prime = 31; public int hashCode() {
int result = 1; final int prime = 31;
long temp; int result = 1;
temp = Double.doubleToLongBits(x); long temp;
result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(x);
temp = Double.doubleToLongBits(y); result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(y);
temp = Double.doubleToLongBits(z); result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(z);
return result; result = prime * result + (int) (temp ^ (temp >>> 32));
} return result;
}
@Override
public boolean equals(Object obj) { @Override
if (this == obj) public boolean equals(Object obj) {
return true; if (this == obj)
if (obj == null) return true;
return false; if (obj == null)
if (getClass() != obj.getClass()) return false;
return false; if (getClass() != obj.getClass())
Point3d other = (Point3d) obj; return false;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) Point3d other = (Point3d) obj;
return false; if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) return false;
return false; if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) return false;
return false; if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
return true; return false;
} return true;
}
}
}
...@@ -79,7 +79,7 @@ public class PolyLine extends BaseEntity { ...@@ -79,7 +79,7 @@ public class PolyLine extends BaseEntity {
} }
public boolean isNullLine() { public boolean isNullLine() {
return isNullLine(0.00); return isNullLine(0.0000001);
} }
...@@ -88,6 +88,13 @@ public class PolyLine extends BaseEntity { ...@@ -88,6 +88,13 @@ public class PolyLine extends BaseEntity {
if (mpFirst == null || mpLast == null) { if (mpFirst == null || mpLast == null) {
return true; return true;
} }
// Check if same point object is start and end
if (mpFirst == mpLast){
Point3d start = mpFirst.getStart().getPoint();
Point3d end = mpLast.getEnd().getPoint();
// If start- and endpoint same object: -> not null line, intersection on a vertex
if(start == end) return false;
}
PolyLineSegment currentSegment = mpFirst; PolyLineSegment currentSegment = mpFirst;
double length = 0.00; double length = 0.00;
// Add length of all segments, starting from mpFirst // Add length of all segments, starting from mpFirst
...@@ -108,7 +115,7 @@ public class PolyLine extends BaseEntity { ...@@ -108,7 +115,7 @@ public class PolyLine extends BaseEntity {
length += segmentLength; length += segmentLength;
} }
// Check if total length is less than the set tolerance // Check if total length is less than the set tolerance
return length <= tolerance; return length < tolerance;
} }
......
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