| |
1 #include "src/triangulate.h" |
| |
2 #include "thirdparty/earcut.h" |
| |
3 #include "src/invert.h" |
| |
4 |
| |
5 // Make mapbox::earcut work with glm::vec3 |
| |
6 template<> struct mapbox::util::nth<0, glm::vec3> |
| |
7 { |
| |
8 static constexpr float get(const glm::vec3& t) { return t.x; } |
| |
9 }; |
| |
10 |
| |
11 template<> struct mapbox::util::nth<1, glm::vec3> |
| |
12 { |
| |
13 static constexpr float get(const glm::vec3& t) { return t.y; } |
| |
14 }; |
| |
15 |
| |
16 using indextype = std::uint16_t; |
| |
17 using indexpair = std::pair<indextype, indextype>; |
| |
18 struct boundaryinfo |
| |
19 { |
| |
20 indextype third; |
| |
21 std::size_t triangleid; |
| |
22 }; |
| |
23 using boundarymap_t = std::map<indexpair, boundaryinfo>; |
| |
24 |
| |
25 struct MergedTriangles |
| |
26 { |
| |
27 std::vector<Quadrilateral> quadrilaterals; |
| |
28 std::set<std::size_t> cutTriangles; |
| |
29 }; |
| |
30 |
| |
31 static bool findQuadsToMerge( |
| |
32 const boundarymap_t& boundaries, |
| |
33 auto&& iscut, |
| |
34 auto&& addMergedQuad |
| |
35 ) { |
| |
36 bool foundQuads = false; |
| |
37 // Go through triangle boundaries |
| |
38 for (boundarymap_t::const_iterator it1 = boundaries.begin(); it1 != boundaries.end(); ++it1) { |
| |
39 const indexpair& pair1 = it1->first; |
| |
40 const boundaryinfo& boundary1 = it1->second; |
| |
41 // .. the ones we haven't already been merged... |
| |
42 if (not iscut(boundary1.triangleid)) { |
| |
43 // Look for its inverse boundary to find the touching triangle |
| |
44 const indexpair inverse_boundary_key = std::make_pair(pair1.second, pair1.first); |
| |
45 const boundarymap_t::const_iterator it2 = boundaries.find(inverse_boundary_key); |
| |
46 // Also if that hasn't been cut |
| |
47 if (it2 != boundaries.end() and not iscut(it2->second.triangleid)) { |
| |
48 // Continue until no more new quads are found |
| |
49 foundQuads |= addMergedQuad(it1, it2); |
| |
50 } |
| |
51 } |
| |
52 } |
| |
53 return foundQuads; |
| |
54 } |
| |
55 |
| |
56 static MergedTriangles mergeTriangles( |
| |
57 const std::vector<std::uint16_t>& indices, |
| |
58 std::vector<glm::vec3>::const_iterator polyBegin) |
| |
59 { |
| |
60 MergedTriangles result; |
| |
61 const auto iscut = [&result](const std::size_t i){ |
| |
62 return result.cutTriangles.find(i) != result.cutTriangles.end(); |
| |
63 }; |
| |
64 const auto addMergedQuad = [&result, polyBegin]( |
| |
65 boundarymap_t::const_iterator it1, |
| |
66 boundarymap_t::const_iterator it2 |
| |
67 ) { |
| |
68 const indexpair& common_indices = it1->first; |
| |
69 const boundaryinfo& boundary_1 = it1->second; |
| |
70 const boundaryinfo& boundary_2 = it2->second; |
| |
71 const Quadrilateral quad{ |
| |
72 *(polyBegin + common_indices.first), |
| |
73 *(polyBegin + boundary_2.third), |
| |
74 *(polyBegin + common_indices.second), |
| |
75 *(polyBegin + boundary_1.third), |
| |
76 }; |
| |
77 if (isConvex(quad)) { |
| |
78 result.quadrilaterals.push_back(quad); |
| |
79 result.cutTriangles.insert(boundary_1.triangleid); |
| |
80 result.cutTriangles.insert(boundary_2.triangleid); |
| |
81 return true; |
| |
82 } |
| |
83 else { |
| |
84 return false; |
| |
85 } |
| |
86 }; |
| |
87 boundarymap_t boundaries; |
| |
88 for (std::size_t i = 0; i < indices.size(); i += 3) { |
| |
89 const auto add = [&](const std::size_t o1, const std::size_t o2, const std::size_t o3){ |
| |
90 const auto key = std::make_pair(indices[i + o1], indices[i + o2]); |
| |
91 boundaries[key] = {indices[i + o3], i}; |
| |
92 }; |
| |
93 add(0, 1, 2); |
| |
94 add(1, 2, 0); |
| |
95 add(2, 0, 1); |
| |
96 } |
| |
97 bool repeat = true; |
| |
98 while (repeat) { |
| |
99 repeat = findQuadsToMerge(boundaries, iscut, addMergedQuad); |
| |
100 } |
| |
101 return result; |
| |
102 } |
| |
103 |
| |
104 std::vector<PlainPolygonElement> polygonize( |
| |
105 std::vector<glm::vec3>::const_iterator begin, |
| |
106 std::vector<glm::vec3>::const_iterator end, |
| |
107 const glm::mat4& planeMatrix) |
| |
108 { |
| |
109 std::vector<PlainPolygonElement> result; |
| |
110 const glm::mat4 inverseGrid = glm::inverse(planeMatrix); |
| |
111 // mapbox::earcut will always produce a CCW polygon, so if we're drawing |
| |
112 // a CW polygon, we should invert the result afterwards |
| |
113 const float shouldInvert = glm::dot( |
| |
114 glm::vec3{inverseGrid[2]}, |
| |
115 glm::cross(*begin - *(begin + 1), *(begin + 2) - *(begin + 1))); |
| |
116 std::vector<std::vector<glm::vec3>> polygons{1}; |
| |
117 std::vector<glm::vec3>& polygon2d = polygons.back(); |
| |
118 polygon2d.reserve(static_cast<std::size_t>(end - begin)); |
| |
119 for (std::vector<glm::vec3>::const_iterator it = begin; it != end; ++it) { |
| |
120 polygon2d.push_back(inverseGrid * glm::vec4{*it, 1}); |
| |
121 } |
| |
122 const std::vector<indextype> indices = mapbox::earcut<std::uint16_t>(polygons); |
| |
123 MergedTriangles mergedTriangles = mergeTriangles(indices, begin); |
| |
124 for (Quadrilateral& quad : mergedTriangles.quadrilaterals) { |
| |
125 if (shouldInvert < 0) { |
| |
126 invert(quad); |
| |
127 } |
| |
128 result.push_back(quad); |
| |
129 } |
| |
130 for (std::size_t i = 0; i < indices.size(); i += 3) { |
| |
131 if (mergedTriangles.cutTriangles.find(i) == mergedTriangles.cutTriangles.end()) { |
| |
132 Triangle tri = triangle( |
| |
133 *(begin + indices[i]), |
| |
134 *(begin + indices[i + 1]), |
| |
135 *(begin + indices[i + 2])); |
| |
136 if (shouldInvert < 0) { |
| |
137 invert(tri); |
| |
138 } |
| |
139 result.push_back(tri); |
| |
140 } |
| |
141 } |
| |
142 return result; |
| |
143 } |