diff --git a/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/IntersectPlanarPolygons.java b/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/IntersectPlanarPolygons.java index efcc9246416bdef586a83d663beaf059024176de..290b264797fa972d9c75ca085a83326bd35b0c3e 100644 --- a/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/IntersectPlanarPolygons.java +++ b/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/IntersectPlanarPolygons.java @@ -82,20 +82,7 @@ public class IntersectPlanarPolygons { for (Interval in2 : intervals2) { if (in1.isOverlapping(in2)) { Interval overlap = in1.overlap(in2); - Point3d start = straight.evaluate(overlap.getStart()); - if (overlap.getLength() < Global.getHighAccuracyTolerance() * 1e6) { - checkAndAddRealIntersectionPoint(cpPolygon1, cpPolygon2, start, intersections, epsilon); - } else { - Point3d end = straight.evaluate(overlap.getEnd()); - PolyLine pPL = new PolyLine(new Coordinate3d(start), new Coordinate3d(end)); - - if (!isPolyLineASharedEdge(cpPolygon1, pPL)) { - PolygonPolygonIntersection pPPI = new PolygonPolygonIntersection(cpPolygon1, cpPolygon2, - pPL); - intersections.add(pPPI); - } - - } + handleIntersectionIntervalOverlap(cpPolygon1, cpPolygon2, straight, intersections, epsilon, overlap); } else if (Math.abs(in1.getStart() - in2.getEnd()) < Global.getHighAccuracyTolerance()) { // check if the overlaps with a tolerance (numeric errors) Point3d point = straight.evaluate(in1.getStart()); @@ -108,6 +95,25 @@ public class IntersectPlanarPolygons { } } + private static void handleIntersectionIntervalOverlap(EdgePolygon cpPolygon1, EdgePolygon cpPolygon2, + GmStraight straight, List 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) { // get coordinates of the poly line Point3d polyLineStart = polyLine.getStart().getPoint(); @@ -136,10 +142,12 @@ public class IntersectPlanarPolygons { /** * - * Checks if the given point is a "real" intersection point and add's it to the - * intersection segments member variable. Real means, that this point isn't - * shared by both polygons as corner point. - * + * Checks if the given point is a 'real' intersection point and adds it to the + * intersection segments member variable. 'Real' meaning, that this point isn't + * a shared corner point of both polygons. + * + * @param p1 First polygon + * @param p2 Second polygon * @param point A possible intersection point */ private static void checkAndAddRealIntersectionPoint(EdgePolygon p1, EdgePolygon p2, Point3d point, @@ -200,7 +208,6 @@ public class IntersectPlanarPolygons { * 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 * intersection will be reduced to a 2d problem. - * * The intersection intervals contains pairs of parameters from the intersection * straight. If an intersection between the straight and the polygon contains * only a point, the according interval will contain the same parameter twice @@ -271,39 +278,7 @@ public class IntersectPlanarPolygons { GmBoundedStraight2d heStraight2d = polyPlane.projectOn2dStraight(heStraight); GmStraight2dIntersectionResult intersectionResult = heStraight2d.intersect(intersectingStraight2d); - if (intersectionResult.areParallel()) { - Vector2d dir = intersectingStraight2d.getDirection(); - Vector2d diffVec = heStraight2d.getOrigin().minus(intersectingStraight2d.getOrigin()); - double diffVecDotPerpDir = diffVec.dot(dir.getPerpendicularVector()); - - if (Math.abs(diffVecDotPerpDir) < Global.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); - } - } + analyseProjectedIntersection(intersectionResult, intersectingStraight2d, heStraight2d, intersectionValues, intersectedPolygonPoints); } processIntersectionIntervals(p, intersectingStraight, intersectionValues, intersectedPolygonPoints, @@ -312,6 +287,42 @@ public class IntersectPlanarPolygons { } + private static void analyseProjectedIntersection(GmStraight2dIntersectionResult intersectionResult, GmStraight2d intersectingStraight2d, GmBoundedStraight2d heStraight2d, TreeSet intersectionValues, TreeSet 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 * straight and polygon. It gets only parameters where the straight hits the @@ -350,43 +361,18 @@ public class IntersectPlanarPolygons { List valuesList = new ArrayList<>(intersectionValues); for (int i = 0; i < valuesList.size(); i++) { double i1 = valuesList.get(i); - i = i + 1; - if (i < valuesList.size()) { - double i2 = valuesList.get(i); + int j = i + 1; + if (j < valuesList.size()) { + double i2 = valuesList.get(j); // check if the double values are corner points of the polygon boolean gotPolygonPoint = intersectedPolygonPoints.contains(i1) || intersectedPolygonPoints.contains(i2); 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 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; - } - } - } - } - + Interval newLineInt = checkIntersectionInvervalPoints(pcPolygon, intersectingStraight, + intersectionIntervals, i1, i2); + if (newLineInt != null) { intersectionIntervals.add(newLineInt); - } else { - i--; } } else { intersectionIntervals.add(new Interval(i1, i2)); @@ -403,6 +389,37 @@ public class IntersectPlanarPolygons { } } + private static Interval checkIntersectionInvervalPoints(EdgePolygon pcPolygon, GmStraight intersectingStraight, List 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 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 * intersection parameter. If this is true, the parameter will be added as @@ -442,46 +459,9 @@ public class IntersectPlanarPolygons { List heList = new ArrayList<>(p1.getHalfEdges()); heList.addAll(p2.getHalfEdges()); - List heListColinear = new ArrayList<>(); - for (HalfEdge he : heList) { - - if (null != he.getPartner()) { - // this half edge is shared by both polygons ==> no intersection - // NOTE: The issue of more than 2 half edges per edge is solved by - // circular pointer, so we have to cycle threw all partners - HalfEdge pStartHE = he; - HalfEdge pPartnerHE = pStartHE.getPartner(); - boolean bothPolysShareThisEdge = false; - while (pStartHE != pPartnerHE && !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); + List heListCollinear = getCollinearHalfEdgeList(p1, p2, straight, epsilon, angleEpsilon, heList); - 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 // create parameter interval of the first projected half edge Point3d startPoint1 = he.getStraight().getOrigin(); @@ -489,7 +469,7 @@ public class IntersectPlanarPolygons { ProjectedPoint3d projP1 = straight.project(startPoint1); ProjectedPoint3d projP2 = straight.project(endPoint1); Interval int1 = new Interval(projP1.getParameter(), projP2.getParameter()); - for (HalfEdge he2 : heListColinear) { + for (HalfEdge he2 : heListCollinear) { if (he == he2) { continue; } @@ -526,8 +506,63 @@ public class IntersectPlanarPolygons { } } - private static boolean areStraightsColinear(GmBoundedStraight straight1, GmStraight straight2, double epsilon, - double angleEpsilon) { + private static List getCollinearHalfEdgeList(EdgePolygon p1, EdgePolygon p2, GmStraight straight, double epsilon, double angleEpsilon, List heList) { + List 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 rDir2 = straight2.getDir(); @@ -541,12 +576,9 @@ public class IntersectPlanarPolygons { ProjectedPoint3d foot1 = straight2.project(rOrigin); Point3d rTarget = straight1.getTarget(); ProjectedPoint3d foot2 = straight2.project(rTarget); - if ((foot1.getPoint().minus(rOrigin)).getLength() > epsilon - || (foot2.getPoint().minus(rTarget)).getLength() > epsilon) { - return false; - } - return true; - } + return !(((foot1.getPoint().minus(rOrigin)).getLength() > epsilon) + || ((foot2.getPoint().minus(rTarget)).getLength() > epsilon)); + } private static List handlePolygonsInSamePlane(GmPlane plane, EdgePolygon p1, EdgePolygon p2, double epsilon) {