/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO 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 General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- 01590 Fachpraktikum "Erweiterbare Datenbanksysteme" WS 2014 / 2015 //paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}] //paragraph [10] Footnote: [{\footnote{] [}}] //[TOC] [\tableofcontents] [1] Implementation of a Spatial3D algebra [TOC] 1 Includes and Defines */ #include #include "Spatial3D.h" #include "geometric_algorithm.h" #include "geometric_algorithm_intersection_line_plane.h" #include "geometric_algorithm_intersection_triangles.h" using namespace std; namespace spatial3d_geometric { TriangleIntersectionResult::TriangleIntersectionType TriangleIntersectionResult::getIntersectionType() { return intersectionType; } vector& TriangleIntersectionResult::getIntersectionPoints() { return intersectionPoints; } TriangleIntersectionResult::TriangleIntersectionResult() : intersectionPoints() { } TriangleIntersectionResult::TriangleIntersectionResult( vector _intersectionPoints, TriangleIntersectionType type) : intersectionPoints(_intersectionPoints), intersectionType(type) { } TriangleIntersectionResult intersection(const Plane3d& plane, const Triangle& triangle) { vector resultSegment; vector pointsLeft; vector pointsRight; vector pointsOn; SimplePoint3d points[3] = { triangle.getA(), triangle.getB(), triangle.getC() }; for (int c = 0; c < 3; ++c) { double side = plane.getNormalVector() * Vector3d(plane.getPoint(), points[c]); if (AlmostEqual(side, 0)) { pointsOn.push_back(points[c]); } else if (side < 0) { pointsLeft.push_back(points[c]); } else // if (site > 0) { pointsRight.push_back(points[c]); } } if (pointsLeft.size() == 3 || pointsRight.size() == 3) { // All points are on the same side of the plane: triangle cannot intersect return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } if (pointsOn.size() == 3) { return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::AREA); } if (pointsOn.size() == 2) { resultSegment = pointsOn; return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } if (pointsOn.size() == 1 && pointsLeft.size() == 1) { resultSegment = pointsOn; SimplePoint3d intersectionPoint = intersection(pointsLeft[0], pointsRight[0], plane) .getIntersectionPoint(); resultSegment.push_back(intersectionPoint); return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } if (pointsOn.size() == 1) { resultSegment = pointsOn; return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::POINT); } else { // two points on one side, one point on the other side. for (vector::iterator l = pointsLeft.begin(); l != pointsLeft.end(); ++l) { for (vector::iterator r = pointsRight.begin(); r != pointsRight.end(); ++r) { resultSegment.push_back( intersection(*l, *r, plane).getIntersectionPoint()); } } return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } } // helper function for some cases in intersection(Triangle&, Triangle&) below TriangleIntersectionResult::TriangleIntersectionType intersectionType(const SimplePoint3d& point, const Triangle& triangle, vector& result) { switch(pointInsideTriangle(point, triangle)) { case OUTSIDE: return TriangleIntersectionResult::NO_INTERSECTION; default: result.push_back(point); return TriangleIntersectionResult::POINT; } } TriangleIntersectionResult intersection(const Triangle& t1, const Triangle& t2) { Plane3d plane1 = t1.getPlane(); Plane3d plane2 = t2.getPlane(); vector resultSegment; TriangleIntersectionResult res1 = intersection(plane1, t2); TriangleIntersectionResult res2 = intersection(plane2, t1); if (res1.getIntersectionType() == TriangleIntersectionResult::AREA || res2.getIntersectionType() == TriangleIntersectionResult::AREA) { // Triangles are on the same plane. Transformation2d t(plane1); SimplePoint2d triangle1_2d[3] = { t.transform(t1.getA()), t.transform(t1.getB()), t.transform(t1.getC()) }; SimplePoint2d triangle2_2d[3] = { t.transform(t2.getA()), t.transform(t2.getB()), t.transform(t2.getC()) }; InsideResult pointsOf1Inside2[3]; InsideResult pointsOf2Inside1[3]; for (int c = 0; c < 3; ++c) { pointsOf1Inside2[c] = pointInsideTriangle(triangle1_2d[c], triangle2_2d[0], triangle2_2d[1], triangle2_2d[2]); pointsOf2Inside1[c] = pointInsideTriangle(triangle2_2d[c], triangle1_2d[0], triangle1_2d[1], triangle1_2d[2]); } int number_of_points_not_outside_1 = 0; int number_of_points_not_outside_2 = 0; for (int c = 0; c < 3; ++c) { if (pointsOf1Inside2[c] == INSIDE || pointsOf2Inside1[c] == INSIDE) { // If any point is on the inside of the other triangle, // we can be sure to have an overlap area. return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::AREA); } if (pointsOf2Inside1[c] != OUTSIDE) { ++ number_of_points_not_outside_1; } if (pointsOf1Inside2[c] != OUTSIDE) { ++ number_of_points_not_outside_2; } } if (number_of_points_not_outside_1 == 3 || number_of_points_not_outside_2 == 3) { return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::AREA); } // Now that we know that no triangle is completely inside the other // triangle, we can tell whether they intersect by searching for // intersections of the edges. bool intersection_of_edges[3][3]; SimplePoint2d intersectionPoints[3][3]; int number_of_intersections = 0; for (int c1 = 0; c1 < 3; ++c1) { for (int c2 = 0; c2 < 3; ++c2) { intersection_of_edges[c1][c2] = doSegmentsIntersect( triangle1_2d[c1], triangle1_2d[(c1 + 1) % 3], // first edge triangle2_2d[c2], triangle2_2d[(c2 + 1) % 3]); if (intersection_of_edges[c1][c2]) { SimplePoint2d out_point; ++ number_of_intersections; lineIntersectionPoint( triangle1_2d[c1], triangle1_2d[(c1 + 1) % 3], // first edge triangle2_2d[c2], triangle2_2d[(c2 + 1) % 3], out_point); intersectionPoints[c1][c2] = out_point; if (!almostEqual(out_point, triangle1_2d[c1]) && !almostEqual(out_point, triangle1_2d[(c1 + 1) % 3]) && !almostEqual(out_point, triangle2_2d[c2]) && !almostEqual(out_point, triangle2_2d[(c2 + 1) % 3])) { // If two segments intersect, but the intersection point is not // the end of either segment, then we know for sure that we // have an intersection area. return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::AREA); } } } } if (number_of_intersections == 0) { return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } // There are intersections, so we now know the triangles intersect. // We still have to find out whether it is an intersection point or // an intersection segment. (Area is ruled out before.) // To find out, compare the intersection points. If its the same point // for all intersections, we have an intersection point. If its two // different points, we have an intersection segment between these // points. Three points are impossible without an intersection area. vector resultSegment2d; for (int c1 = 0; c1 < 3; ++c1) { for (int c2 = 0; c2 < 3; ++c2) { if (intersection_of_edges[c1][c2]) { // compare to intersection points we already have bool pointAlreadyKnown = false; for (vector::iterator d = resultSegment2d.begin(); d != resultSegment2d.end(); ++d) { if (almostEqual(*d, intersectionPoints[c1][c2])) { pointAlreadyKnown = true; } } if (!pointAlreadyKnown) { resultSegment2d.push_back(intersectionPoints[c1][c2]); } } } } for (vector::iterator it = resultSegment2d.begin(); it != resultSegment2d.end(); ++it) { resultSegment.push_back(t.transform(*it)); } return TriangleIntersectionResult(resultSegment, resultSegment.size() == 1 ? TriangleIntersectionResult::POINT : TriangleIntersectionResult::SEGMENT); } else if (collinear(plane1.getNormalVector(), plane2.getNormalVector())) { // Triangle planes are not identical, but parallel. return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } else { if (res1.getIntersectionType() == TriangleIntersectionResult::NO_INTERSECTION || res2.getIntersectionType() == TriangleIntersectionResult::NO_INTERSECTION) { // A triangle does not intersect the plane of the other triangle return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } if (res1.getIntersectionType() == TriangleIntersectionResult::POINT) { // Triangle2 shares a single point with the plane of triangle1. // Is it inside of triangle1? TriangleIntersectionResult::TriangleIntersectionType resultType = intersectionType(res1.getIntersectionPoints()[0], t1, resultSegment); return TriangleIntersectionResult(resultSegment, resultType); } if (res2.getIntersectionType() == TriangleIntersectionResult::POINT) { TriangleIntersectionResult::TriangleIntersectionType resultType = intersectionType(res2.getIntersectionPoints()[0], t2, resultSegment); return TriangleIntersectionResult(resultSegment, resultType); } // Both intersections are segments that are on a common line. // To find out whether they overlap, project them onto an axis of // the coordinate system. Choose the axis where the coordinates // have the largest difference. double min_value[3], max_value[3]; for(int axis = X; axis <= Z; axis++) { min_value[axis] = res1.getIntersectionPoints()[0].get((Axis)axis); min_value[axis] = min(min_value[axis], res1.getIntersectionPoints()[1].get((Axis)axis)); min_value[axis] = min(min_value[axis], res2.getIntersectionPoints()[0].get((Axis)axis)); min_value[axis] = min(min_value[axis], res2.getIntersectionPoints()[1].get((Axis)axis)); max_value[axis] = res1.getIntersectionPoints()[0].get((Axis)axis); max_value[axis] = max(max_value[axis], res1.getIntersectionPoints()[1].get((Axis)axis)); max_value[axis] = max(max_value[axis], res2.getIntersectionPoints()[0].get((Axis)axis)); max_value[axis] = max(max_value[axis], res2.getIntersectionPoints()[1].get((Axis)axis)); } Axis chosenAxis = X; double max_difference = max_value[X] - min_value[X]; for (int axis = Y; axis <= Z; axis++) { if (max_value[axis] - min_value[axis] > max_difference) { chosenAxis = (Axis)axis; max_difference = max_value[axis] - min_value[axis]; } } double segment1[2], segment2[2]; segment1[0] = res1.getIntersectionPoints()[0].get(chosenAxis); segment1[1] = res1.getIntersectionPoints()[1].get(chosenAxis); segment2[0] = res2.getIntersectionPoints()[0].get(chosenAxis); segment2[1] = res2.getIntersectionPoints()[1].get(chosenAxis); double minSegment1 = min(segment1[0], segment1[1]); double maxSegment1 = max(segment1[0], segment1[1]); double minSegment2 = min(segment2[0], segment2[1]); double maxSegment2 = max(segment2[0], segment2[1]); if (AlmostEqual(minSegment1, maxSegment2)) { // Overlapping with a single point if (segment1[0] == minSegment1) { resultSegment.push_back(res1.getIntersectionPoints()[0]); } else { resultSegment.push_back(res1.getIntersectionPoints()[1]); } return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::POINT); } if (AlmostEqual(minSegment2, maxSegment1)) { // Overlapping with a single point if (segment2[0] == minSegment2) { resultSegment.push_back(res2.getIntersectionPoints()[0]); } else { resultSegment.push_back(res2.getIntersectionPoints()[1]); } return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::POINT); } if (minSegment1 > maxSegment2 && !AlmostEqual(minSegment1, maxSegment2)) { return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } if (minSegment2 > maxSegment1 && !AlmostEqual(minSegment2, maxSegment1)) { return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::NO_INTERSECTION); } if (minSegment1 >= minSegment2 && maxSegment1 <= maxSegment2) { // Segment 1 is completely inside of segment 2. Overlap is segment 1. resultSegment.push_back(res1.getIntersectionPoints()[0]); resultSegment.push_back(res1.getIntersectionPoints()[1]); return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } if (minSegment2 >= minSegment1 && maxSegment2 <= maxSegment1) { // Segment 2 is completely inside of segment 1. Overlap is segment 2. resultSegment.push_back(res2.getIntersectionPoints()[0]); resultSegment.push_back(res2.getIntersectionPoints()[1]); return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } // Now we know that the segments overlap and its a segment, not a point. // For the left point, choose the maximum of the minimum. // For the right point, choose the minimum of the maximum. double left1d = max(minSegment1, minSegment2); double right1d = min(maxSegment1, maxSegment2); // Now we have to find the original points. SimplePoint3d left3d, right3d; SimplePoint3d points[4] = { res1.getIntersectionPoints()[0], res1.getIntersectionPoints()[1], res2.getIntersectionPoints()[0], res2.getIntersectionPoints()[1] }; for(int c = 0; c < 4; ++c) { if (points[c].get(chosenAxis) == left1d) { left3d = points[c]; } if (points[c].get(chosenAxis) == right1d) { right3d = points[c]; } } resultSegment.push_back(left3d); resultSegment.push_back(right3d); return TriangleIntersectionResult(resultSegment, TriangleIntersectionResult::SEGMENT); } } }