Loading [MathJax]/extensions/tex2jax.js

dune-mmesh (unstable)

polygoncutting.hh
1#ifndef DUNE_MMESH_GRID_POLYGONCUTTING_HH
2#define DUNE_MMESH_GRID_POLYGONCUTTING_HH
3
4#include <algorithm>
5#include <iostream>
6
7namespace Dune {
8template <class Scalar, class Point>
9class PolygonCutting {
10 public:
11 // implements the Sutherland-Hodgman algorithm to determine the polygon that
12 // emerges from clipping a subject polygon with respect to the edges of a
13 // clip polygon, subject polygon and clip polygon as well as the resulting
14 // polygon are represented by lists of their corners points with the points
15 // arranged in a consecutive counterclockwise order
16 template <typename Polygon>
17 static std::vector<Point> sutherlandHodgman(const Polygon& subjectPolygon,
18 const Polygon& clipPolygonInput) {
19 std::vector<Point> outputPolygon(subjectPolygon.begin(),
20 subjectPolygon.end());
21 std::vector<Point> clipPolygon(clipPolygonInput.begin(),
22 clipPolygonInput.end());
23
24 const int clipSize = clipPolygon.size();
25
26 // iterate over the edges of the clipping polygon represented by
27 // consecutive points (indices clipIdx and clipIdxNext) in clipPolygon
28 for (int clipIdx = 0; clipIdx < clipSize; clipIdx++) {
29 const int clipIdxNext = (clipIdx + 1) % clipSize;
30
31 std::vector<Point> inputPolygon(outputPolygon);
32 outputPolygon.clear();
33 const int inputSize = inputPolygon.size();
34
35 // iterate over the edges of the subject polygon represented by
36 // consectutive points (indices subjIdx and subjIdxNext) and clip each
37 // subject edge with respect to current clipping edge (indices clipIdx
38 // and clipIdxNext)
39 for (int inputIdx = 0; inputIdx < inputSize; inputIdx++) {
40 const int inputIdxNext = (inputIdx + 1) % inputSize;
41
42 // Case differentation: Determine the positions of the corner points
43 // of the subject edge p1, p2 with respect to the current clipping
44 // edge given as the line from q1 to q2.
45 // This is done by evaluating the cross product
46 // d := (q1 - q2) x (p_i - q_1) = [(q1 - q2) x p_i] + q2 x q1
47 // for i = 1, 2.
48 // d < 0 => p_i inside, d > 0 => p_i outside,
49 // d = 0 => p_i on clipping edge
50
51 Point deltaQ = clipPolygon[clipIdxNext] - clipPolygon[clipIdx];
52
53 Scalar q2_cross_q1 =
54 clipPolygon[clipIdx][1] * clipPolygon[clipIdxNext][0] -
55 clipPolygon[clipIdx][0] * clipPolygon[clipIdxNext][1];
56
57 Scalar firstPos = -deltaQ[0] * inputPolygon[inputIdx][1] +
58 deltaQ[1] * inputPolygon[inputIdx][0] + q2_cross_q1;
59 Scalar secondPos = -deltaQ[0] * inputPolygon[inputIdxNext][1] +
60 deltaQ[1] * inputPolygon[inputIdxNext][0] +
61 q2_cross_q1;
62
63 static constexpr double EPSILON = 1e-14;
64 // case 1: first point outside, second point inside
65 if (firstPos > EPSILON && secondPos < -EPSILON) {
66 // add intersection point and second point to output polygon
67 outputPolygon.push_back(lineIntersectionPoint(
68 inputPolygon[inputIdx], inputPolygon[inputIdxNext],
69 clipPolygon[clipIdx], clipPolygon[clipIdxNext]));
70 outputPolygon.push_back(inputPolygon[inputIdxNext]);
71 }
72
73 // case 2: first point inside, second point outside
74 else if (firstPos < -EPSILON && secondPos > EPSILON) {
75 // add intersection point to output polygon
76 outputPolygon.push_back(lineIntersectionPoint(
77 inputPolygon[inputIdx], inputPolygon[inputIdxNext],
78 clipPolygon[clipIdx], clipPolygon[clipIdxNext]));
79 }
80
81 // case 3: first point outside or on the edge, scond point outside
82 else if (firstPos >= -EPSILON && secondPos > EPSILON) {
83 // do nothing
84 continue;
85 }
86
87 // case 4: first point inside, second point inside or on the edge
88 else {
89 // add second point to output polygon
90 outputPolygon.push_back(inputPolygon[inputIdxNext]);
91 }
92 }
93 }
94
95 return outputPolygon;
96 }
97
98 // computes the point of intersection in 2d between the line given by the
99 // points p1, p2 and the line defined by the points q1, q2
100 static Point lineIntersectionPoint(const Point& p1, const Point& p2,
101 const Point& q1, const Point& q2) {
102 Point deltaP = p1 - p2;
103 Point deltaQ = q1 - q2;
104
105 Scalar scaleFactor = deltaP[0] * deltaQ[1] - deltaQ[0] * deltaP[1];
106
107 deltaP *= q1[0] * q2[1] - q1[1] * q2[0];
108 deltaQ *= p1[0] * p2[1] - p1[1] * p2[0];
109
110 Point intersection = deltaQ - deltaP;
111 intersection /= scaleFactor;
112
113 return intersection;
114 }
115
116 // computes the area of a convex polygon by evaluating the shoelace formula,
117 // the polygon is represented by a corner point list with the points arranged
118 // consecutively in counterclockwise order (for clockwise order (-1)*area
119 // will be returned)
120 template <typename Points>
121 static Scalar polygonArea(const Points& polygonPoints) {
122 const int polygonSize = polygonPoints.size();
123
124 if (polygonSize < 3) {
125 return 0.0;
126 }
127
128 Scalar area(0.0);
129
130 for (int i = 0; i < polygonSize; i++) {
131 const int j = (i + 1) % polygonSize;
132 area += polygonPoints[i][0] * polygonPoints[j][1];
133 area -= polygonPoints[j][0] * polygonPoints[i][1];
134 }
135
136 return 0.5 * area;
137 }
138
139#if CGAL_INTERSECTION
140 template <typename Triang>
141 static Scalar intersectionVolumeCGAL(const Triang& subjectTriangle,
142 const Triang& clipTriangle) {
143 using Epeck = CGAL::Exact_predicates_exact_constructions_kernel;
144 using PointCGAL = CGAL::Point_2<Epeck>;
145 using Triangle = CGAL::Triangle_2<Epeck>;
146
147 std::array<PointCGAL, 3> v;
148 for (int i = 0; i < 3; ++i) {
149 const auto& p = makePoint(subjectTriangle[i]);
150 v[i] = PointCGAL(p.x(), p.y());
151 }
152 const Triangle triangle1(v[0], v[1], v[2]);
153
154 for (int i = 0; i < 3; ++i) {
155 const auto& p = makePoint(clipTriangle[i]);
156 v[i] = PointCGAL(p.x(), p.y());
157 }
158 const Triangle triangle2(v[0], v[1], v[2]);
159
160 const auto result = CGAL::intersection(triangle1, triangle2);
161
162 using PointList = std::vector<PointCGAL>;
163 using Polygon = CGAL::Polygon_2<Epeck>;
164 if (result) {
165 // intersection is triangle
166 if (const Triangle* t = boost::get<Triangle>(&*result))
167 return std::abs(CGAL::to_double(
168 CGAL::area(t->vertex(0), t->vertex(1), t->vertex(2))));
169
170 // intersection is polygon
171 else if (const PointList* points = boost::get<PointList>(&*result)) {
172 Polygon polygon(points->begin(), points->end());
173 return std::abs(CGAL::to_double(polygon.area()));
174 }
175 }
176
177 // there is no intersection
178 return 0;
179 }
180#endif
181};
182} // namespace Dune
183
184#endif
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden & Uni Heidelberg  |  generated with Hugo v0.111.3 (Mar 16, 23:47, 2025)