Fix of mesh decimation (the admesh library).

Fixes "Unable to save project (#2445)"
This commit is contained in:
bubnikv 2019-06-04 18:25:53 +02:00
parent 7a5d3de1c4
commit 3ab886b747
5 changed files with 495 additions and 481 deletions

View file

@ -37,7 +37,6 @@ static void stl_match_neighbors_nearby(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b); stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_record_neighbors(stl_file *stl, static void stl_record_neighbors(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b); stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_initialize_facet_check_exact(stl_file *stl);
static void stl_initialize_facet_check_nearby(stl_file *stl); static void stl_initialize_facet_check_nearby(stl_file *stl);
static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b); static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b);
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
@ -47,63 +46,90 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
stl_hash_edge *edge_a, stl_hash_edge *edge_b)); stl_hash_edge *edge_a, stl_hash_edge *edge_b));
static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_free_edges(stl_file *stl); static void stl_free_edges(stl_file *stl);
static void stl_remove_facet(stl_file *stl, int facet_number);
static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, static void stl_change_vertices(stl_file *stl, int facet_num, int vnot,
stl_vertex new_vertex); stl_vertex new_vertex);
static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
stl_hash_edge *edge_b, int *facet1, int *vertex1, stl_hash_edge *edge_b, int *facet1, int *vertex1,
int *facet2, int *vertex2, int *facet2, int *vertex2,
stl_vertex *new_vertex1, stl_vertex *new_vertex2); stl_vertex *new_vertex1, stl_vertex *new_vertex2);
static void stl_remove_degenerate(stl_file *stl, int facet);
extern int stl_check_normal_vector(stl_file *stl, extern int stl_check_normal_vector(stl_file *stl,
int facet_num, int normal_fix_flag); int facet_num, int normal_fix_flag);
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
static inline size_t hash_size_from_nr_faces(const size_t nr_faces)
{
// Good primes for addressing a cca. 30 bit space.
// https://planetmath.org/goodhashtableprimes
static std::vector<uint32_t> primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 };
// Find a prime number for 50% filling of the shared triangle edges in the mesh.
auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1);
return (it == primes.end()) ? primes.back() : *it;
}
// This function builds the neighbors list. No modifications are made // This function builds the neighbors list. No modifications are made
// to any of the facets. The edges are said to match only if all six // to any of the facets. The edges are said to match only if all six
// floats of the first edge matches all six floats of the second edge. // floats of the first edge matches all six floats of the second edge.
void stl_check_facets_exact(stl_file *stl) void stl_check_facets_exact(stl_file *stl)
{ {
if (stl->error) if (stl->error)
return; return;
stl->stats.connected_edges = 0; stl->stats.connected_edges = 0;
stl->stats.connected_facets_1_edge = 0; stl->stats.connected_facets_1_edge = 0;
stl->stats.connected_facets_2_edge = 0; stl->stats.connected_facets_2_edge = 0;
stl->stats.connected_facets_3_edge = 0; stl->stats.connected_facets_3_edge = 0;
// If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
// Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet // Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet
// will break the references. // will break the references.
for (int i = 0; i < stl->stats.number_of_facets;) { for (uint32_t i = 0; i < stl->stats.number_of_facets;) {
stl_facet &facet = stl->facet_start[i]; stl_facet &facet = stl->facet_start[i];
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) { if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
// Remove the degenerate facet. // Remove the degenerate facet.
facet = stl->facet_start[--stl->stats.number_of_facets]; facet = stl->facet_start[--stl->stats.number_of_facets];
stl->stats.facets_removed += 1; stl->stats.facets_removed += 1;
stl->stats.degenerate_facets += 1; stl->stats.degenerate_facets += 1;
} else } else
++ i; ++ i;
} }
// Connect neighbor edges. // Initialize hash table.
stl_initialize_facet_check_exact(stl); stl->stats.malloced = 0;
for (int i = 0; i < stl->stats.number_of_facets; i++) { stl->stats.freed = 0;
const stl_facet &facet = stl->facet_start[i]; stl->stats.collisions = 0;
for (int j = 0; j < 3; j++) { stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets);
stl_hash_edge edge; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
edge.facet_number = i; // initialize neighbors list to -1 to mark unconnected edges
edge.which_edge = j; stl->neighbors_start[i].neighbor[0] = -1;
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); stl->neighbors_start[i].neighbor[1] = -1;
insert_hash_edge(stl, edge, stl_record_neighbors); stl->neighbors_start[i].neighbor[2] = -1;
} }
} stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
stl_free_edges(stl); if (stl->heads == NULL)
perror("stl_initialize_facet_check_exact");
stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if (stl->tail == NULL)
perror("stl_initialize_facet_check_exact");
stl->tail->next = stl->tail;
for (int i = 0; i < stl->M; ++ i)
stl->heads[i] = stl->tail;
// Connect neighbor edges.
for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
const stl_facet &facet = stl->facet_start[i];
for (int j = 0; j < 3; ++ j) {
stl_hash_edge edge;
edge.facet_number = i;
edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_record_neighbors);
}
}
stl_free_edges(stl);
#if 0 #if 0
printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n",
stl->stats.number_of_facets, stl->stats.number_of_facets * 3, stl->stats.number_of_facets, stl->stats.number_of_facets * 3,
stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges);
#endif #endif
} }
@ -141,48 +167,6 @@ static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_ve
} }
} }
static inline size_t hash_size_from_nr_faces(const size_t nr_faces)
{
// Good primes for addressing a cca. 30 bit space.
// https://planetmath.org/goodhashtableprimes
static std::vector<uint32_t> primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 };
// Find a prime number for 50% filling of the shared triangle edges in the mesh.
auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1);
return (it == primes.end()) ? primes.back() : *it;
}
static void
stl_initialize_facet_check_exact(stl_file *stl) {
int i;
if (stl->error) return;
stl->stats.malloced = 0;
stl->stats.freed = 0;
stl->stats.collisions = 0;
stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets);
for (i = 0; i < stl->stats.number_of_facets ; i++) {
/* initialize neighbors list to -1 to mark unconnected edges */
stl->neighbors_start[i].neighbor[0] = -1;
stl->neighbors_start[i].neighbor[1] = -1;
stl->neighbors_start[i].neighbor[2] = -1;
}
stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
if(stl->heads == NULL) perror("stl_initialize_facet_check_exact");
stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if(stl->tail == NULL) perror("stl_initialize_facet_check_exact");
stl->tail->next = stl->tail;
for(i = 0; i < stl->M; i++) {
stl->heads[i] = stl->tail;
}
}
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
void (*match_neighbors)(stl_file *stl, void (*match_neighbors)(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b)) stl_hash_edge *edge_a, stl_hash_edge *edge_b))
@ -264,7 +248,7 @@ void stl_check_facets_nearby(stl_file *stl, float tolerance)
stl_initialize_facet_check_nearby(stl); stl_initialize_facet_check_nearby(stl);
for (int i = 0; i < stl->stats.number_of_facets; ++ i) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
//FIXME is the copy necessary? //FIXME is the copy necessary?
stl_facet facet = stl->facet_start[i]; stl_facet facet = stl->facet_start[i];
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
@ -348,7 +332,7 @@ static void stl_initialize_facet_check_nearby(stl_file *stl)
/* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/
/* tolerance *= 0.5;*/ /* tolerance *= 0.5;*/
stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets); stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets);
stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby");
@ -611,181 +595,170 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
} }
} }
static void static void remove_facet(stl_file *stl, int facet_number)
stl_remove_facet(stl_file *stl, int facet_number) { {
int neighbor[3]; assert(! stl->error);
int vnot[3]; ++ stl->stats.facets_removed;
int i; /* Update list of connected edges */
int j; stl_neighbors &neighbors = stl->neighbors_start[facet_number];
// Update statistics on unconnected triangle edges.
switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) {
case 0: // Facet has 3 neighbors
-- stl->stats.connected_facets_3_edge;
-- stl->stats.connected_facets_2_edge;
-- stl->stats.connected_facets_1_edge;
break;
case 1: // Facet has 2 neighbors
-- stl->stats.connected_facets_2_edge;
-- stl->stats.connected_facets_1_edge;
break;
case 2: // Facet has 1 neighbor
-- stl->stats.connected_facets_1_edge;
case 3: // Facet has 0 neighbors
break;
default:
assert(false);
}
if (stl->error) return; if (facet_number == -- stl->stats.number_of_facets)
// Removing the last face is easy, just forget the last face.
return;
stl->stats.facets_removed += 1; // Copy the face and neighborship from the last face to facet_number.
/* Update list of connected edges */ stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets];
j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) + neighbors = stl->neighbors_start[stl->stats.number_of_facets];
(stl->neighbors_start[facet_number].neighbor[1] == -1) + // Update neighborship of faces, which used to point to the last face, now moved to facet_number.
(stl->neighbors_start[facet_number].neighbor[2] == -1)); for (int i = 0; i < 3; ++ i)
if(j == 2) { if (neighbors.neighbor[i] != -1) {
stl->stats.connected_facets_1_edge -= 1; int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3];
} else if(j == 1) { if (other_face_idx != stl->stats.number_of_facets) {
stl->stats.connected_facets_2_edge -= 1; printf("in remove_facet: neighbor = %d numfacets = %d this is wrong\n", other_face_idx, stl->stats.number_of_facets);
stl->stats.connected_facets_1_edge -= 1; return;
} else if(j == 0) { }
stl->stats.connected_facets_3_edge -= 1; other_face_idx = facet_number;
stl->stats.connected_facets_2_edge -= 1; }
stl->stats.connected_facets_1_edge -= 1; }
}
stl->facet_start[facet_number] = static void remove_degenerate(stl_file *stl, int facet)
stl->facet_start[stl->stats.number_of_facets - 1]; {
/* I could reallocate at this point, but it is not really necessary. */ assert(! stl->error);
stl->neighbors_start[facet_number] =
stl->neighbors_start[stl->stats.number_of_facets - 1];
stl->stats.number_of_facets -= 1;
for(i = 0; i < 3; i++) { // Update statistics on face connectivity.
neighbor[i] = stl->neighbors_start[facet_number].neighbor[i]; auto stl_update_connects_remove_1 = [stl](int facet_num) {
vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i]; assert(! stl->error);
} //FIXME when decreasing 3_edge, should I increase 2_edge etc?
switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) {
case 0: // Facet has 3 neighbors
-- stl->stats.connected_facets_3_edge; break;
case 1: // Facet has 2 neighbors
-- stl->stats.connected_facets_2_edge; break;
case 2: // Facet has 1 neighbor
-- stl->stats.connected_facets_1_edge; break;
case 3: // Facet has 0 neighbors
break;
default:
assert(false);
}
};
for(i = 0; i < 3; i++) { int edge_to_collapse = 0;
if(neighbor[i] != -1) { if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) {
if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] != if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
stl->stats.number_of_facets) { // All 3 vertices are equal. Collapse the edge with no neighbor if it exists.
printf("\ const int *nbr = stl->neighbors_start[facet].neighbor;
in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n", edge_to_collapse = (nbr[0] == -1) ? 0 : (nbr[1] == -1) ? 1 : 2;
stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3], } else {
stl->stats.number_of_facets); edge_to_collapse = 0;
return; }
} } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] edge_to_collapse = 1;
= facet_number; } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) {
} edge_to_collapse = 2;
} } else {
// No degenerate. Function shouldn't have been called.
return;
}
int edge[3] = { (edge_to_collapse + 1) % 3, (edge_to_collapse + 2) % 3, edge_to_collapse };
int neighbor[] = {
stl->neighbors_start[facet].neighbor[edge[0]],
stl->neighbors_start[facet].neighbor[edge[1]],
stl->neighbors_start[facet].neighbor[edge[2]]
};
int vnot[] = {
stl->neighbors_start[facet].which_vertex_not[edge[0]],
stl->neighbors_start[facet].which_vertex_not[edge[1]],
stl->neighbors_start[facet].which_vertex_not[edge[2]]
};
// Update statistics on edge connectivity.
if (neighbor[0] == -1)
stl_update_connects_remove_1(neighbor[1]);
if (neighbor[1] == -1)
stl_update_connects_remove_1(neighbor[0]);
if (neighbor[0] >= 0) {
if (neighbor[1] >= 0) {
// Adjust the "flip" flag for the which_vertex_not values.
if (vnot[0] > 2) {
if (vnot[1] > 2) {
// The face to be removed has its normal flipped compared to the left & right neighbors, therefore after removing this face
// the two remaining neighbors will be oriented correctly.
vnot[0] -= 3;
vnot[1] -= 3;
} else
// One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally.
// After removal, the two neighbors will have their normals flipped.
vnot[1] += 3;
} else if (vnot[1] > 2)
// One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally.
// After removal, the two neighbors will have their normals flipped.
vnot[0] += 3;
}
stl->neighbors_start[neighbor[0]].neighbor[(vnot[0] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[1];
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = vnot[1];
}
if (neighbor[1] >= 0) {
stl->neighbors_start[neighbor[1]].neighbor[(vnot[1] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[0];
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
}
if (neighbor[2] >= 0) {
stl_update_connects_remove_1(neighbor[2]);
stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
}
remove_facet(stl, facet);
} }
void stl_remove_unconnected_facets(stl_file *stl) void stl_remove_unconnected_facets(stl_file *stl)
{ {
/* A couple of things need to be done here. One is to remove any */ // A couple of things need to be done here. One is to remove any completely unconnected facets (0 edges connected) since these are
/* completely unconnected facets (0 edges connected) since these are */ // useless and could be completely wrong. The second thing that needs to be done is to remove any degenerate facets that were created during
/* useless and could be completely wrong. The second thing that needs to */ // stl_check_facets_nearby().
/* be done is to remove any degenerate facets that were created during */ if (stl->error)
/* stl_check_facets_nearby(). */ return;
if (stl->error)
return;
// remove degenerate facets // remove degenerate facets
for (int i = 0; i < stl->stats.number_of_facets; ++ i) { for (uint32_t i = 0; i < stl->stats.number_of_facets;)
if(stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] || if (stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] ||
stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] || stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] ||
stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) { stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) {
stl_remove_degenerate(stl, i); remove_degenerate(stl, i);
i--; // assert(stl_validate(stl));
} } else
} ++ i;
if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) {
// remove completely unconnected facets // remove completely unconnected facets
for (int i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets;)
if (stl->neighbors_start[i].neighbor[0] == -1 && if (stl->neighbors_start[i].neighbor[0] == -1 &&
stl->neighbors_start[i].neighbor[1] == -1 && stl->neighbors_start[i].neighbor[1] == -1 &&
stl->neighbors_start[i].neighbor[2] == -1) { stl->neighbors_start[i].neighbor[2] == -1) {
// This facet is completely unconnected. Remove it. // This facet is completely unconnected. Remove it.
stl_remove_facet(stl, i); remove_facet(stl, i);
-- i; assert(stl_validate(stl));
} } else
} ++ i;
} }
}
static void
stl_remove_degenerate(stl_file *stl, int facet) {
int edge1;
int edge2;
int edge3;
int neighbor1;
int neighbor2;
int neighbor3;
int vnot1;
int vnot2;
int vnot3;
if (stl->error) return;
if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1] &&
stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
/* all 3 vertices are equal. Just remove the facet. I don't think*/
/* this is really possible, but just in case... */
printf("removing a facet in stl_remove_degenerate\n");
stl_remove_facet(stl, facet);
return;
}
if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) {
edge1 = 1;
edge2 = 2;
edge3 = 0;
} else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
edge1 = 0;
edge2 = 2;
edge3 = 1;
} else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) {
edge1 = 0;
edge2 = 1;
edge3 = 2;
} else {
/* No degenerate. Function shouldn't have been called. */
return;
}
neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
neighbor2 = stl->neighbors_start[facet].neighbor[edge2];
if(neighbor1 == -1) {
stl_update_connects_remove_1(stl, neighbor2);
}
if(neighbor2 == -1) {
stl_update_connects_remove_1(stl, neighbor1);
}
neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];
if(neighbor1 >= 0){
stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
}
if(neighbor2 >= 0){
stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
}
stl_remove_facet(stl, facet);
if(neighbor3 >= 0) {
stl_update_connects_remove_1(stl, neighbor3);
stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
}
}
void
stl_update_connects_remove_1(stl_file *stl, int facet_num) {
int j;
if (stl->error) return;
/* Update list of connected edges */
j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
(stl->neighbors_start[facet_num].neighbor[1] == -1) +
(stl->neighbors_start[facet_num].neighbor[2] == -1));
if(j == 0) { /* Facet has 3 neighbors */
stl->stats.connected_facets_3_edge -= 1;
} else if(j == 1) { /* Facet has 2 neighbors */
stl->stats.connected_facets_2_edge -= 1;
} else if(j == 2) { /* Facet has 1 neighbor */
stl->stats.connected_facets_1_edge -= 1;
}
} }
void void
@ -801,7 +774,6 @@ stl_fill_holes(stl_file *stl) {
int next_edge; int next_edge;
int pivot_vertex; int pivot_vertex;
int next_facet; int next_facet;
int i;
int j; int j;
int k; int k;
@ -809,7 +781,7 @@ stl_fill_holes(stl_file *stl) {
/* Insert all unconnected edges into hash list */ /* Insert all unconnected edges into hash list */
stl_initialize_facet_check_nearby(stl); stl_initialize_facet_check_nearby(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i]; facet = stl->facet_start[i];
for(j = 0; j < 3; j++) { for(j = 0; j < 3; j++) {
if(stl->neighbors_start[i].neighbor[j] != -1) continue; if(stl->neighbors_start[i].neighbor[j] != -1) continue;
@ -822,7 +794,7 @@ stl_fill_holes(stl_file *stl) {
} }
} }
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i]; facet = stl->facet_start[i];
neighbors_initial[0] = stl->neighbors_start[i].neighbor[0]; neighbors_initial[0] = stl->neighbors_start[i].neighbor[0];
neighbors_initial[1] = stl->neighbors_start[i].neighbor[1]; neighbors_initial[1] = stl->neighbors_start[i].neighbor[1];
@ -900,7 +872,7 @@ stl_add_facet(stl_file *stl, stl_facet *new_facet) {
if (stl->error) return; if (stl->error) return;
stl->stats.facets_added += 1; stl->stats.facets_added += 1;
if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) { if(stl->stats.facets_malloced < (int)stl->stats.number_of_facets + 1) {
stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->facet_start = (stl_facet*)realloc(stl->facet_start,
(sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); (sizeof(stl_facet) * (stl->stats.facets_malloced + 256)));
if(stl->facet_start == NULL) perror("stl_add_facet"); if(stl->facet_start == NULL) perror("stl_add_facet");

View file

@ -23,242 +23,239 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <vector>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#include "stl.h" #include "stl.h"
void void stl_invalidate_shared_vertices(stl_file *stl)
stl_invalidate_shared_vertices(stl_file *stl) { {
if (stl->error) return; if (stl->error)
return;
if (stl->v_indices != NULL) { if (stl->v_indices != nullptr) {
free(stl->v_indices); free(stl->v_indices);
stl->v_indices = NULL; stl->v_indices = nullptr;
} }
if (stl->v_shared != NULL) { if (stl->v_shared != nullptr) {
free(stl->v_shared); free(stl->v_shared);
stl->v_shared = NULL; stl->v_shared = nullptr;
} }
} }
void void stl_generate_shared_vertices(stl_file *stl)
stl_generate_shared_vertices(stl_file *stl) { {
int i; if (stl->error)
int j; return;
int first_facet;
int direction;
int facet_num;
int vnot;
int next_edge;
int pivot_vertex;
int next_facet;
int reversed;
if (stl->error) return; /* make sure this function is idempotent and does not leak memory */
stl_invalidate_shared_vertices(stl);
/* make sure this function is idempotent and does not leak memory */ // 3 indices to vertex per face
stl_invalidate_shared_vertices(stl); stl->v_indices = (v_indices_struct*)calloc(stl->stats.number_of_facets, sizeof(v_indices_struct));
if (stl->v_indices == nullptr)
perror("stl_generate_shared_vertices");
// Shared vertices (3D coordinates)
stl->v_shared = (stl_vertex*)calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex));
if (stl->v_shared == nullptr)
perror("stl_generate_shared_vertices");
stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
stl->stats.shared_vertices = 0;
stl->v_indices = (v_indices_struct*) for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); // vertex index -1 means no shared vertex was assigned yet.
if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); stl->v_indices[i].vertex[0] = -1;
stl->v_shared = (stl_vertex*) stl->v_indices[i].vertex[1] = -1;
calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); stl->v_indices[i].vertex[2] = -1;
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); }
stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
stl->stats.shared_vertices = 0;
for(i = 0; i < stl->stats.number_of_facets; i++) { // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop
stl->v_indices[i].vertex[0] = -1; // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal
stl->v_indices[i].vertex[1] = -1; // are marked with a unique fan_traversal_stamp.
stl->v_indices[i].vertex[2] = -1; unsigned int fan_traversal_stamp = 0;
} std::vector<unsigned int> fan_traversal_facet_visited(stl->stats.number_of_facets, 0);
for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) {
for (int j = 0; j < 3; ++ j) {
if (stl->v_indices[facet_idx].vertex[j] != -1)
// Shared vertex was already assigned.
continue;
// Create a new shared vertex.
if (stl->stats.shared_vertices == stl->stats.shared_malloced) {
stl->stats.shared_malloced += 1024;
stl->v_shared = (stl_vertex*)realloc(stl->v_shared, stl->stats.shared_malloced * sizeof(stl_vertex));
if(stl->v_shared == nullptr)
perror("stl_generate_shared_vertices");
}
stl->v_shared[stl->stats.shared_vertices] = stl->facet_start[facet_idx].vertex[j];
// Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan.
int facet_in_fan_idx = facet_idx;
bool edge_direction = false;
bool traversal_reversed = false;
int vnot = (j + 2) % 3;
// Increase the
++ fan_traversal_stamp;
for (;;) {
// Next edge on facet_in_fan_idx to be traversed. The edge is indexed by its starting vertex index.
int next_edge = 0;
// Vertex index in facet_in_fan_idx, which is being pivoted around, and which is being assigned a new shared vertex.
int pivot_vertex = 0;
if (vnot > 2) {
// The edge of facet_in_fan_idx opposite to vnot is equally oriented, therefore
// the neighboring facet is flipped.
if (! edge_direction) {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
} else {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3;
}
edge_direction = ! edge_direction;
} else {
// The neighboring facet is correctly oriented.
if (! edge_direction) {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot;
} else {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
}
}
stl->v_indices[facet_in_fan_idx].vertex[pivot_vertex] = stl->stats.shared_vertices;
fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp;
for(i = 0; i < stl->stats.number_of_facets; i++) { // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
first_facet = i; int next_facet = stl->neighbors_start[facet_in_fan_idx].neighbor[next_edge];
for(j = 0; j < 3; j++) { if (next_facet == -1) {
if(stl->v_indices[i].vertex[j] != -1) { // No neighbor going in the current direction.
continue; if (traversal_reversed) {
} // Went to one limit, then turned back and reached the other limit. Quit the fan traversal.
if(stl->stats.shared_vertices == stl->stats.shared_malloced) { break;
stl->stats.shared_malloced += 1024; } else {
stl->v_shared = (stl_vertex*)realloc(stl->v_shared, // Reached the first limit. Now try to reverse and traverse up to the other limit.
stl->stats.shared_malloced * sizeof(stl_vertex)); edge_direction = true;
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); vnot = (j + 1) % 3;
} traversal_reversed = true;
facet_in_fan_idx = facet_idx;
}
} else if (next_facet == facet_idx) {
// Traversed a closed fan all around.
// assert(! traversal_reversed);
break;
} else if (next_facet >= (int)stl->stats.number_of_facets) {
// The mesh is not valid!
// assert(false);
break;
} else if (fan_traversal_facet_visited[next_facet] == fan_traversal_stamp) {
// Traversed a closed fan all around, but did not reach the starting face.
// This indicates an invalid geometry (non-manifold).
//assert(false);
break;
} else {
// Continue traversal.
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
vnot = stl->neighbors_start[facet_in_fan_idx].which_vertex_not[next_edge];
facet_in_fan_idx = next_facet;
}
}
stl->v_shared[stl->stats.shared_vertices] = ++ stl->stats.shared_vertices;
stl->facet_start[i].vertex[j]; }
}
direction = 0;
reversed = 0;
facet_num = i;
vnot = (j + 2) % 3;
for(;;) {
if(vnot > 2) {
if(direction == 0) {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
direction = 1;
} else {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3;
direction = 0;
}
} else {
if(direction == 0) {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot;
} else {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
}
}
stl->v_indices[facet_num].vertex[pivot_vertex] =
stl->stats.shared_vertices;
next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
if(next_facet == -1) {
if(reversed) {
break;
} else {
direction = 1;
vnot = (j + 1) % 3;
reversed = 1;
facet_num = first_facet;
}
} else if(next_facet != first_facet) {
vnot = stl->neighbors_start[facet_num].
which_vertex_not[next_edge];
facet_num = next_facet;
} else {
break;
}
}
stl->stats.shared_vertices += 1;
}
}
} }
void void stl_write_off(stl_file *stl, const char *file)
stl_write_off(stl_file *stl, const char *file) { {
int i; if (stl->error)
FILE *fp; return;
char *error_msg;
if (stl->error) return; /* Open the file */
FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Open the file */ fprintf(fp, "OFF\n");
fp = boost::nowide::fopen(file, "w"); fprintf(fp, "%d %d 0\n", stl->stats.shared_vertices, stl->stats.number_of_facets);
if(fp == NULL) { for (int i = 0; i < stl->stats.shared_vertices; ++ i)
error_msg = (char*) fprintf(fp, "\t%f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
file); fclose(fp);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "OFF\n");
fprintf(fp, "%d %d 0\n",
stl->stats.shared_vertices, stl->stats.number_of_facets);
for(i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "\t%f %f %f\n",
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
}
fclose(fp);
} }
void void stl_write_vrml(stl_file *stl, const char *file)
stl_write_vrml(stl_file *stl, const char *file) { {
int i; if (stl->error)
FILE *fp; return;
char *error_msg;
if (stl->error) return; /* Open the file */
FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Open the file */ fprintf(fp, "#VRML V1.0 ascii\n\n");
fp = boost::nowide::fopen(file, "w"); fprintf(fp, "Separator {\n");
if(fp == NULL) { fprintf(fp, "\tDEF STLShape ShapeHints {\n");
error_msg = (char*) fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ fprintf(fp, "\t\tfaceType CONVEX\n");
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", fprintf(fp, "\t\tshapeType SOLID\n");
file); fprintf(fp, "\t\tcreaseAngle 0.0\n");
perror(error_msg); fprintf(fp, "\t}\n");
free(error_msg); fprintf(fp, "\tDEF STLModel Separator {\n");
stl->error = 1; fprintf(fp, "\t\tDEF STLColor Material {\n");
return; fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
} fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
fprintf(fp, "\t\t\tpoint [\n");
fprintf(fp, "#VRML V1.0 ascii\n\n"); int i = 0;
fprintf(fp, "Separator {\n"); for (; i < (stl->stats.shared_vertices - 1); i++)
fprintf(fp, "\tDEF STLShape ShapeHints {\n"); fprintf(fp, "\t\t\t\t%f %f %f,\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n"); fprintf(fp, "\t\t\t\t%f %f %f]\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
fprintf(fp, "\t\tfaceType CONVEX\n"); fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tshapeType SOLID\n"); fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\tcreaseAngle 0.0\n"); fprintf(fp, "\t\t\tcoordIndex [\n");
fprintf(fp, "\t}\n");
fprintf(fp, "\tDEF STLModel Separator {\n");
fprintf(fp, "\t\tDEF STLColor Material {\n");
fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
fprintf(fp, "\t\t\tpoint [\n");
for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { for (int i = 0; i + 1 < (int)stl->stats.number_of_facets; ++ i)
fprintf(fp, "\t\t\t\t%f %f %f,\n", fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
} fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\t\t\t%f %f %f]\n", fprintf(fp, "\t}\n");
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); fprintf(fp, "}\n");
fprintf(fp, "\t\t}\n"); fclose(fp);
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\t\tcoordIndex [\n");
for(i = 0; i < (stl->stats.number_of_facets - 1); i++) {
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
}
fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t}\n");
fprintf(fp, "}\n");
fclose(fp);
} }
void stl_write_obj (stl_file *stl, const char *file) { void stl_write_obj (stl_file *stl, const char *file)
int i; {
FILE* fp; if (stl->error)
return;
if (stl->error) return; FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Open the file */ for (int i = 0; i < stl->stats.shared_vertices; ++ i)
fp = boost::nowide::fopen(file, "w"); fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
if (fp == NULL) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); fclose(fp);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
for (i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
}
for (i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
}
fclose(fp);
} }

