-
Notifications
You must be signed in to change notification settings - Fork 84
/
Copy pathPolygonBuilder.cpp
209 lines (193 loc) · 7.52 KB
/
PolygonBuilder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright 2015, Christopher J. Foster and the other displaz contributors.
// Use of this code is governed by the BSD-style license found in LICENSE.txt
#include "PolygonBuilder.h"
#include "polypartition/polypartition.h"
#include "QtLogger.h"
// Initialize polypartition TPPLPoly from a list of indices and vertices
//
// verts - 3D polygon vertex vectors
// xind,yind - Indices of 3D vectors to extract as x and y coordinates for 2D
// triangulation computation.
// inds,size - Array of indices into the `verts` list
// isHole - Value for the hole flag, and to determine the orientation
static void initTPPLPoly(TPPLPoly& poly,
const std::vector<float>& verts,
int xind, int yind,
const GLuint* inds, int size,
bool isHole)
{
// Check for explicitly closed polygons (last and first vertices equal) and
// discard the last vertex in these cases. This is a pretty stupid
// convention, but the OGC have blessed it and now we've got a bunch of
// geospatial formats (kml, WKT, GeoJSON) which require it. Sigh.
// http://gis.stackexchange.com/questions/10308/why-do-valid-polygons-repeat-the-same-start-and-end-point/10309#10309
if (inds[0] == inds[size-1] ||
(verts[3*inds[0]+0] == verts[3*inds[size-1]+0] &&
verts[3*inds[0]+1] == verts[3*inds[size-1]+1] &&
verts[3*inds[0]+2] == verts[3*inds[size-1]+2]))
{
g_logger.warning_limited("Ignoring duplicate final vertex in explicitly closed polygon");
size -= 1;
}
// Copy into polypartition data structure
poly.Init(size);
for (int i = 0; i < size; ++i)
{
poly[i].x = verts[3*inds[i]+xind];
poly[i].y = verts[3*inds[i]+yind];
poly[i].id = inds[i];
}
int orientation = poly.GetOrientation();
// Invert so that outer = ccw, holes = cw
if ((orientation == TPPL_CW) ^ isHole)
poly.Invert();
poly.SetHole(isHole);
}
// Triangulate a polygon, returning vertex indices to the triangles
//
// verts - 3D vertex positions
// outerRingInds - Indices of outer boundary vertices in `verts` array;
// the j'th component of the i'th polygon vertex is
// verts[3*outerRingInds[i]+j].
// innerRingSizes - Number of vertices in each polygon hole
// innerRingInds - Indices of vertices for all holes; the first hole has
// indices innerRingInds[i] for i in [0, innerRingSize[0])
// triangleInds - Indices of triangular pieces are appended to this array.
//
// Return false if polygon couldn't be triangulated.
static bool triangulatePolygon(const std::vector<float>& verts,
const std::vector<GLuint>& outerRingInds,
const std::vector<GLuint>& innerRingSizes,
const std::vector<GLuint>& innerRingInds,
std::vector<GLuint>& triangleInds)
{
// Figure out which axis the normal is maximum along, and discard it to
// reduce dimensionality to 2D. The polygon is guaranteed to be
// non-singular in the remaining dimensions.
V3d normal = polygonNormal(verts, outerRingInds);
normal.x = std::abs(normal.x);
normal.y = std::abs(normal.y);
normal.z = std::abs(normal.z);
int ind1 = 0;
int ind2 = 1;
if (normal.z < std::max(normal.x, normal.y))
{
if (normal.x > normal.y)
ind1 = 2;
else
ind2 = 2;
}
std::list<TPPLPoly> triangles;
if (innerRingSizes.empty())
{
TPPLPoly poly;
// Simple case - no holes
//
// TODO: Use Triangulate_MONO, after figuring out why it's not always
// working?
initTPPLPoly(poly, verts, ind1, ind2,
outerRingInds.data(), (int)outerRingInds.size(), false);
TPPLPartition polypartition;
if (!polypartition.Triangulate_EC(&poly, &triangles))
{
g_logger.warning("Ignoring polygon which couldn't be triangulated.");
return false;
}
}
else
{
// Set up outer polygon boundary and holes.
std::list<TPPLPoly> inputPolys;
inputPolys.resize(1 + innerRingSizes.size());
auto polyIter = inputPolys.begin();
initTPPLPoly(*polyIter, verts, ind1, ind2,
outerRingInds.data(), (int)outerRingInds.size(), false);
++polyIter;
for (size_t i = 0, j = 0; i < innerRingSizes.size(); ++i, ++polyIter)
{
size_t count = innerRingSizes[i];
if (j + count > innerRingInds.size())
{
g_logger.warning("Ignoring polygon with inner ring %d of %d too large",
i, innerRingSizes.size());
return false;
}
initTPPLPoly(*polyIter, verts, ind1, ind2,
innerRingInds.data() + j, (int)count, true);
j += count;
}
// Triangulate
std::list<TPPLPoly> noHolePolys;
TPPLPartition polypartition;
polypartition.RemoveHoles(&inputPolys, &noHolePolys);
if (!polypartition.Triangulate_EC(&noHolePolys, &triangles))
{
g_logger.warning("Ignoring polygon which couldn't be triangulated.");
return false;
}
}
for (auto tri = triangles.begin(); tri != triangles.end(); ++tri)
{
triangleInds.push_back((*tri)[0].id);
triangleInds.push_back((*tri)[1].id);
triangleInds.push_back((*tri)[2].id);
}
return true;
}
//------------------------------------------------------------------------------
PolygonBuilder::PolygonBuilder()
: m_valid(true),
m_vertexCount(0),
m_propsAvail(OuterRingInds),
m_propsRead(0)
{ }
bool PolygonBuilder::addIndex(long propType, long plyListLength,
long plyListIndex, long vertexIndex)
{
assert(propType & m_propsAvail);
m_propsRead |= propType; // support any ply property order
size_t currSize = 0;
if (plyListLength != 0 && plyListIndex >= 0)
{
switch (propType)
{
# define ADD_IND(vec) vec.push_back(vertexIndex); currSize = vec.size();
case OuterRingInds: ADD_IND(m_outerRingInds); break;
case InnerRingSizes: ADD_IND(m_innerRingSizes); break;
case InnerRingInds: ADD_IND(m_innerRingInds); break;
# undef ADD_IND
default: assert(0 && "Unexpected index type");
}
if (propType != InnerRingSizes &&
(vertexIndex < 0 || vertexIndex >= m_vertexCount))
{
g_logger.warning("Vertex index %d outside of valid range [0,%d] - ignoring polygon",
vertexIndex, m_vertexCount-1);
m_valid = false;
}
}
return m_propsRead == m_propsAvail && (long)currSize == plyListLength;
}
void PolygonBuilder::triangulate(const std::vector<float>& verts,
std::vector<GLuint>& triangleInds)
{
if (!m_valid)
return;
if (m_outerRingInds.size() == 3 && m_innerRingSizes.empty())
{
// Already a triangle - nothing to do.
for (int i = 0; i < 3; ++i)
triangleInds.push_back(m_outerRingInds[i]);
return;
}
triangulatePolygon(verts, m_outerRingInds, m_innerRingSizes,
m_innerRingInds, triangleInds);
}
void PolygonBuilder::reset()
{
m_valid = true;
m_propsRead = 0;
m_outerRingInds.clear();
m_innerRingSizes.clear();
m_innerRingInds.clear();
}