// Original comment: // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two // most signficant axes. Here we find the two longest axes and use them to triangulate. Inverse transforming them would // introduce more doubling point error and isn't worth it. // // SC says: // This doesn't work: the vertices can be collinear when projected onto the plane of the two longest axes of the bounding box. // Example (from real data): // // v[0] = (-13.7199, 4.45725, -8.00059) // v[1] = (-0.115787, 12.3116, -4.96109) // v[2] = (0.88992, 12.8922, -3.80342) // v[3] = (-0.115787, 12.3116, -2.64576) // v[4] = (-13.7199, 4.45725, 0.393742) // v[5] = (-13.7199, 4.45725, -0.856258) // v[6] = (-12.5335, 5.14221, -3.80342) // v[7] = (-13.7199, 4.45725, -6.75059) // // Instead, we will project onto the plane of the polygon. long Polygon3::triangulate(Array<long> & tri_indices, Real epsilon) const { if (epsilon < 0) epsilon = Math::eps<Real>(); if (vertices.size() < 3) { tri_indices.clear(); } else if (vertices.size() == 3) { tri_indices.resize(3); tri_indices[0] = vertices[0].index; tri_indices[1] = vertices[1].index; tri_indices[2] = vertices[2].index; } else if (vertices.size() > 3) { tri_indices.clear(); size_t n = vertices.size(); proj_vertices.resize(n); Vector3 normal = computeNormal(); Matrix3 basis = Math::orthonormalBasis(normal); Vector3 axis0 = basis.col(0); Vector3 axis1 = basis.col(1); Vector3 v0 = vertices[0].position; // a reference point for the plane of the polygon for (size_t i = 0; i < n; ++i) { Vector3 v = vertices[i].position - v0; proj_vertices[i] = Vector2(v.dot(axis0), v.dot(axis1)); } Array<size_t> indices(n); bool flipped = false; if (projArea() > 0) { for (size_t v = 0; v < n; ++v) indices[v] = v; } else { for (size_t v = 0; v < n; ++v) indices[v] = (n - 1) - v; flipped = true; } size_t nv = n; size_t count = 2 * nv; for (size_t v = nv - 1; nv > 2; ) { if ((count--) <= 0) break; size_t u = v; if (nv <= u) u = 0; v = u + 1; if (nv <= v) v = 0; size_t w = v + 1; if (nv <= w) w = 0; if (snip(u, v, w, nv, indices, epsilon)) { size_t a = indices[u]; size_t b = indices[v]; size_t c = indices[w]; if (flipped) { tri_indices.push_back(vertices[c].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[a].index); } else { tri_indices.push_back(vertices[a].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[c].index); } size_t s = v, t = v + 1; for ( ; t < nv; ++s, ++t) indices[s] = indices[t]; nv--; count = 2 * nv; } } } return (long)tri_indices.size() / 3; }
// Original comment: // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two // most signficant axes. Here we find the two longest axes and use them to triangulate. Inverse transforming them would // introduce more doubling point error and isn't worth it. // // SC says: // This doesn't work: the vertices can be collinear when projected onto the plane of the two longest axes of the bounding box. // Example (from real data): // // v[0] = (-13.7199, 4.45725, -8.00059) // v[1] = (-0.115787, 12.3116, -4.96109) // v[2] = (0.88992, 12.8922, -3.80342) // v[3] = (-0.115787, 12.3116, -2.64576) // v[4] = (-13.7199, 4.45725, 0.393742) // v[5] = (-13.7199, 4.45725, -0.856258) // v[6] = (-12.5335, 5.14221, -3.80342) // v[7] = (-13.7199, 4.45725, -6.75059) // // Instead, we will project onto the plane of the polygon. long Polygon3::triangulate(TheaArray<long> & tri_indices) const { if (vertices.size() < 3) { tri_indices.clear(); } else if (vertices.size() == 3) { tri_indices.resize(3); tri_indices[0] = vertices[0].index; tri_indices[1] = vertices[1].index; tri_indices[2] = vertices[2].index; } else if (vertices.size() > 3) { tri_indices.clear(); array_size_t n = vertices.size(); proj_vertices.resize(n); Vector3 normal = getNormal(); Vector3 axis0, axis1; normal.createOrthonormalBasis(axis0, axis1); Vector3 v0 = vertices[0].position; // a reference point for the plane of the polygon for (array_size_t i = 0; i < n; ++i) { Vector3 v = vertices[i].position - v0; proj_vertices[i] = Vector2(v.dot(axis0), v.dot(axis1)); } TheaArray<array_size_t> indices(n); bool flipped = false; if (projArea() > 0) { for (array_size_t v = 0; v < n; ++v) indices[v] = v; } else { for (array_size_t v = 0; v < n; ++v) indices[v] = (n - 1) - v; flipped = true; } array_size_t nv = n; array_size_t count = 2 * nv; for (array_size_t v = nv - 1; nv > 2; ) { if ((count--) <= 0) break; array_size_t u = v; if (nv <= u) u = 0; v = u + 1; if (nv <= v) v = 0; array_size_t w = v + 1; if (nv <= w) w = 0; if (snip(u, v, w, nv, indices)) { array_size_t a = indices[u]; array_size_t b = indices[v]; array_size_t c = indices[w]; if (flipped) { tri_indices.push_back(vertices[c].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[a].index); } else { tri_indices.push_back(vertices[a].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[c].index); } array_size_t s = v, t = v + 1; for ( ; t < nv; ++s, ++t) indices[s] = indices[t]; nv--; count = 2 * nv; } } } return (long)tri_indices.size() / 3; }