View file

@ -277,5 +277,7 @@ extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
extern void stl_clear_error(stl_file *stl); extern void stl_clear_error(stl_file *stl);
extern int stl_get_error(stl_file *stl); extern int stl_get_error(stl_file *stl);
extern void stl_exit_on_error(stl_file *stl); extern void stl_exit_on_error(stl_file *stl);
// Validate the mesh, assert on error.
extern bool stl_validate(stl_file *stl);
#endif #endif

View file

@ -457,3 +457,50 @@ All facets connected. No further nearby check necessary.\n");
stl_verify_neighbors(stl); stl_verify_neighbors(stl);
} }
} }
// Check validity of the mesh, assert on error.
bool stl_validate(stl_file *stl)
{
assert(! stl->error);
assert(stl->fp == nullptr);
assert(stl->facet_start != nullptr);
assert(stl->heads == nullptr);
assert(stl->tail == nullptr);
assert(stl->neighbors_start != nullptr);
assert((stl->v_indices == nullptr) == (stl->v_shared == nullptr));
assert(stl->stats.number_of_facets > 0);
#ifdef _DEBUG
// Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) {
const stl_neighbors &nbr = stl->neighbors_start[facet_idx];
const int *vertices = (stl->v_indices == nullptr) ? nullptr : stl->v_indices[facet_idx].vertex;
for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) {
int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx];
assert(nbr_face < (int)stl->stats.number_of_facets);
if (nbr_face != -1) {
int nbr_vnot = nbr.which_vertex_not[nbr_idx];
assert(nbr_vnot >= 0 && nbr_vnot < 6);
// Neighbor of the neighbor is the original face.
assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx);
int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3];
assert(vnot_back >= 0 && vnot_back < 6);
assert((nbr_vnot < 3) == (vnot_back < 3));
assert(vnot_back % 3 == (nbr_idx + 2) % 3);
if (vertices != nullptr) {
// Has shared vertices.
if (nbr_vnot < 3) {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented.
assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[nbr_idx]));
} else {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[nbr_idx]));
}
}
}
}
}
#endif /* _DEBUG */
return true;
}

View file

@ -104,18 +104,21 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
void TriangleMesh::repair() void TriangleMesh::repair()
{ {
if (this->repaired) return; if (this->repaired)
return;
// admesh fails when repairing empty meshes // admesh fails when repairing empty meshes
if (this->stl.stats.number_of_facets == 0) return; if (this->stl.stats.number_of_facets == 0)
return;
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started"; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
// checking exact // checking exact
#ifdef SLIC3R_TRACE_REPAIR #ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact"; BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_check_facets_exact(&stl); stl_check_facets_exact(&stl);
assert(stl_validate(&this->stl));
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge); stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge); stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
@ -141,6 +144,7 @@ void TriangleMesh::repair()
} }
} }
} }
assert(stl_validate(&this->stl));
// remove_unconnected // remove_unconnected
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
@ -148,6 +152,7 @@ void TriangleMesh::repair()
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets"; BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_remove_unconnected_facets(&stl); stl_remove_unconnected_facets(&stl);
assert(stl_validate(&this->stl));
} }
// fill_holes // fill_holes
@ -168,24 +173,28 @@ void TriangleMesh::repair()
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions"; BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_directions(&stl); stl_fix_normal_directions(&stl);
assert(stl_validate(&this->stl));
// normal_values // normal_values
#ifdef SLIC3R_TRACE_REPAIR #ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values"; BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_values(&stl); stl_fix_normal_values(&stl);
assert(stl_validate(&this->stl));
// always calculate the volume and reverse all normals if volume is negative // always calculate the volume and reverse all normals if volume is negative
#ifdef SLIC3R_TRACE_REPAIR #ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume"; BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_calculate_volume(&stl); stl_calculate_volume(&stl);
assert(stl_validate(&this->stl));
// neighbors // neighbors
#ifdef SLIC3R_TRACE_REPAIR #ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors"; BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
stl_verify_neighbors(&stl); stl_verify_neighbors(&stl);
assert(stl_validate(&this->stl));
this->repaired = true; this->repaired = true;
@ -594,27 +603,14 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
void TriangleMesh::require_shared_vertices() void TriangleMesh::require_shared_vertices()
{ {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
if (!this->repaired) assert(stl_validate(&this->stl));
if (! this->repaired)
this->repair(); this->repair();
if (this->stl.v_shared == NULL) { if (this->stl.v_shared == nullptr) {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
stl_generate_shared_vertices(&(this->stl)); stl_generate_shared_vertices(&(this->stl));
} }
#ifdef _DEBUG assert(stl_validate(&this->stl));
// Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
const int *vertices = stl.v_indices[facet_idx].vertex;
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
if (nbr_face != -1) {
assert(
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]) ||
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[nbr_idx]));
}
}
}
#endif /* _DEBUG */
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
} }