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

CityDoctor2 validation open source release

parents
/*-
* 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.Iterator;
import java.util.List;
public class IntersectPolygon2d {
private IntersectPolygon2d() {
// only static use
}
public static List<Polygon2dPolygon2dInt> getIntersections(Polygon2d mcpPolygon1, Polygon2d mcpPolygon2,
double epsilon) {
List<Polygon2dPolygon2dInt> mpIntersections = new ArrayList<>();
List<PolyLineSegment2d> segments = new ArrayList<>();
calculateIntersectionSegments(mcpPolygon1, mcpPolygon2, mcpPolygon1, mcpPolygon2, segments, epsilon);
calculateIntersectionSegments(mcpPolygon1, mcpPolygon2, mcpPolygon2, mcpPolygon1, segments, epsilon);
List<PolyLine2d> polyLines = buildPolyLines(segments);
for (PolyLine2d line : polyLines) {
IntersectionType intType;
if (line.getFirstSegment() == line.getLastSegment()) {
Point2d startPnt = line.getStart().getPoint();
Coordinate2d endCoordinate = line.getEnd();
Point2d endPnt = endCoordinate.getPoint();
if (startPnt.isAlmostEqual(endPnt)) {
// point intersection ==> normal intersection
endCoordinate.setPoint(startPnt);
intType = IntersectionType.NORMAL_INTERSECTION;
} else {
// partially/fully embedded edge intersection
int hitsPoly1 = getNumberOfCoincidentNodes(mcpPolygon1, startPnt, endPnt);
int hitsPoly2 = getNumberOfCoincidentNodes(mcpPolygon2, startPnt, endPnt);
if ((0 == hitsPoly1 && 2 == hitsPoly2) || (1 == hitsPoly1 && 2 == hitsPoly2)
|| (2 == hitsPoly1 && 0 == hitsPoly2) || (2 == hitsPoly1 && 1 == hitsPoly2)
|| (2 == hitsPoly1 && 2 == hitsPoly2)) {
intType = IntersectionType.FULLY_EMBEDDED_EDGE;
} else if ((1 == hitsPoly1 && 1 == hitsPoly2)) {
intType = IntersectionType.PARTIALLY_EMBEDDED_EDGE;
} else {
// this shouldn't happen, something went wrong
throw new IllegalStateException("Unable to determine intersection type for polygons ");
}
}
} else {
intType = IntersectionType.PARTIALLY_EMBEDDED_POLYGON;
if (isFullyInPolygon(mcpPolygon2, mcpPolygon1)) {
// is mcpPolygon2 fully in mcpPolygon1?
intType = IntersectionType.POLYGON_1_FULLY_EMBEDDED;
// not perfect but necessary for problems just like 'is polygon of a door in the
// polygon if the surrounding wall'
if (!mcpPolygon2.isPointInsidePolygon(mcpPolygon1.getMidPoint())) {
intType = IntersectionType.FULLY_EMBEDDED_EDGE;
}
} else if (isFullyInPolygon(mcpPolygon1, mcpPolygon2)) {
// is mcpPolygon1 fully in mcpPolygon2?
intType = IntersectionType.POLYGON_2_FULLY_EMBEDDED;
// not perfect but necessary for problems just like 'is polygon of a door in the
// polygon if the surrounding wall'
if (!mcpPolygon1.isPointInsidePolygon(mcpPolygon2.getMidPoint())) {
intType = IntersectionType.FULLY_EMBEDDED_EDGE;
}
}
}
// Const cast is needed, because the intersection result calls ref on
// the polygons. The reason is, that the intersection of loops of a PoylgonNS
// creates temporary Polygon2d's which will be invalid after the intersections
// are calculated and thus the intersection results holds invalid objects
Polygon2dPolygon2dInt ppi = new Polygon2dPolygon2dInt(mcpPolygon1, mcpPolygon2, line, intType);
mpIntersections.add(ppi);
}
return mpIntersections;
}
private static boolean isFullyInPolygon(Polygon2d poly, Polygon2d checkPoly) {
List<Coordinate2d> pListCoord2d;
boolean bPoly2FullyInPoly1 = true;
pListCoord2d = checkPoly.getCoordinates();
for (Coordinate2d coord : pListCoord2d) {
bPoly2FullyInPoly1 &= poly.isPointInsidePolygon(coord.getPoint());
}
return bPoly2FullyInPoly1;
}
private static int getNumberOfCoincidentNodes(Polygon2d cpPolygon, Point2d crStartPnt, Point2d crEndPnt) {
HalfEdge2d cCurrHe = cpPolygon.getFirstHalfEdge();
HalfEdge2d cStartHe = cCurrHe;
int hits = 0;
do {
Point2d currStart = cCurrHe.getStart().getPoint();
if (currStart.isAlmostEqual(crStartPnt)) {
hits++;
if (overlapsWithCurrOrPrevHE(cCurrHe, crEndPnt)) {
hits++;
}
break;
} else if (currStart.isAlmostEqual(crEndPnt)) {
hits++;
if (overlapsWithCurrOrPrevHE(cCurrHe, crStartPnt)) {
hits++;
}
break;
}
cCurrHe = cCurrHe.getNext();
} while (cCurrHe != null && cCurrHe != cStartHe);
return hits;
}
private static boolean overlapsWithCurrOrPrevHE(HalfEdge2d cpHE, Point2d crPnt) {
return cpHE.getEnd().getPoint().isAlmostEqual(crPnt)
|| cpHE.getPrevious().getStart().getPoint().isAlmostEqual(crPnt);
}
private static List<PolyLine2d> buildPolyLines(List<PolyLineSegment2d> rSegments) {
List<PolyLine2d> orIntPolyLines = new ArrayList<>();
if (rSegments.isEmpty()) {
return orIntPolyLines;
}
checkSegmentsForDuplicatedSegments(rSegments);
// build PolyLines
while (!rSegments.isEmpty()) {
Iterator<PolyLineSegment2d> currSegIt = rSegments.iterator();
PolyLineSegment2d next = currSegIt.next();
// cout << "rSegments.size(): " << rSegments.size() << endl;
PolyLineSegment2d pCurrLeftSeg = next;
PolyLineSegment2d pCurrRightSeg = next;
currSegIt.remove();
if (!currSegIt.hasNext()) {
// only one line-segment
orIntPolyLines.add(new PolyLine2d(pCurrLeftSeg));
return orIntPolyLines;
}
// How the PolyLine notion is defined here:
// leftEnd <--Segment--> <--Segment--> <--Segment--> rightEnd
// if leftEnd == rightEnd, I have a closed PolyLine
Point2d polyLineRightEnd = pCurrLeftSeg.getEnd().getPoint();
Point2d polyLineLeftEnd = pCurrLeftSeg.getStart().getPoint();
while (currSegIt.hasNext()) {
next = currSegIt.next();
Point2d localeRightEnd = next.getEnd().getPoint();
Point2d localeLeftEnd = next.getStart().getPoint();
// check if current segments fits the current PolyLine at the rightEnd
boolean fitsRightEnd = false;
if (polyLineRightEnd.isAlmostEqual(localeLeftEnd)) {
fitsRightEnd = true;
} else if (polyLineRightEnd.isAlmostEqual(localeRightEnd)) {
next.reverse();
fitsRightEnd = true;
}
if (fitsRightEnd) {
pCurrRightSeg.setNext(next);
next.setStart(pCurrRightSeg.getEnd());
pCurrRightSeg = next;
polyLineRightEnd = pCurrRightSeg.getEnd().getPoint();
}
// check if current segments fits the current PolyLine at the leftEnd
boolean fitsLeftEnd = false;
if (polyLineLeftEnd.isAlmostEqual(localeRightEnd)) {
fitsLeftEnd = true;
} else if (polyLineLeftEnd.isAlmostEqual(localeLeftEnd)) {
next.reverse();
fitsLeftEnd = true;
}
if (fitsLeftEnd) {
next.setNext(pCurrLeftSeg);
next.setEnd(pCurrLeftSeg.getStart());
pCurrLeftSeg = next;
polyLineLeftEnd = pCurrLeftSeg.getStart().getPoint();
}
// If the current PolyLineSegments fits at least one end, delete the
// segment from the list and start over; otherwise continue
if (fitsLeftEnd || fitsRightEnd) {
currSegIt.remove();
currSegIt = rSegments.iterator();
// next = currSegIt.next();
if (pCurrLeftSeg == pCurrRightSeg) {
pCurrLeftSeg = pCurrLeftSeg.getNext();
pCurrRightSeg.setNext(null);
break;
}
}
} // inner while
if (null == pCurrLeftSeg.getNext() && pCurrLeftSeg.getStart() != pCurrLeftSeg.getEnd()) {
// only one segment, we need at least 2 non point segments to create a
// closed polyline
// delete single segment
} else {
orIntPolyLines.add(new PolyLine2d(pCurrLeftSeg));
}
} // outer while
// there might be loops in the PolyLine
checkForDoubleUsedPoints(orIntPolyLines);
return orIntPolyLines;
}
private static void checkForDoubleUsedPoints(List<PolyLine2d> orIntPolyLines) {
for (PolyLine2d currPolyLine : orIntPolyLines) {
List<PolyLineSegment2d> segs = currPolyLine.getSegments();
if (2 == segs.size()) {
PolyLine2d newPolyLine = new PolyLine2d(segs.get(0).getStart(), segs.get(0).getEnd());
orIntPolyLines.add(newPolyLine);
continue;
}
List<Coordinate2d> coords = currPolyLine.getCoordinates();
for (Coordinate2d cIt1 : coords) {
Point2d currPoint = cIt1.getPoint();
for (Coordinate2d cIt2 : coords) {
Point2d checkPoint = cIt2.getPoint();
// only check for geometrically equal points; this is needed,
// we have a closed PolyLine (start and end point referring to
// the same Coordinate2d)
if (cIt1 != cIt2 && currPoint.isAlmostEqual(checkPoint))// edited by JvF on 15/02/2013
{
// we found a loop in the polyLine
// ==> cut it out, and sew the endings together
PolyLine2d right = currPolyLine.split(cIt1);
PolyLine2d rightRight = right.split(cIt2);
PolyLineSegment2d startRight = right.getFirstSegment();
PolyLineSegment2d endRight = right.getLastSegment();
// the ending coordinates need to be switched, because the erase
// method of std::list deletes the range [first, last), thus the
// ending coordinate of the original polyline would be removed
// instead of the ending of the loop
currPolyLine.getLastSegment().setEnd(endRight.getEnd());
// sewing the endings of the loop
endRight.setEnd(startRight.getStart());
endRight.setNext(null);
// insert the new PolyLine; it will be checked for double nodes later
orIntPolyLines.add(right);
// sew the orignal polyline together, where the loop was cut out
currPolyLine.append(rightRight);
// erase the coordinates from the loop out of the list; these
// coordinates are checked later
coords.remove(cIt1);
coords.remove(cIt2);
}
}
}
}
}
private static void checkSegmentsForDuplicatedSegments(List<PolyLineSegment2d> rSegments) {
// code edit JvF: old 'Code doesnt work correctly new one does// should TODO:
// test it with other examples (where a point segment with multiplicity 2 is
// given)
// segment suchen, dass kein punkt ist
// dann immer von vorne starten und punkte / segmente suchen die geometrische
// mit dem aktuellen segment übereinstimmen
// löschen falls das der fall ist
// edited by Jvf on 19/2/2013
// start--------------------------------------------------------------------------------------
double eps = 0.00001;
List<PolyLineSegment2d> lines = new ArrayList<>();
List<PolyLineSegment2d> points = new ArrayList<>();
// separate segments in lines and points
for (PolyLineSegment2d seg : rSegments) {
boolean segIsPoint = seg.getLength() < eps;
if (segIsPoint) {
points.add(seg);
} else {
lines.add(seg);
}
}
// eliminate points that are connected to lines
for (PolyLineSegment2d line : lines) {
Iterator<PolyLineSegment2d> pointsit = points.iterator();
Point2d startSegPnt = line.getStart().getPoint();
Point2d endSegPnt = line.getEnd().getPoint();
while (pointsit.hasNext()) {
PolyLineSegment2d pointSegment = pointsit.next();
Point2d pnt = pointSegment.getStart().getPoint();
boolean isConnectedToSegment = (pnt.isAlmostEqual(startSegPnt, eps)
|| pnt.isAlmostEqual(endSegPnt, eps));
if (isConnectedToSegment) {
pointsit.remove();
}
}
}
// eliminate geometric equal lines
for (int i = 0; i < lines.size() - 1; i++) {
PolyLineSegment2d line1 = lines.get(i);
List<PolyLineSegment2d> remainingSegments = new ArrayList<>();
// the first segment cannot be duplicated
for (int j = 0; j < i + 1; j++) {
remainingSegments.add(lines.get(j));
}
for (int j = i + 1; j < lines.size(); j++) {
PolyLineSegment2d line2 = lines.get(j);
boolean isGeometricallyEqual = line1.isGeometricallyEqual(line2, eps);
if (!isGeometricallyEqual) {
remainingSegments.add(line2);
}
}
// only the non duplicate segments are contained here
lines = remainingSegments;
}
rSegments.clear();
rSegments.addAll(lines);
rSegments.addAll(points);
// Iterator<PolyLineSegment2d> currSegIt = rSegments.iterator();
// PolyLineSegment2d currSegItNext = currSegIt.next();
// while (currSegIt.hasNext()) {
// Iterator<PolyLineSegment2d> toCheckIt = rSegments.iterator();
// PolyLineSegment2d toCheckItNext = toCheckIt.next();
//
// Point2d startSegPnt = currSegItNext.getStart().getPoint();
// Point2d endSegPnt = currSegItNext.getEnd().getPoint();
//
// while (toCheckIt.hasNext()) {
// Point2d pnt = toCheckItNext.getStart().getPoint();
// boolean isNotTheSameIterator = (currSegItNext != toCheckItNext);
// boolean currSegIsPoint = toCheckItNext.getLength() < eps;
// boolean isConnectedToSegment = (pnt.isAlmostEqual(startSegPnt, eps)
// || pnt.isAlmostEqual(endSegPnt, eps));
// boolean isGeometricallyEqual = currSegItNext.isGeometricallyEqual(toCheckItNext, eps);
//
// // check if the current segment is obsolete
// if (isNotTheSameIterator && ((currSegIsPoint && isConnectedToSegment) || isGeometricallyEqual)) {
// toCheckIt.remove();
// toCheckItNext = toCheckIt.next();
// } else {
// toCheckItNext = toCheckIt.next();
// } // else
// } // inner while
// currSegItNext = currSegIt.next();
// } // outer while
}
private static void calculateIntersectionSegments(Polygon2d mcpPolygon1, Polygon2d mcpPolygon2,
Polygon2d cpFirstPolygon, Polygon2d cpIntersectWithFirstPolygon, List<PolyLineSegment2d> segments,
double eps) {
List<HalfEdge2d> polyHEs = cpFirstPolygon.getHalfEdges();
for (HalfEdge2d he : polyHEs) {
if (he.getPartner() != null && he.getPartner().getPolygon() == cpIntersectWithFirstPolygon) {
// both polygons sharing this edge, no need to intersect it
// NOTE: it's assumed, that the polygons aren't self intersecting
continue;
}
GmBoundedStraight2d straight = he.getStraight();
List<Interval> intList = IntersectPolygonAndStraight2d.intersect(cpIntersectWithFirstPolygon, straight,
eps);
List<PolyLineSegment2d> newSegs = createSegments(mcpPolygon1, mcpPolygon2, eps, intList, straight);
newSegs.addAll(segments);
segments.clear();
segments.addAll(newSegs);
}
}
private static List<PolyLineSegment2d> createSegments(Polygon2d mcpPolygon1, Polygon2d mcpPolygon2, double eps,
List<Interval> crIntersectionIntervals, GmBoundedStraight2d crHalfEdgeStraight) {
/*
* An intersection interval consists of two parameter values (t_a, t_e)
* referring to the intersecting straight (g(t_i) = p + t_i*v). This straight is
* created from a HalfEdge2d. It is possible that the calculated intersection
* extend the restricted domain of the underlying HalfEdge2d. Normally you need
* to check, if the values of the interval are inside the interval [0,1], but
* the direction vector of the straight is normalized during the creation of the
* straight, thats why an additional treatment is neccessary.
*/
List<PolyLineSegment2d> orSegments = new ArrayList<>();
double len = crHalfEdgeStraight.getLength();
Interval validInterval = new Interval(0, len);
for (Interval interval : crIntersectionIntervals) {
Interval overlap = validInterval.overlap(interval);
if (!overlap.isValid()) {
continue;
}
Point2d pntStart = crHalfEdgeStraight.evaluate(overlap.getStart());
// check if point interval is a coordinate of both polygons
if (overlap.getLength() < Global.getHighAccuracyTolerance() * 100
&& bothPolygonsSharingThisPointAsCoordinate(mcpPolygon1, mcpPolygon2, pntStart, eps)) {
continue;
}
Coordinate2d pStart = new Coordinate2d(pntStart);
Point2d pntEnd = crHalfEdgeStraight.evaluate(overlap.getEnd());
Coordinate2d pEnd = new Coordinate2d(pntEnd);
orSegments.add(new PolyLineSegment2d(pStart, pEnd));
}
return orSegments;
}
private static boolean bothPolygonsSharingThisPointAsCoordinate(Polygon2d mcpPolygon1, Polygon2d mcpPolygon2,
Point2d crIntPnt, double eps) {
List<Coordinate2d> coords = mcpPolygon1.getCoordinates();
Coordinate2d tmp = null;
for (Coordinate2d it : coords) {
if (crIntPnt.isAlmostEqual(it.getPoint(), eps)) {
tmp = it;
break;
}
}
if (tmp != null) {
// check if mcpPolygon2 contains the same coordiante2d
coords = mcpPolygon2.getCoordinates();
for (Coordinate2d it : coords) {
if (it == tmp) {
return true;
}
}
}
return false;
}
}
/*-
* 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.Iterator;
import java.util.List;
import java.util.TreeSet;
public class IntersectPolygonAndStraight2d {
/**
* @param pcPolygon A polygon
*
* @param rcIntersectingStraight The straight which have been intersected
* with the given polygon
*
* @param rIntersectionValues The calculated parameters of the
* intersection
*
* @param rIntersectedPolygonPoints A set of values, containing parameter values
* where the straight intersects the polygon in
* a corner point.
*
* @return rIntersectionIntervals A list of computed intersection intervals of
* the straight with the polygon
*
* This methods computes the intervals of the intersection between the
* given straight and polygon. It gets only parameters where the
* straight hits the polygon.
*
* The additional set of intersected polygon points is neccessary,
* 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.
*
*/
public static List<Interval> intersect(Polygon2d mcpPolygon, GmBoundedStraight2d mIntStraight, double eps) {
List<Interval> mIntersectionIntervals = new ArrayList<>();
TreeSet<Double> intersectionValues = new TreeSet<>(Global.getDoubleTolCompare(eps));
TreeSet<Double> intersectedPolygonPoints = new TreeSet<>(Global.getDoubleTolCompare(eps));
List<HalfEdge2d> halfEdges = mcpPolygon.getHalfEdges();
for (HalfEdge2d heIt : halfEdges) {
GmBoundedStraight2d hEStraight = heIt.getStraight();
GmStraight2dIntersectionResult res = hEStraight.intersect(mIntStraight);
if (res.areParallel()) {
Vector2d dir = mIntStraight.getDirection();
Vector2d diffVec = mIntStraight.getOrigin().minus(hEStraight.getOrigin());
// calculate the do product to check if the vector connecting the
// origin points of the straight is orthogonal to the direction
// if so ==> the parallel straights overlap
double diffVecDotPerpDir = diffVec.dot(dir.getPerpendicularVector());
if (Math.abs(diffVecDotPerpDir) < Global.getHighAccuracyTolerance()) {
// Straights are identical
Point2d p1 = hEStraight.getOrigin();
Point2d p2 = hEStraight.getTarget();
Point2d orig = mIntStraight.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 IllegalArgumentException(
"Directional vector of identical straights is equal to the zero vector");
}
// Point2d pnt = mIntStraight.evaluate( params[0] );
// cout << "got ( " << pnt.getU() << ", " << pnt.getV() << " ) as int point with
// param " << params[0] << endl;
// pnt = mIntStraight.evaluate( params[1] );
// cout << "got ( " << pnt.getU() << ", " << pnt.getV() << " ) as int point with
// param " << params[1] << endl;
assignParameterToCorrectList(params[0], intersectionValues, intersectedPolygonPoints);
assignParameterToCorrectList(params[1], intersectionValues, intersectedPolygonPoints);
}
} else {
if (hEStraight.isWithinBoundaries(res.getParamHE())) {
// Point2d pnt = mIntStraight.evaluate( paramInt );
// cout << "got ( " << pnt.getU() << ", " << pnt.getV() << " ) as int point with
// param " << paramInt << endl;
assignParameterToCorrectList(res.getParamInt(), intersectionValues, intersectedPolygonPoints);
}
}
}
processIntersectionIntervals(mcpPolygon, mIntStraight, intersectionValues, intersectedPolygonPoints,
mIntersectionIntervals);
return mIntersectionIntervals;
}
/**
*
* 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 crIntersectionValues The calculated parameters of the
* intersection
*
* @param crIntersectedPolygonPoints A set of values, containing parameter
* values where the straight intersects the
* polygon in a corner point.
*
*
*/
private static void processIntersectionIntervals(Polygon2d mcpPolygon, GmStraight2d mIntStraight,
TreeSet<Double> crIntersectionValues, TreeSet<Double> crIntersectedPolygonPoints,
List<Interval> mIntersectionIntervals) {
// create intervals
if (!crIntersectionValues.isEmpty()) {
// if (crIntersectionValues.size() > 1) {
Iterator<Double> it1 = crIntersectionValues.iterator();
double vit1 = it1.next();
while (it1.hasNext()) {
double vit2 = it1.next();
// check if the point between the values (midpoint) is inside of the
// polygon; this don't need to be true for concave polygons
// if the midpoint is inside, than we have an interval, if it's outside
// it's not. i can
Point2d pnt = mIntStraight.evaluate((vit1 + vit2) / 2);
if (mcpPolygon.isPointInsidePolygon(pnt)) {
Interval intIntv = new Interval(vit1, vit2);
mIntersectionIntervals.add(intIntv);
}
vit1 = vit2;
}
}
// insert points intervals, if they are no part of an already existing
// interval
for (Double d : crIntersectedPolygonPoints) {
boolean pntIsPartOfExistingInterval = false;
for (Interval interval : mIntersectionIntervals) {
if (pntIsPartOfExistingInterval) {
break;
}
if (interval.getStart() == d || interval.getEnd() == d) {
pntIsPartOfExistingInterval = true;
}
}
// the current value is not part of an existing interval, so insert it
if (!pntIsPartOfExistingInterval) {
mIntersectionIntervals.add(new Interval(d, d));
}
}
}
/**
*
*
* Util method. Checks if the given parameter has been already inserted as
* intersection 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.
*
* Just a util method to purge the code
*
* @param param Calculated intersection parameter
*
* @param orIntersectionValues Set of intersection parameters
*
* @param orIntersectedPolygonPoints Set of intersected polygon corner points
*
*/
private static void assignParameterToCorrectList(double param, TreeSet<Double> orIntersectionValues,
TreeSet<Double> orIntersectedPolygonPoints) {
if (!orIntersectionValues.contains(param)) {
orIntersectionValues.add(param);
} else {
// value is already present ==> this must be a point of the polygon
// note, it's assumed, that the polygon do not intersect itself
orIntersectedPolygonPoints.add(param);
}
}
}
/*-
* 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 enum IntersectionType {
FULLY_EMBEDDED_EDGE, PARTIALLY_EMBEDDED_EDGE, NORMAL_INTERSECTION, UNDEFINED_INTERSECTION, PARTIALLY_EMBEDDED_POLYGON, POLYGON_1_FULLY_EMBEDDED, POLYGON_2_FULLY_EMBEDDED
}
/*-
* 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 Interval {
private static final Interval INVALID_INTERVAL = new Interval(1, 0);
private double start;
private double end;
public Interval(double start, double end) {
this.start = start;
this.end = end;
}
public Interval overlap(Interval other) {
if (isValid() && other.isValid() && isOverlapping(other)) {
return new Interval(Math.max(start, other.start), Math.min(end, other.end));
}
return INVALID_INTERVAL;
}
public boolean isOverlapping(Interval other) {
return !(end < other.start || other.end < start);
}
public boolean isValid() {
return end >= start;
}
public double getLength() {
return end - start;
}
public double getStart() {
return start;
}
public double getEnd() {
return end;
}
@Override
public String toString() {
return "Interval [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;
public class Matrix3d {
private double[][] val = new double[3][3];
public void set(int i, int j, double value) {
val[i][j] = value;
}
public void gauss(double[] b, double[] x) {
int[] rowIndex = new int[3];
int[] colIndex = new int[3];
// initialization
// --------------
for (int i = 0; i < 3; i++) {
rowIndex[i] = colIndex[i] = i;
}
for (int i = 0; i < 2; i++) {
int rIndex = -1;
int cIndex = -1;
// find total pivot
// ----------------
double max = 0.0;
for (int k = i; k < 3; k++) {
for (int l = i; l < 3; l++) {
double ab = Math.abs(val[rowIndex[k]][colIndex[l]]);
if (max < ab) {
max = ab;
rIndex = k;
cIndex = l;
}
}
}
// check result against eps
// ------------------------
if (max < 1e-6) {
throw new IllegalStateException("gauss: max (" + max + ") below eps (" + 1e-6 + ")");
}
// change indicies
// ---------------
int u = rowIndex[i];
int v = colIndex[i];
rowIndex[i] = rowIndex[rIndex];
colIndex[i] = colIndex[cIndex];
rowIndex[rIndex] = u;
colIndex[cIndex] = v;
double pivot = val[rowIndex[i]][colIndex[i]];
// Reduktion
// ---------
for (int z = i + 1; z < 3; z++) {
double q = val[rowIndex[z]][colIndex[i]] / pivot;
for (int s = i + 1; s < 3; s++) {
val[rowIndex[z]][colIndex[s]] = val[rowIndex[z]][colIndex[s]]
- val[rowIndex[i]][colIndex[s]] * q;
}
b[rowIndex[z]] = b[rowIndex[z]] - b[rowIndex[i]] * q;
}
}
// solution
// --------
for (int i = 2; i >= 0; i--) {
x[colIndex[i]] = b[rowIndex[i]];
for (int l = i + 1; l < 3; l++) {
x[colIndex[i]] = x[colIndex[i]] - x[colIndex[l]] * val[rowIndex[i]][colIndex[l]];
}
x[colIndex[i]] = x[colIndex[i]] / val[rowIndex[i]][colIndex[i]];
}
}
}
/*-
* 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.IdentityHashMap;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
public class MeshSurface {
private List<CDPolygonNs> polygons;
public static MeshSurface of(Geometry geom) {
List<CDPolygonNs> polygonList = new ArrayList<>();
Map<Vertex, Coordinate3d> pointMap = new IdentityHashMap<>();
for (Polygon p : geom.getPolygons()) {
List<List<Coordinate3d>> loopCoordinates = new ArrayList<>();
List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, p.getExteriorRing().getVertices());
loopCoordinates.add(edgeExtRing);
for (LinearRing innerRing : p.getInnerRings()) {
List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices());
loopCoordinates.add(edgeInnerRing);
}
List<List<HalfEdge>> halfEdges = new ArrayList<>();
for (List<Coordinate3d> ringCoordinates : loopCoordinates) {
List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates);
halfEdges.add(currHeList);
}
CDPolygonNs newPolygon = new CDPolygonNs(halfEdges);
polygonList.add(newPolygon);
}
return new MeshSurface(polygonList);
}
private static List<Coordinate3d> createCoordinatesFromRing(Map<Vertex, Coordinate3d> pointMap,
List<Vertex> vertices) {
List<Coordinate3d> edgeRing = new ArrayList<>();
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex v = vertices.get(i);
Coordinate3d c = pointMap.computeIfAbsent(v, key -> new Coordinate3d(key.getX(), key.getY(), key.getZ()));
edgeRing.add(c);
}
return edgeRing;
}
private static List<HalfEdge> createHalfEdgesFromCoordinates(List<Coordinate3d> ringCoordinates) {
List<HalfEdge> currHeList = new ArrayList<>();
HalfEdge prevHalfEdge = null;
for (int currCoordIndex = 1; currCoordIndex < ringCoordinates.size(); currCoordIndex++) {
int prevCoordIndex = currCoordIndex - 1;
Coordinate3d currCoord = ringCoordinates.get(currCoordIndex);
Coordinate3d prevCoord = ringCoordinates.get(prevCoordIndex);
HalfEdge e = new HalfEdge(prevCoord, currCoord);
if (prevHalfEdge != null) {
prevHalfEdge.setNext(e);
}
currHeList.add(e);
prevHalfEdge = e;
}
if (prevHalfEdge == null) {
throw new IllegalStateException("No half edges were created");
}
Coordinate3d start = ringCoordinates.get(0);
Coordinate3d end = ringCoordinates.get(ringCoordinates.size() - 1);
HalfEdge e = new HalfEdge(end, start);
prevHalfEdge.setNext(e);
e.setNext(currHeList.get(0));
currHeList.add(e);
return currHeList;
}
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
*
* 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 MeshSurfaceUtils {
public static List<PolygonPolygonIntersection> selfIntersects(MeshSurface ms, double epsilon, double angleEpsilon) {
Objects.requireNonNull(ms);
List<CDPolygonNs> polygons = ms.getPolygons();
List<PolygonPolygonIntersection> intersections = new ArrayList<>();
for (int i = 0; i < polygons.size() - 1; i++) {
EdgePolygon p1 = polygons.get(i);
for (int j = i + 1; j < polygons.size(); j++) {
EdgePolygon p2 = polygons.get(j);
List<PolygonPolygonIntersection> result = IntersectPlanarPolygons.intersectPolygons(p1, p2, epsilon, angleEpsilon);
intersections.addAll(result);
}
}
return intersections;
}
private MeshSurfaceUtils() {
}
}
/*-
* 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 Point2d {
private double x;
private double y;
public Point2d(double x, double y) {
this.x = x;
this.y = y;
}
public Vector2d minus(Point2d other) {
return new Vector2d(x - other.x, y - other.y);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public Point2d plus(Vector2d dir) {
return new Point2d(x + dir.getX(), y + dir.getY());
}
@Override
public String toString() {
return "Point2d [x=" + x + ", y=" + y + "]";
}
public boolean isAlmostEqual(Point2d other) {
return isAlmostEqual(other, Global.getHighAccuracyTolerance());
}
public boolean isAlmostEqual(Point2d other, double eps) {
Vector2d vec = minus(other);
double localEps = eps * eps;
// prevent the epsilon of getting to small
if (localEps < Global.getHighAccuracyTolerance()) {
localEps = Global.getHighAccuracyTolerance();
}
// not using sqrt, should speed this a little bit up
return vec.getLength2() < localEps;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(x);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Point2d other = (Point2d) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) {
return false;
}
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) {
return false;
}
return true;
}
}
/*-
* 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 Point3d {
private double x;
private double y;
private double z;
public Point3d(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Point3d(double[] values) {
this(values[0], values[1], values[2]);
}
public Point3d(Point3d p) {
this(p.getX(), p.getY(), p.getZ());
}
public boolean isAlmostEqual(Point3d origin, double eps) {
Vector3d vec = minus(origin);
double localEps = eps * eps;
if (localEps < Global.getHighAccuracyTolerance()) {
localEps = Global.getHighAccuracyTolerance();
}
return vec.getLength2() < localEps;
}
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 double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
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);
}
@Override
public String toString() {
return "Point3d [x=" + x + ", y=" + y + ", z=" + z + "]";
}
}
/*-
* 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 PolyLine extends BaseEntity {
private PolyLineSegment mpFirst;
private PolyLineSegment mpLast;
public PolyLine(Coordinate3d pVertex1, Coordinate3d pVertex2) {
PolyLineSegment pSegment = new PolyLineSegment(pVertex1, pVertex2);
mpFirst = mpLast = pSegment;
addChild(pSegment);
}
public PolyLine(List<Coordinate3d> rCoordinateList, boolean closePolyLine) {
if (rCoordinateList.size() < 2) {
throw new IllegalArgumentException("rCoordinateList list needs at least 2 points");
}
mpFirst = mpLast = null;
for (int i = 0; i < rCoordinateList.size() - 1; i++) {
Coordinate3d itCS = rCoordinateList.get(i);
Coordinate3d itCE = rCoordinateList.get(i + 1);
PolyLineSegment pSegment = new PolyLineSegment(itCS, itCE);
if (null == mpFirst) {
mpFirst = mpLast = pSegment;
} else {
mpLast.setNext(pSegment);
mpLast = pSegment;
}
addChild(pSegment);
}
if (mpLast == null) {
throw new NullPointerException();
}
if ((rCoordinateList.get(0) == rCoordinateList.get(rCoordinateList.size() - 1)) && closePolyLine) {
mpLast.setNext(mpFirst);
}
}
public Coordinate3d getStart() {
return mpFirst.getStart();
}
public Coordinate3d getEnd() {
return mpLast.getEnd();
}
public PolyLineSegment getFirstSegment() {
return mpFirst;
}
public PolyLineSegment getLastSegment() {
return mpLast;
}
@Override
public String toString() {
return "PolyLine [mpFirst=" + mpFirst + ", mpLast=" + mpLast + "]";
}
}
/*-
* 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 PolyLine2d extends BaseEntity {
private PolyLineSegment2d mpFirst;
private PolyLineSegment2d mpLast;
public PolyLine2d(PolyLineSegment2d pStart) {
mpFirst = mpLast = pStart;
addChild(mpFirst);
while (mpLast.getNext() != null) {
addChild(mpLast.getNext());
mpLast = mpLast.getNext();
}
}
public PolyLine2d(Coordinate2d pVertex1, Coordinate2d pVertex2) {
PolyLineSegment2d pSegment = new PolyLineSegment2d(pVertex1, pVertex2);
mpFirst = mpLast = pSegment;
addChild(pSegment);
}
public List<Coordinate2d> getCoordinates() {
List<Coordinate2d> coordinates = new ArrayList<>();
if (mpFirst != null) {
PolyLineSegment2d pSegment = mpFirst;
coordinates.add(pSegment.getStart());
while (pSegment != null) {
coordinates.add(pSegment.getEnd());
pSegment = pSegment.getNext();
}
}
return coordinates;
}
public PolyLineSegment2d getFirstSegment() {
return mpFirst;
}
public PolyLineSegment2d getLastSegment() {
return mpLast;
}
public Coordinate2d getStart() {
return mpFirst.getStart();
}
public Coordinate2d getEnd() {
return mpLast.getEnd();
}
public List<PolyLineSegment2d> getSegments() {
List<PolyLineSegment2d> segments = new ArrayList<>();
PolyLineSegment2d pSegment = mpFirst;
while (pSegment != null) {
segments.add(pSegment);
pSegment = pSegment.getNext();
}
return segments;
}
public PolyLine2d append(PolyLine2d pSecondLine) {
// append the end-vertices to the current line
PolyLineSegment2d pSecondStart = pSecondLine.getFirstSegment();
while (pSecondStart != null) {
append(pSecondStart.getEnd());
pSecondStart = pSecondStart.getNext();
}
return pSecondLine;
}
private Coordinate2d append(Coordinate2d pVertex) {
// check if the polyline already contains segments
// -----------------------------------------------
PolyLineSegment2d pNewSegment = new PolyLineSegment2d(getEnd(), pVertex);
addChild(pNewSegment);
mpLast.setNext(pNewSegment);
mpLast = pNewSegment;
return pVertex;
}
/**
* Split a polyline at a vertex that is already part of the polyline. The
* original PolyLine will be split, and the right fragment will be returned. The
* original PolyLine will hold the left fragment
*
* @param pVertex
* @return
*/
public PolyLine2d split(Coordinate2d pVertex) {
PolyLineSegment2d seg = mpFirst;
PolyLineSegment2d leftLast = null;
PolyLineSegment2d rightFirst = null;
for (; seg != null; seg = seg.getNext()) {
if (seg.getEnd() == pVertex) {
leftLast = seg;
rightFirst = seg.getNext();
break;
}
}
if (leftLast == null) {
throw new IllegalArgumentException("vertex is not part of polyline");
}
List<PolyLineSegment2d> segmentsToRemove = new ArrayList<>();
PolyLineSegment2d pToRemove = rightFirst;
while (pToRemove != null) {
segmentsToRemove.add(pToRemove);
pToRemove = pToRemove.getNext();
}
// terminate this polyline
// -----------------------
leftLast.setNext(null);
mpLast = leftLast;
PolyLine2d pRightLine = new PolyLine2d(rightFirst);
// De-refernence all unused segments from this line
// ------------------------------------------------
for (PolyLineSegment2d itTR : segmentsToRemove) {
removeChild(itTR);
}
return pRightLine;
}
}
/*-
* 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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class PolyLineSegment extends BaseEntity {
private static final Logger logger = LogManager.getLogger(PolyLineSegment.class);
private Coordinate3d mpStart;
private Coordinate3d mpEnd;
private PolyLineSegment mpNext;
public PolyLineSegment(Coordinate3d cpStart, Coordinate3d cpEnd) {
if (cpStart == cpEnd) {
logger.warn("PolyLineSegment::PolyLineSegment() : start and end coordinate are the same object");
}
mpStart = cpStart;
mpEnd = cpEnd;
addChild(mpStart);
addChild(mpEnd);
}
public void setNext(PolyLineSegment next) {
mpNext = next;
}
public Coordinate3d getStart() {
return mpStart;
}
public Coordinate3d getEnd() {
return mpEnd;
}
public PolyLineSegment getNext() {
return mpNext;
}
@Override
public String toString() {
return "PolyLineSegment [mpStart=" + mpStart + ", mpEnd=" + mpEnd + "]";
}
}
/*-
* 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 PolyLineSegment2d extends BaseEntity {
private Coordinate2d start;
private Coordinate2d end;
private PolyLineSegment2d next;
public PolyLineSegment2d(Coordinate2d start, Coordinate2d end, PolyLineSegment2d next) {
this.start = start;
this.end = end;
this.next = next;
addChild(start);
addChild(end);
}
public PolyLineSegment2d(Coordinate2d start, Coordinate2d end) {
this(start, end, null);
}
public Coordinate2d getEnd() {
return end;
}
public Coordinate2d getStart() {
return start;
}
public void reverse() {
Coordinate2d pHelp = start;
start = end;
end = pHelp;
}
public void setNext(PolyLineSegment2d next) {
this.next = next;
}
public void setStart(Coordinate2d start) {
this.start = start;
}
public void setEnd(Coordinate2d end) {
this.end = end;
}
public PolyLineSegment2d getNext() {
return next;
}
public double getLength() {
Point2d startPoint = start.getPoint();
Point2d endPoint = end.getPoint();
return ((startPoint.equals(endPoint)) ? 0.0 : (startPoint.minus(endPoint)).getLength());
}
public boolean isGeometricallyEqual(PolyLineSegment2d cpSegment2, double eps) {
PolyLineSegment2d cpSegment1 = this;
Point2d start1 = cpSegment1.start.getPoint();
Point2d end1 = cpSegment1.end.getPoint();
Point2d start2 = cpSegment2.start.getPoint();
Point2d end2 = cpSegment2.end.getPoint();
return (start1.isAlmostEqual(start2, eps) && end1.isAlmostEqual(end2, eps))
|| (start1.isAlmostEqual(end2, eps) && end1.isAlmostEqual(start2, eps));
}
@Override
public String toString() {
return "PolyLineSegment2d [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.List;
public class Polygon2d extends BaseEntity {
List<List<HalfEdge2d>> mHalfEdges = new ArrayList<>();
public Polygon2d() {
}
public Polygon2d(List<HalfEdge2d> halfEdges) {
for (HalfEdge2d he : halfEdges) {
addChild(he);
}
mHalfEdges.add(halfEdges);
}
public boolean isPointInsidePolygon(Point2d rcPoint, double eps) {
// algorithm idea: cast a ray from rcPoint in u-direction and
// count how often it hits the polygon borders. If there are an odd number
// of intersections, the point lays inside the polygon, otherwise not.
// easy check: check point vs bounding box
Box2d box = getBoundingBox2d();
if (!box.isPointInside(rcPoint)) {
return false;
}
List<HalfEdge2d> hes = getHalfEdges();
double u = rcPoint.getX();
double v = rcPoint.getY();
boolean inside = false;
for (HalfEdge2d it : hes) {
Point2d start = it.getStart().getPoint();
// just check the start, the current end point is the start point of the
// next half edge.
if (rcPoint.equals(start)) {
return true;
}
Point2d end = it.getEnd().getPoint();
double u1 = start.getX();
double v1 = start.getY();
double u2 = end.getX();
double v2 = end.getY();
boolean startVIsBigger = v1 >= v;
boolean endVIsBigger = v2 >= v;
// If the v values of the current halfedges are both bigger or smaler
// than the v coordinate from the given point, than a line, parallel to
// the u-axis wont hit the half edge
if (startVIsBigger != endVIsBigger) {
// half edge and ray might intersect. calculate the intersection point
// and check if the u-value of the intersection point is smaler than
// u from given point (no intersection) or bigger or equal (intersection)
// the formula is derived by caculating the intersection of the function
// g(x) = v with the half edge
double uInt = (v * (u2 - u1) - v1 * u2 + v2 * u1) / (v2 - v1);
if (Math.abs(uInt - u) < eps) {
return true; // point is lying on edge-> inside
} else {
if (uInt > u) {
inside = !inside;
}
}
}
}
return inside;
}
private Box2d getBoundingBox2d() {
List<HalfEdge2d> halfEdges = getHalfEdges();
Point2d point = halfEdges.get(0).getStart().getPoint();
double lowerU = point.getX();
double lowerV = point.getY();
double upperU = point.getX();
double upperV = point.getY();
for (int i = 1; i < halfEdges.size(); i++) {
HalfEdge2d halfEdge = halfEdges.get(i);
point = halfEdge.getStart().getPoint();
lowerU = (point.getX() < lowerU) ? point.getX() : lowerU;
lowerV = (point.getY() < lowerV) ? point.getY() : lowerV;
upperU = (point.getX() > upperU) ? point.getX() : upperU;
upperV = (point.getY() > upperV) ? point.getY() : upperV;
}
Box2d box = new Box2d(new Point2d(lowerU, lowerV), new Point2d(upperU, upperV));
return box;
}
public static void connectHalfEdges(List<HalfEdge2d> rHalfEdges) {
if (rHalfEdges.isEmpty()) {
throw new IllegalArgumentException("no halfEdges given");
}
if (rHalfEdges.size() == 1) {
HalfEdge2d front = rHalfEdges.get(0);
front.setNext(front);
front.setPrevious(front);
} else {
for (int i = 0; i < rHalfEdges.size() - 1; i++) {
HalfEdge2d previous = rHalfEdges.get(i);
HalfEdge2d next = rHalfEdges.get(i + 1);
previous.setNext(next);
next.setPrevious(previous);
}
HalfEdge2d back = rHalfEdges.get(rHalfEdges.size() - 1);
HalfEdge2d front = rHalfEdges.get(0);
back.setNext(front);
front.setPrevious(back);
}
}
public boolean isPointInsidePolygon(Point2d rcPoint) {
return isPointInsidePolygon(rcPoint, Global.getHighAccuracyTolerance());
}
public List<Coordinate2d> getCoordinates() {
List<Coordinate2d> coords = new ArrayList<>();
HalfEdge2d firstHE = getFirstHalfEdge();
HalfEdge2d currHE = firstHE;
do {
coords.add(currHE.getStart());
currHE = currHE.getNext();
} while (currHE != firstHE);
return coords;
}
public Point2d getMidPoint() {
double x = 0;
double y = 0;
List<HalfEdge2d> halfEdges = getChildren(HalfEdge2d.class);
for (HalfEdge2d he : halfEdges) {
Point2d point = he.getStart().getPoint();
x = x + point.getX();
y = y + point.getY();
}
int count = halfEdges.size();
return new Point2d(x / count, y / count);
}
public List<HalfEdge2d> getHalfEdges() {
return getChildren(HalfEdge2d.class);
}
public HalfEdge2d getFirstHalfEdge() {
List<HalfEdge2d> halfEdges = getHalfEdges();
if (!halfEdges.isEmpty()) {
return halfEdges.get(0);
}
return null;
}
}
/*-
* 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;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Polygon2dNs extends Polygon2d {
private static final Logger logger = LogManager.getLogger(Polygon2dNs.class);
public Polygon2dNs(List<Coordinate2d> coordinates, boolean setPartner) {
Set<Coordinate2d> singleCoords = new HashSet<>(coordinates);
// check if the coordinates are used twice
// NOTE: A CoordinateSet is used, which allows only unique elements
if (singleCoords.size() != coordinates.size()) {
logger.warn("Some coordinates are identical");
}
List<HalfEdge2d> halfEdges = new ArrayList<>();
for (int i = 0; i < coordinates.size() - 1; i++) {
HalfEdge2d pHE = new HalfEdge2d(coordinates.get(i), coordinates.get(i + 1), setPartner);
addChild(pHE);
halfEdges.add(pHE);
}
// it2 is always one Coordinate ahead of it1, hence if it2 is pointing
// behind the last element, it1 is pointing to the last
HalfEdge2d pLastHE = new HalfEdge2d(coordinates.get(coordinates.size() - 1), coordinates.get(0), setPartner);
addChild(pLastHE);
halfEdges.add(pLastHE);
mHalfEdges.add(halfEdges);
// TODO MW: inner edges
Polygon2d.connectHalfEdges(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 Polygon2dPolygon2dInt {
private Polygon2d mcpPolygon1;
private Polygon2d mcpPolygon2;
private PolyLine2d line;
private IntersectionType intType;
public Polygon2dPolygon2dInt(Polygon2d mcpPolygon1, Polygon2d mcpPolygon2, PolyLine2d line,
IntersectionType intType) {
this.mcpPolygon1 = mcpPolygon1;
this.mcpPolygon2 = mcpPolygon2;
this.line = line;
this.intType = intType;
}
public PolyLine2d getPolyLine2d() {
return line;
}
public IntersectionType getIntType() {
return intType;
}
public Polygon2d getPolygon1() {
return mcpPolygon1;
}
public Polygon2d getPolygon2() {
return mcpPolygon2;
}
}
/*-
* 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 PolygonIntersectionResult {
private boolean intersecting = false;
private PolygonPolygonIntersection intersection;
public static PolygonIntersectionResult noIntersection() {
return new PolygonIntersectionResult();
}
public static PolygonIntersectionResult ofIntersection(PolygonPolygonIntersection intersection) {
return new PolygonIntersectionResult(intersection);
}
private PolygonIntersectionResult() {
}
private PolygonIntersectionResult(PolygonPolygonIntersection intersection) {
this.intersection = intersection;
intersecting = true;
}
public boolean arePolygonsIntersecting() {
return intersecting;
}
public PolygonPolygonIntersection getIntersection() {
return intersection;
}
public static PolygonIntersectionResult ofIntersections(List<PolygonPolygonIntersection> ppIntersections) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
}
/*-
* 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 PolygonNs extends EdgePolygon {
public PolygonNs(List<HalfEdge> list) {
super(list);
}
}
/*-
* 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 PolygonPolygonIntersection {
private EdgePolygon p1;
private EdgePolygon p2;
private PolyLine polyLine;
private IntersectionType type;
public PolygonPolygonIntersection(EdgePolygon p1, EdgePolygon p2, PolyLine polyLine, IntersectionType type) {
this.p1 = p1;
this.p2 = p2;
this.polyLine = polyLine;
this.type = type;
}
public PolygonPolygonIntersection(EdgePolygon p1, EdgePolygon p2, PolyLine polyLine) {
this(p1, p2, polyLine, IntersectionType.NORMAL_INTERSECTION);
}
public PolyLine getPolyLine() {
return polyLine;
}
public IntersectionType getIntersectionType() {
return type;
}
public EdgePolygon getPolygon1() {
return p1;
}
public EdgePolygon getPolygon2() {
return p2;
}
}
/*-
* 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 ProjectedPoint3d {
private Point3d point;
private double parameter;
public ProjectedPoint3d(Point3d point, double d) {
this.point = point;
this.parameter = d;
}
public Point3d getPoint() {
return point;
}
public double getParameter() {
return parameter;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment