Merge branch 'vb_admesh_fix'

This commit is contained in:
bubnikv 2019-06-18 08:54:50 +02:00
commit c95a324c3f
36 changed files with 2313 additions and 2894 deletions

View file

@ -60,7 +60,7 @@ if (MSVC)
# /bigobj (Increase Number of Sections in .Obj file) # /bigobj (Increase Number of Sections in .Obj file)
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
# Generate symbols at every build target, even for the release. # Generate symbols at every build target, even for the release.
add_compile_options(-bigobj -Zm316 /Zi) add_compile_options(-bigobj -Zm520 /Zi)
endif () endif ()
# Display and check CMAKE_PREFIX_PATH # Display and check CMAKE_PREFIX_PATH

View file

@ -7,10 +7,13 @@
#include <Windows.h> #include <Windows.h>
#include <wchar.h> #include <wchar.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
extern "C"
{
// Let the NVIDIA and AMD know we want to use their graphics card // Let the NVIDIA and AMD know we want to use their graphics card
// on a dual graphics card system. // on a dual graphics card system.
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
#endif /* WIN32 */ #endif /* WIN32 */
@ -241,8 +244,7 @@ int CLI::run(int argc, char **argv)
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") { } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
std::vector<Model> new_models; std::vector<Model> new_models;
for (auto &model : m_models) { for (auto &model : m_models) {
model.repair(); model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
size_t num_objects = model.objects.size(); size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) { for (size_t i = 0; i < num_objects; ++ i) {
@ -301,8 +303,9 @@ int CLI::run(int argc, char **argv)
} }
} }
} else if (opt_key == "repair") { } else if (opt_key == "repair") {
for (auto &model : m_models) // Models are repaired by default.
model.repair(); //for (auto &model : m_models)
// model.repair();
} else { } else {
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl; boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1; return 1;

View file

@ -8,10 +8,13 @@
#include <wchar.h> #include <wchar.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
extern "C"
{
// Let the NVIDIA and AMD know we want to use their graphics card // Let the NVIDIA and AMD know we want to use their graphics card
// on a dual graphics card system. // on a dual graphics card system.
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
#include <stdlib.h> #include <stdlib.h>

File diff suppressed because it is too large Load diff

View file

@ -25,271 +25,214 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
// Boost pool: Don't use mutexes to synchronize memory allocation.
#define BOOST_POOL_NO_MT
#include <boost/pool/object_pool.hpp>
#include "stl.h" #include "stl.h"
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); static void reverse_facet(stl_file *stl, int facet_num)
{
++ stl->stats.facets_reversed;
static void int neighbor[3] = { stl->neighbors_start[facet_num].neighbor[0], stl->neighbors_start[facet_num].neighbor[1], stl->neighbors_start[facet_num].neighbor[2] };
stl_reverse_facet(stl_file *stl, int facet_num) { int vnot[3] = { stl->neighbors_start[facet_num].which_vertex_not[0], stl->neighbors_start[facet_num].which_vertex_not[1], stl->neighbors_start[facet_num].which_vertex_not[2] };
stl_vertex tmp_vertex;
/* int tmp_neighbor;*/
int neighbor[3];
int vnot[3];
stl->stats.facets_reversed += 1; // reverse the facet
stl_vertex tmp_vertex = stl->facet_start[facet_num].vertex[0];
stl->facet_start[facet_num].vertex[0] = stl->facet_start[facet_num].vertex[1];
stl->facet_start[facet_num].vertex[1] = tmp_vertex;
neighbor[0] = stl->neighbors_start[facet_num].neighbor[0]; // fix the vnots of the neighboring facets
neighbor[1] = stl->neighbors_start[facet_num].neighbor[1]; if (neighbor[0] != -1)
neighbor[2] = stl->neighbors_start[facet_num].neighbor[2]; stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = (stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0]; if (neighbor[1] != -1)
vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1]; stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = (stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2]; if (neighbor[2] != -1)
stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = (stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
/* reverse the facet */ // swap the neighbors of the facet that is being reversed
tmp_vertex = stl->facet_start[facet_num].vertex[0]; stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
stl->facet_start[facet_num].vertex[0] = stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
stl->facet_start[facet_num].vertex[1];
stl->facet_start[facet_num].vertex[1] = tmp_vertex;
/* fix the vnots of the neighboring facets */ // swap the vnots of the facet that is being reversed
if(neighbor[0] != -1) stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
(stl->neighbors_start[neighbor[0]].
which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
if(neighbor[1] != -1)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] =
(stl->neighbors_start[neighbor[1]].
which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
if(neighbor[2] != -1)
stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] =
(stl->neighbors_start[neighbor[2]].
which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
/* swap the neighbors of the facet that is being reversed */ // reverse the values of the vnots of the facet that is being reversed
stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; stl->neighbors_start[facet_num].which_vertex_not[0] = (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; stl->neighbors_start[facet_num].which_vertex_not[1] = (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
stl->neighbors_start[facet_num].which_vertex_not[2] = (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
/* swap the vnots of the facet that is being reversed */
stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
/* reverse the values of the vnots of the facet that is being reversed */
stl->neighbors_start[facet_num].which_vertex_not[0] =
(stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
stl->neighbors_start[facet_num].which_vertex_not[1] =
(stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
stl->neighbors_start[facet_num].which_vertex_not[2] =
(stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
} }
void // Returns true if the normal was flipped.
stl_fix_normal_directions(stl_file *stl) { static bool check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag)
char *norm_sw; {
/* int edge_num;*/ stl_facet *facet = &stl->facet_start[facet_num];
/* int vnot;*/
int checked = 0;
int facet_num;
/* int next_facet;*/
int i;
int j;
struct stl_normal {
int facet_num;
struct stl_normal *next;
};
struct stl_normal *head;
struct stl_normal *tail;
struct stl_normal *newn;
struct stl_normal *temp;
int* reversed_ids; stl_normal normal;
int reversed_count = 0; stl_calculate_normal(normal, facet);
int id; stl_normalize_vector(normal);
int force_exit = 0; stl_normal normal_dif = (normal - facet->normal).cwiseAbs();
if (stl->error) return; const float eps = 0.001f;
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
// Normal is within tolerance. It is not really necessary to change the values here, but just for consistency, I will.
facet->normal = normal;
return false;
}
// this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209 stl_normal test_norm = facet->normal;
if (stl->stats.number_of_facets == 0) return; stl_normalize_vector(test_norm);
normal_dif = (normal - test_norm).cwiseAbs();
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
// The normal is not within tolerance, but direction is OK.
if (normal_fix_flag) {
facet->normal = normal;
++ stl->stats.normals_fixed;
}
return false;
}
/* Initialize linked list. */ test_norm *= -1.f;
head = (struct stl_normal*)malloc(sizeof(struct stl_normal)); normal_dif = (normal - test_norm).cwiseAbs();
if(head == NULL) perror("stl_fix_normal_directions"); if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
tail = (struct stl_normal*)malloc(sizeof(struct stl_normal)); // The normal is not within tolerance and backwards.
if(tail == NULL) perror("stl_fix_normal_directions"); if (normal_fix_flag) {
head->next = tail; facet->normal = normal;
tail->next = tail; ++ stl->stats.normals_fixed;
}
/* Initialize list that keeps track of already fixed facets. */ return true;
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char)); }
if(norm_sw == NULL) perror("stl_fix_normal_directions"); if (normal_fix_flag) {
facet->normal = normal;
/* Initialize list that keeps track of reversed facets. */ ++ stl->stats.normals_fixed;
reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int)); }
if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids"); // Status is unknown.
return false;
facet_num = 0;
/* If normal vector is not within tolerance and backwards:
Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
of it being wrong randomly are low if most of the triangles are right: */
if (stl_check_normal_vector(stl, 0, 0) == 2) {
stl_reverse_facet(stl, 0);
reversed_ids[reversed_count++] = 0;
}
/* Say that we've fixed this facet: */
norm_sw[facet_num] = 1;
checked++;
for(;;) {
/* Add neighbors_to_list.
Add unconnected neighbors to the list:a */
for(j = 0; j < 3; j++) {
/* Reverse the neighboring facets if necessary. */
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
/* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */
for (id = reversed_count - 1; id >= 0; --id) {
stl_reverse_facet(stl, reversed_ids[id]);
}
force_exit = 1;
break;
} else {
stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j];
}
}
}
/* If this edge of the facet is connected: */
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
/* If we haven't fixed this facet yet, add it to the list: */
if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
/* Add node to beginning of list. */
newn = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(newn == NULL) perror("stl_fix_normal_directions");
newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
newn->next = head->next;
head->next = newn;
}
}
}
/* an error occourred, quit the for loop and exit */
if (force_exit) break;
/* Get next facet to fix from top of list. */
if(head->next != tail) {
facet_num = head->next->facet_num;
if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */
norm_sw[facet_num] = 1; /* Record this one as being fixed. */
checked++;
}
temp = head->next; /* Delete this facet from the list. */
head->next = head->next->next;
free(temp);
} else { /* if we ran out of facets to fix: */
/* All of the facets in this part have been fixed. */
stl->stats.number_of_parts += 1;
if(checked >= stl->stats.number_of_facets) {
/* All of the facets have been checked. Bail out. */
break;
} else {
/* There is another part here. Find it and continue. */
for(i = 0; i < stl->stats.number_of_facets; i++) {
if(norm_sw[i] == 0) {
/* This is the first facet of the next part. */
facet_num = i;
if(stl_check_normal_vector(stl, i, 0) == 2) {
stl_reverse_facet(stl, i);
reversed_ids[reversed_count++] = i;
}
norm_sw[facet_num] = 1;
checked++;
break;
}
}
}
}
}
free(head);
free(tail);
free(reversed_ids);
free(norm_sw);
} }
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { void stl_fix_normal_directions(stl_file *stl)
/* Returns 0 if the normal is within tolerance */ {
/* Returns 1 if the normal is not within tolerance, but direction is OK */ // This may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
/* Returns 2 if the normal is not within tolerance and backwards */ if (stl->stats.number_of_facets == 0)
/* Returns 4 if the status is unknown. */ return;
stl_facet *facet; struct stl_normal {
int facet_num;
stl_normal *next;
};
facet = &stl->facet_start[facet_num]; // Initialize linked list.
boost::object_pool<stl_normal> pool;
stl_normal *head = pool.construct();
stl_normal *tail = pool.construct();
head->next = tail;
tail->next = tail;
stl_normal normal; // Initialize list that keeps track of already fixed facets.
stl_calculate_normal(normal, facet); std::vector<char> norm_sw(stl->stats.number_of_facets, 0);
stl_normalize_vector(normal); // Initialize list that keeps track of reversed facets.
stl_normal normal_dif = (normal - facet->normal).cwiseAbs(); std::vector<int> reversed_ids(stl->stats.number_of_facets, 0);
const float eps = 0.001f; int facet_num = 0;
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { int reversed_count = 0;
/* It is not really necessary to change the values here */ // If normal vector is not within tolerance and backwards:
/* but just for consistency, I will. */ // Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
facet->normal = normal; // of it being wrong randomly are low if most of the triangles are right:
return 0; if (check_normal_vector(stl, 0, 0)) {
} reverse_facet(stl, 0);
reversed_ids[reversed_count ++] = 0;
}
stl_normal test_norm = facet->normal; // Say that we've fixed this facet:
stl_normalize_vector(test_norm); norm_sw[facet_num] = 1;
normal_dif = (normal - test_norm).cwiseAbs(); int checked = 1;
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
if(normal_fix_flag) {
facet->normal = normal;
stl->stats.normals_fixed += 1;
}
return 1;
}
test_norm *= -1.f; for (;;) {
normal_dif = (normal - test_norm).cwiseAbs(); // Add neighbors_to_list. Add unconnected neighbors to the list.
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { bool force_exit = false;
// Facet is backwards. for (int j = 0; j < 3; ++ j) {
if(normal_fix_flag) { // Reverse the neighboring facets if necessary.
facet->normal = normal; if (stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
stl->stats.normals_fixed += 1; // If the facet has a neighbor that is -1, it means that edge isn't shared by another facet
} if (stl->neighbors_start[facet_num].neighbor[j] != -1) {
return 2; if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
} // trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206)
if(normal_fix_flag) { for (int id = reversed_count - 1; id >= 0; -- id)
facet->normal = normal; reverse_facet(stl, reversed_ids[id]);
stl->stats.normals_fixed += 1; force_exit = true;
} break;
return 4; }
reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
reversed_ids[reversed_count ++] = stl->neighbors_start[facet_num].neighbor[j];
}
}
// If this edge of the facet is connected:
if (stl->neighbors_start[facet_num].neighbor[j] != -1) {
// If we haven't fixed this facet yet, add it to the list:
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
// Add node to beginning of list.
stl_normal *newn = pool.construct();
newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
newn->next = head->next;
head->next = newn;
}
}
}
// an error occourred, quit the for loop and exit
if (force_exit)
break;
// Get next facet to fix from top of list.
if (head->next != tail) {
facet_num = head->next->facet_num;
if (norm_sw[facet_num] != 1) { // If facet is in list mutiple times
norm_sw[facet_num] = 1; // Record this one as being fixed.
++ checked;
}
stl_normal *temp = head->next; // Delete this facet from the list.
head->next = head->next->next;
// pool.destroy(temp);
} else { // If we ran out of facets to fix: All of the facets in this part have been fixed.
++ stl->stats.number_of_parts;
if (checked >= stl->stats.number_of_facets)
// All of the facets have been checked. Bail out.
break;
// There is another part here. Find it and continue.
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
if (norm_sw[i] == 0) {
// This is the first facet of the next part.
facet_num = i;
if (check_normal_vector(stl, i, 0)) {
reverse_facet(stl, i);
reversed_ids[reversed_count++] = i;
}
norm_sw[facet_num] = 1;
++ checked;
break;
}
}
}
// pool.destroy(head);
// pool.destroy(tail);
} }
void stl_fix_normal_values(stl_file *stl) { void stl_fix_normal_values(stl_file *stl)
int i; {
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
if (stl->error) return; check_normal_vector(stl, i, 1);
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl_check_normal_vector(stl, i, 1);
}
} }
void stl_reverse_all_facets(stl_file *stl) void stl_reverse_all_facets(stl_file *stl)
{ {
if (stl->error) stl_normal normal;
return; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
reverse_facet(stl, i);
stl_normal normal; stl_calculate_normal(normal, &stl->facet_start[i]);
for(int i = 0; i < stl->stats.number_of_facets; i++) { stl_normalize_vector(normal);
stl_reverse_facet(stl, i); stl->facet_start[i].normal = normal;
stl_calculate_normal(normal, &stl->facet_start[i]); }
stl_normalize_vector(normal);
stl->facet_start[i].normal = normal;
}
} }

View file

@ -23,242 +23,237 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <vector>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#include "stl.h" #include "stl.h"
void void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its)
stl_invalidate_shared_vertices(stl_file *stl) { {
if (stl->error) return; // 3 indices to vertex per face
its.indices.assign(stl->stats.number_of_facets, stl_triangle_vertex_indices(-1, -1, -1));
// Shared vertices (3D coordinates)
its.vertices.clear();
its.vertices.reserve(stl->stats.number_of_facets / 2);
if (stl->v_indices != NULL) { // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop
free(stl->v_indices); // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal
stl->v_indices = NULL; // are marked with a unique fan_traversal_stamp.
} unsigned int fan_traversal_stamp = 0;
if (stl->v_shared != NULL) { std::vector<unsigned int> fan_traversal_facet_visited(stl->stats.number_of_facets, 0);
free(stl->v_shared);
stl->v_shared = NULL; for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) {
} for (int j = 0; j < 3; ++ j) {
if (its.indices[facet_idx][j] != -1)
// Shared vertex was already assigned.
continue;
// Create a new shared vertex.
its.vertices.emplace_back(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;
}
}
its.indices[facet_in_fan_idx][pivot_vertex] = its.vertices.size() - 1;
fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp;
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
int next_facet = stl->neighbors_start[facet_in_fan_idx].neighbor[next_edge];
if (next_facet == -1) {
// No neighbor going in the current direction.
if (traversal_reversed) {
// Went to one limit, then turned back and reached the other limit. Quit the fan traversal.
break;
} else {
// Reached the first limit. Now try to reverse and traverse up to the other limit.
edge_direction = true;
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;
}
}
}
}
} }
void bool its_write_off(const indexed_triangle_set &its, const char *file)
stl_generate_shared_vertices(stl_file *stl) { {
int i; /* Open the file */
int j; FILE *fp = boost::nowide::fopen(file, "w");
int first_facet; if (fp == nullptr) {
int direction; BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
int facet_num; return false;
int vnot; }
int next_edge;
int pivot_vertex;
int next_facet;
int reversed;
if (stl->error) return; fprintf(fp, "OFF\n");
fprintf(fp, "%d %d 0\n", (int)its.vertices.size(), (int)its.indices.size());
for (int i = 0; i < its.vertices.size(); ++ i)
fprintf(fp, "\t%f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
for (uint32_t i = 0; i < its.indices.size(); ++ i)
fprintf(fp, "\t3 %d %d %d\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
fclose(fp);
return true;
}
/* make sure this function is idempotent and does not leak memory */ bool its_write_vrml(const indexed_triangle_set &its, const char *file)
stl_invalidate_shared_vertices(stl); {
/* Open the file */
FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_write_vrml: Couldn't open " << file << " for writing";
return false;
}
stl->v_indices = (v_indices_struct*) fprintf(fp, "#VRML V1.0 ascii\n\n");
calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); fprintf(fp, "Separator {\n");
if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); fprintf(fp, "\tDEF STLShape ShapeHints {\n");
stl->v_shared = (stl_vertex*) fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); fprintf(fp, "\t\tfaceType CONVEX\n");
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); fprintf(fp, "\t\tshapeType SOLID\n");
stl->stats.shared_malloced = stl->stats.number_of_facets / 2; fprintf(fp, "\t\tcreaseAngle 0.0\n");
stl->stats.shared_vertices = 0; 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.number_of_facets; i++) { int i = 0;
stl->v_indices[i].vertex[0] = -1; for (; i + 1 < its.vertices.size(); ++ i)
stl->v_indices[i].vertex[1] = -1; fprintf(fp, "\t\t\t\t%f %f %f,\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
stl->v_indices[i].vertex[2] = -1; fprintf(fp, "\t\t\t\t%f %f %f]\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
} fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\t\tcoordIndex [\n");
for (size_t i = 0; i + 1 < its.indices.size(); ++ i)
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t}\n");
fprintf(fp, "}\n");
fclose(fp);
return true;
}
bool its_write_obj(const indexed_triangle_set &its, const char *file)
{
FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
return false;
}
for (size_t i = 0; i < its.vertices.size(); ++ i)
fprintf(fp, "v %f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
for (size_t i = 0; i < its.indices.size(); ++ i)
fprintf(fp, "f %d %d %d\n", its.indices[i][0]+1, its.indices[i][1]+1, its.indices[i][2]+1);
fclose(fp);
return true;
}
for(i = 0; i < stl->stats.number_of_facets; i++) { // Check validity of the mesh, assert on error.
first_facet = i; bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
for(j = 0; j < 3; j++) { {
if(stl->v_indices[i].vertex[j] != -1) { assert(! stl->facet_start.empty());
continue; assert(stl->facet_start.size() == stl->stats.number_of_facets);
} assert(stl->neighbors_start.size() == stl->stats.number_of_facets);
if(stl->stats.shared_vertices == stl->stats.shared_malloced) { assert(stl->facet_start.size() == stl->neighbors_start.size());
stl->stats.shared_malloced += 1024; assert(! stl->neighbors_start.empty());
stl->v_shared = (stl_vertex*)realloc(stl->v_shared, assert((its.indices.empty()) == (its.vertices.empty()));
stl->stats.shared_malloced * sizeof(stl_vertex)); assert(stl->stats.number_of_facets > 0);
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); assert(its.vertices.empty() || its.indices.size() == stl->stats.number_of_facets);
}
stl->v_shared[stl->stats.shared_vertices] = #ifdef _DEBUG
stl->facet_start[i].vertex[j]; // Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) {
direction = 0; const stl_neighbors &nbr = stl->neighbors_start[facet_idx];
reversed = 0; const int *vertices = its.indices.empty() ? nullptr : its.indices[facet_idx].data();
facet_num = i; for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) {
vnot = (j + 2) % 3; int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx];
assert(nbr_face < (int)stl->stats.number_of_facets);
for(;;) { if (nbr_face != -1) {
if(vnot > 2) { int nbr_vnot = nbr.which_vertex_not[nbr_idx];
if(direction == 0) { assert(nbr_vnot >= 0 && nbr_vnot < 6);
pivot_vertex = (vnot + 2) % 3; // Neighbor of the neighbor is the original face.
next_edge = pivot_vertex; assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx);
direction = 1; int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3];
} else { assert(vnot_back >= 0 && vnot_back < 6);
pivot_vertex = (vnot + 1) % 3; assert((nbr_vnot < 3) == (vnot_back < 3));
next_edge = vnot % 3; assert(vnot_back % 3 == (nbr_idx + 2) % 3);
direction = 0; if (vertices != nullptr) {
} // Has shared vertices.
} else { if (nbr_vnot < 3) {
if(direction == 0) { // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented.
pivot_vertex = (vnot + 1) % 3; assert((its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[nbr_idx]));
next_edge = vnot; } else {
} else { // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
pivot_vertex = (vnot + 2) % 3; assert((its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[nbr_idx]));
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;
} }
} #endif /* _DEBUG */
return true;
} }
void // Check validity of the mesh, assert on error.
stl_write_off(stl_file *stl, const char *file) { bool stl_validate(const stl_file *stl)
int i; {
FILE *fp; indexed_triangle_set its;
char *error_msg; return stl_validate(stl, its);
if (stl->error) return;
/* Open the file */
fp = boost::nowide::fopen(file, "w");
if(fp == NULL) {
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;
}
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
stl_write_vrml(stl_file *stl, const char *file) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = boost::nowide::fopen(file, "w");
if(fp == NULL) {
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;
}
fprintf(fp, "#VRML V1.0 ascii\n\n");
fprintf(fp, "Separator {\n");
fprintf(fp, "\tDEF STLShape ShapeHints {\n");
fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
fprintf(fp, "\t\tfaceType CONVEX\n");
fprintf(fp, "\t\tshapeType SOLID\n");
fprintf(fp, "\t\tcreaseAngle 0.0\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++) {
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\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\t}\n");
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) {
int i;
FILE* fp;
if (stl->error) return;
/* Open the file */
fp = boost::nowide::fopen(file, "w");
if (fp == NULL) {
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;
}
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

@ -27,6 +27,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <vector>
#include <Eigen/Geometry> #include <Eigen/Geometry>
// Size of the binary STL header, free form. // Size of the binary STL header, free form.
@ -40,22 +41,23 @@
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_vertex; typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_vertex;
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal; typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal;
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> stl_triangle_vertex_indices;
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
struct stl_facet { struct stl_facet {
stl_normal normal; stl_normal normal;
stl_vertex vertex[3]; stl_vertex vertex[3];
char extra[2]; char extra[2];
stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) { stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) const {
stl_facet out; stl_facet out;
out.normal = rot * this->normal; out.normal = rot * this->normal;
out.vertex[0] = rot * this->vertex[0]; out.vertex[0] = rot * this->vertex[0];
out.vertex[1] = rot * this->vertex[1]; out.vertex[1] = rot * this->vertex[1];
out.vertex[2] = rot * this->vertex[2]; out.vertex[2] = rot * this->vertex[2];
return out; return out;
} }
}; };
#define SIZEOF_STL_FACET 50 #define SIZEOF_STL_FACET 50
@ -67,104 +69,94 @@ static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrec
typedef enum {binary, ascii, inmemory} stl_type; typedef enum {binary, ascii, inmemory} stl_type;
typedef struct { struct stl_neighbors {
stl_vertex p1; stl_neighbors() { reset(); }
stl_vertex p2; void reset() {
int facet_number; neighbor[0] = -1;
} stl_edge; neighbor[1] = -1;
neighbor[2] = -1;
which_vertex_not[0] = -1;
which_vertex_not[1] = -1;
which_vertex_not[2] = -1;
}
int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); }
int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
typedef struct stl_hash_edge { // Index of a neighbor facet.
// Key of a hash edge: sorted vertices of the edge. int neighbor[3];
uint32_t key[6]; // Index of an opposite vertex at the neighbor face.
// Compare two keys. char which_vertex_not[3];
bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; } };
bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); }
int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; }
// Index of a facet owning this edge.
int facet_number;
// Index of this edge inside the facet with an index of facet_number.
// If this edge is stored backwards, which_edge is increased by 3.
int which_edge;
struct stl_hash_edge *next;
} stl_hash_edge;
typedef struct { struct stl_stats {
// Index of a neighbor facet. stl_stats() { this->reset(); }
int neighbor[3]; void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; }
// Index of an opposite vertex at the neighbor face. char header[81];
char which_vertex_not[3]; stl_type type;
} stl_neighbors; uint32_t number_of_facets;
stl_vertex max;
stl_vertex min;
stl_vertex size;
float bounding_diameter;
float shortest_edge;
float volume;
int connected_edges;
int connected_facets_1_edge;
int connected_facets_2_edge;
int connected_facets_3_edge;
int facets_w_1_bad_edge;
int facets_w_2_bad_edge;
int facets_w_3_bad_edge;
int original_num_facets;
int edges_fixed;
int degenerate_facets;
int facets_removed;
int facets_added;
int facets_reversed;
int backwards_edges;
int normals_fixed;
int number_of_parts;
};
typedef struct { struct stl_file {
int vertex[3]; stl_file() {}
} v_indices_struct;
typedef struct { void clear() {
char header[81]; this->facet_start.clear();
stl_type type; this->neighbors_start.clear();
uint32_t number_of_facets; this->stats.reset();
stl_vertex max; }
stl_vertex min;
stl_vertex size;
float bounding_diameter;
float shortest_edge;
float volume;
unsigned number_of_blocks;
int connected_edges;
int connected_facets_1_edge;
int connected_facets_2_edge;
int connected_facets_3_edge;
int facets_w_1_bad_edge;
int facets_w_2_bad_edge;
int facets_w_3_bad_edge;
int original_num_facets;
int edges_fixed;
int degenerate_facets;
int facets_removed;
int facets_added;
int facets_reversed;
int backwards_edges;
int normals_fixed;
int number_of_parts;
int malloced;
int freed;
int facets_malloced;
int collisions;
int shared_vertices;
int shared_malloced;
} stl_stats;
typedef struct { std::vector<stl_facet> facet_start;
FILE *fp; std::vector<stl_neighbors> neighbors_start;
stl_facet *facet_start; // Statistics
stl_hash_edge **heads; stl_stats stats;
stl_hash_edge *tail; };
int M;
stl_neighbors *neighbors_start;
v_indices_struct *v_indices;
stl_vertex *v_shared;
stl_stats stats;
char error;
} stl_file;
struct indexed_triangle_set
{
indexed_triangle_set() {}
extern void stl_open(stl_file *stl, const char *file); void clear() { indices.clear(); vertices.clear(); }
extern void stl_close(stl_file *stl);
std::vector<stl_triangle_vertex_indices> indices;
std::vector<stl_vertex> vertices;
//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
//std::vector<stl_normal> normals
};
extern bool stl_open(stl_file *stl, const char *file);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file); extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
extern void stl_print_neighbors(stl_file *stl, char *file); extern bool stl_print_neighbors(stl_file *stl, char *file);
extern void stl_put_little_int(FILE *fp, int value_in); extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label);
extern void stl_put_little_float(FILE *fp, float value_in); extern bool stl_write_binary(stl_file *stl, const char *file, const char *label);
extern void stl_write_ascii(stl_file *stl, const char *file, const char *label);
extern void stl_write_binary(stl_file *stl, const char *file, const char *label);
extern void stl_write_binary_block(stl_file *stl, FILE *fp);
extern void stl_check_facets_exact(stl_file *stl); extern void stl_check_facets_exact(stl_file *stl);
extern void stl_check_facets_nearby(stl_file *stl, float tolerance); extern void stl_check_facets_nearby(stl_file *stl, float tolerance);
extern void stl_remove_unconnected_facets(stl_file *stl); extern void stl_remove_unconnected_facets(stl_file *stl);
extern void stl_write_vertex(stl_file *stl, int facet, int vertex); extern void stl_write_vertex(stl_file *stl, int facet, int vertex);
extern void stl_write_facet(stl_file *stl, char *label, int facet); extern void stl_write_facet(stl_file *stl, char *label, int facet);
extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge);
extern void stl_write_neighbor(stl_file *stl, int facet); extern void stl_write_neighbor(stl_file *stl, int facet);
extern void stl_write_quad_object(stl_file *stl, char *file); extern bool stl_write_quad_object(stl_file *stl, char *file);
extern void stl_verify_neighbors(stl_file *stl); extern void stl_verify_neighbors(stl_file *stl);
extern void stl_fill_holes(stl_file *stl); extern void stl_fill_holes(stl_file *stl);
extern void stl_fix_normal_directions(stl_file *stl); extern void stl_fix_normal_directions(stl_file *stl);
@ -186,36 +178,30 @@ extern void stl_get_size(stl_file *stl);
template<typename T> template<typename T>
extern void stl_transform(stl_file *stl, T *trafo3x4) extern void stl_transform(stl_file *stl, T *trafo3x4)
{ {
if (stl->error) for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
return; stl_facet &face = stl->facet_start[i_face];
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
stl_vertex &v_dst = face.vertex[i_vertex];
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
stl_vertex &v_dst = face.normal;
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2));
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2));
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2));
}
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { stl_get_size(stl);
stl_facet &face = stl->facet_start[i_face];
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
stl_vertex &v_dst = face.vertex[i_vertex];
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
stl_vertex &v_dst = face.normal;
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2));
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2));
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2));
}
stl_get_size(stl);
} }
template<typename T> template<typename T>
inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t) inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
{ {
if (stl->error)
return;
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0); const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
for (size_t i = 0; i < stl->stats.number_of_facets; ++i) { for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_facet &f = stl->facet_start[i]; stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j) for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval(); f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
@ -228,10 +214,7 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Aff
template<typename T> template<typename T>
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m) inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
{ {
if (stl->error) for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
return;
for (size_t i = 0; i < stl->stats.number_of_facets; ++i) {
stl_facet &f = stl->facet_start[i]; stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j) for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval(); f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
@ -241,13 +224,43 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
stl_get_size(stl); stl_get_size(stl);
} }
extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_invalidate_shared_vertices(stl_file *stl); template<typename T>
extern void stl_generate_shared_vertices(stl_file *stl); extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
extern void stl_write_obj(stl_file *stl, const char *file); {
extern void stl_write_off(stl_file *stl, const char *file); for (stl_vertex &v_dst : its.vertices) {
extern void stl_write_dxf(stl_file *stl, const char *file, char *label); stl_vertex v_src = v_dst;
extern void stl_write_vrml(stl_file *stl, const char *file); v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
{
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
for (stl_vertex &v : its.vertices)
v = (t * v.template cast<T>()).template cast<float>().eval();
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
{
for (stl_vertex &v : its.vertices)
v = (m * v.template cast<T>()).template cast<float>().eval();
}
extern void its_rotate_x(indexed_triangle_set &its, float angle);
extern void its_rotate_y(indexed_triangle_set &its, float angle);
extern void its_rotate_z(indexed_triangle_set &its, float angle);
extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its);
extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) { inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]); normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
} }
@ -258,24 +271,18 @@ inline void stl_normalize_vector(stl_normal &normal) {
else else
normal *= float(1.0 / length); normal *= float(1.0 / length);
} }
inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
return (a(0) != b(0)) ? (a(0) < b(0)) :
((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
}
extern void stl_calculate_volume(stl_file *stl); extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag);
extern void stl_initialize(stl_file *stl);
extern void stl_count_facets(stl_file *stl, const char *file);
extern void stl_allocate(stl_file *stl); extern void stl_allocate(stl_file *stl);
extern void stl_read(stl_file *stl, int first_facet, bool first); extern void stl_read(stl_file *stl, int first_facet, bool first);
extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first);
extern void stl_reallocate(stl_file *stl); extern void stl_reallocate(stl_file *stl);
extern void stl_add_facet(stl_file *stl, stl_facet *new_facet); extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet);
extern void stl_clear_error(stl_file *stl); // Validate the mesh, assert on error.
extern int stl_get_error(stl_file *stl); extern bool stl_validate(const stl_file *stl);
extern void stl_exit_on_error(stl_file *stl); extern bool stl_validate(const stl_file *stl, const indexed_triangle_set &its);
#endif #endif

View file

@ -22,159 +22,86 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/predef/other/endian.h>
#include "stl.h" #include "stl.h"
#include <boost/nowide/cstdio.hpp> void stl_stats_out(stl_file *stl, FILE *file, char *input_file)
#include <boost/detail/endian.hpp> {
// This is here for Slic3r, without our config.h it won't use this part of the code anyway.
#if !defined(SEEK_SET)
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
void
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
if (stl->error) return;
/* this is here for Slic3r, without our config.h
it won't use this part of the code anyway */
#ifndef VERSION #ifndef VERSION
#define VERSION "unknown" #define VERSION "unknown"
#endif #endif
fprintf(file, "\n\ fprintf(file, "\n================= Results produced by ADMesh version " VERSION " ================\n");
================= Results produced by ADMesh version " VERSION " ================\n"); fprintf(file, "Input file : %s\n", input_file);
fprintf(file, "\ if (stl->stats.type == binary)
Input file : %s\n", input_file); fprintf(file, "File type : Binary STL file\n");
if(stl->stats.type == binary) { else
fprintf(file, "\ fprintf(file, "File type : ASCII STL file\n");
File type : Binary STL file\n"); fprintf(file, "Header : %s\n", stl->stats.header);
} else { fprintf(file, "============== Size ==============\n");
fprintf(file, "\ fprintf(file, "Min X = % f, Max X = % f\n", stl->stats.min(0), stl->stats.max(0));
File type : ASCII STL file\n"); fprintf(file, "Min Y = % f, Max Y = % f\n", stl->stats.min(1), stl->stats.max(1));
} fprintf(file, "Min Z = % f, Max Z = % f\n", stl->stats.min(2), stl->stats.max(2));
fprintf(file, "\ fprintf(file, "========= Facet Status ========== Original ============ Final ====\n");
Header : %s\n", stl->stats.header); fprintf(file, "Number of facets : %5d %5d\n", stl->stats.original_num_facets, stl->stats.number_of_facets);
fprintf(file, "============== Size ==============\n"); fprintf(file, "Facets with 1 disconnected edge : %5d %5d\n",
fprintf(file, "Min X = % f, Max X = % f\n", stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge);
stl->stats.min(0), stl->stats.max(0)); fprintf(file, "Facets with 2 disconnected edges : %5d %5d\n",
fprintf(file, "Min Y = % f, Max Y = % f\n", stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge);
stl->stats.min(1), stl->stats.max(1)); fprintf(file, "Facets with 3 disconnected edges : %5d %5d\n",
fprintf(file, "Min Z = % f, Max Z = % f\n", stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_1_edge);
stl->stats.min(2), stl->stats.max(2)); fprintf(file, "Total disconnected facets : %5d %5d\n",
stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_3_edge);
fprintf(file, "\ fprintf(file, "=== Processing Statistics === ===== Other Statistics =====\n");
========= Facet Status ========== Original ============ Final ====\n"); fprintf(file, "Number of parts : %5d Volume : %f\n", stl->stats.number_of_parts, stl->stats.volume);
fprintf(file, "\ fprintf(file, "Degenerate facets : %5d\n", stl->stats.degenerate_facets);
Number of facets : %5d %5d\n", fprintf(file, "Edges fixed : %5d\n", stl->stats.edges_fixed);
stl->stats.original_num_facets, stl->stats.number_of_facets); fprintf(file, "Facets removed : %5d\n", stl->stats.facets_removed);
fprintf(file, "\ fprintf(file, "Facets added : %5d\n", stl->stats.facets_added);
Facets with 1 disconnected edge : %5d %5d\n", fprintf(file, "Facets reversed : %5d\n", stl->stats.facets_reversed);
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - fprintf(file, "Backwards edges : %5d\n", stl->stats.backwards_edges);
stl->stats.connected_facets_3_edge); fprintf(file, "Normals fixed : %5d\n", stl->stats.normals_fixed);
fprintf(file, "\
Facets with 2 disconnected edges : %5d %5d\n",
stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge -
stl->stats.connected_facets_2_edge);
fprintf(file, "\
Facets with 3 disconnected edges : %5d %5d\n",
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
stl->stats.connected_facets_1_edge);
fprintf(file, "\
Total disconnected facets : %5d %5d\n",
stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge +
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
stl->stats.connected_facets_3_edge);
fprintf(file,
"=== Processing Statistics === ===== Other Statistics =====\n");
fprintf(file, "\
Number of parts : %5d Volume : % f\n",
stl->stats.number_of_parts, stl->stats.volume);
fprintf(file, "\
Degenerate facets : %5d\n", stl->stats.degenerate_facets);
fprintf(file, "\
Edges fixed : %5d\n", stl->stats.edges_fixed);
fprintf(file, "\
Facets removed : %5d\n", stl->stats.facets_removed);
fprintf(file, "\
Facets added : %5d\n", stl->stats.facets_added);
fprintf(file, "\
Facets reversed : %5d\n", stl->stats.facets_reversed);
fprintf(file, "\
Backwards edges : %5d\n", stl->stats.backwards_edges);
fprintf(file, "\
Normals fixed : %5d\n", stl->stats.normals_fixed);
} }
void bool stl_write_ascii(stl_file *stl, const char *file, const char *label)
stl_write_ascii(stl_file *stl, const char *file, const char *label) { {
int i; FILE *fp = boost::nowide::fopen(file, "w");
char *error_msg; if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
return false;
}
if (stl->error) return; fprintf(fp, "solid %s\n", label);
/* Open the file */ for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
FILE *fp = boost::nowide::fopen(file, "w"); fprintf(fp, " facet normal % .8E % .8E % .8E\n", stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), stl->facet_start[i].normal(2));
if(fp == NULL) { fprintf(fp, " outer loop\n");
error_msg = (char*) fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2));
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2));
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
file); fprintf(fp, " endloop\n");
perror(error_msg); fprintf(fp, " endfacet\n");
free(error_msg); }
stl->error = 1;
return;
}
fprintf(fp, "solid %s\n", label); fprintf(fp, "endsolid %s\n", label);
fclose(fp);
for(i = 0; i < stl->stats.number_of_facets; i++) { return true;
fprintf(fp, " facet normal % .8E % .8E % .8E\n",
stl->facet_start[i].normal(0), stl->facet_start[i].normal(1),
stl->facet_start[i].normal(2));
fprintf(fp, " outer loop\n");
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0](2));
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1](2));
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2](2));
fprintf(fp, " endloop\n");
fprintf(fp, " endfacet\n");
}
fprintf(fp, "endsolid %s\n", label);
fclose(fp);
} }
void bool stl_print_neighbors(stl_file *stl, char *file)
stl_print_neighbors(stl_file *stl, char *file) { {
int i; FILE *fp = boost::nowide::fopen(file, "w");
FILE *fp; if (fp == nullptr) {
char *error_msg; BOOST_LOG_TRIVIAL(error) << "stl_print_neighbors: Couldn't open " << file << " for writing";
return false;
}
if (stl->error) return; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
/* Open the file */
fp = boost::nowide::fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
i, i,
stl->neighbors_start[i].neighbor[0], stl->neighbors_start[i].neighbor[0],
(int)stl->neighbors_start[i].which_vertex_not[0], (int)stl->neighbors_start[i].which_vertex_not[0],
@ -182,234 +109,142 @@ stl_print_neighbors(stl_file *stl, char *file) {
(int)stl->neighbors_start[i].which_vertex_not[1], (int)stl->neighbors_start[i].which_vertex_not[1],
stl->neighbors_start[i].neighbor[2], stl->neighbors_start[i].neighbor[2],
(int)stl->neighbors_start[i].which_vertex_not[2]); (int)stl->neighbors_start[i].which_vertex_not[2]);
} }
fclose(fp); fclose(fp);
return true;
} }
#ifndef BOOST_LITTLE_ENDIAN #if BOOST_ENDIAN_BIG_BYTE
// Swap a buffer of 32bit data from little endian to big endian and vice versa. // Swap a buffer of 32bit data from little endian to big endian and vice versa.
void stl_internal_reverse_quads(char *buf, size_t cnt) void stl_internal_reverse_quads(char *buf, size_t cnt)
{ {
for (size_t i = 0; i < cnt; i += 4) { for (size_t i = 0; i < cnt; i += 4) {
std::swap(buf[i], buf[i+3]); std::swap(buf[i], buf[i+3]);
std::swap(buf[i+1], buf[i+2]); std::swap(buf[i+1], buf[i+2]);
} }
} }
#endif #endif
void bool stl_write_binary(stl_file *stl, const char *file, const char *label)
stl_write_binary(stl_file *stl, const char *file, const char *label) { {
FILE *fp; FILE *fp = boost::nowide::fopen(file, "wb");
int i; if (fp == nullptr) {
char *error_msg; BOOST_LOG_TRIVIAL(error) << "stl_write_binary: Couldn't open " << file << " for writing";
return false;
}
if (stl->error) return; fprintf(fp, "%s", label);
for (size_t i = strlen(label); i < LABEL_SIZE; ++ i)
putc(0, fp);
/* Open the file */ #if !defined(SEEK_SET)
fp = boost::nowide::fopen(file, "wb"); #define SEEK_SET 0
if(fp == NULL) { #endif
error_msg = (char*) fseek(fp, LABEL_SIZE, SEEK_SET);
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ #if BOOST_ENDIAN_LITTLE_BYTE
sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing", fwrite(&stl->stats.number_of_facets, 4, 1, fp);
file); for (const stl_facet &facet : stl->facet_start)
perror(error_msg); fwrite(&facet, SIZEOF_STL_FACET, 1, fp);
free(error_msg); #else /* BOOST_ENDIAN_LITTLE_BYTE */
stl->error = 1; char buffer[50];
return; // Convert the number of facets to little endian.
} memcpy(buffer, &stl->stats.number_of_facets, 4);
stl_internal_reverse_quads(buffer, 4);
fprintf(fp, "%s", label); fwrite(buffer, 4, 1, fp);
for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); for (i = 0; i < stl->stats.number_of_facets; ++ i) {
memcpy(buffer, stl->facet_start + i, 50);
fseek(fp, LABEL_SIZE, SEEK_SET); // Convert to little endian.
#ifdef BOOST_LITTLE_ENDIAN stl_internal_reverse_quads(buffer, 48);
fwrite(&stl->stats.number_of_facets, 4, 1, fp); fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
for (i = 0; i < stl->stats.number_of_facets; ++ i) }
fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); #endif /* BOOST_ENDIAN_LITTLE_BYTE */
#else /* BOOST_LITTLE_ENDIAN */ fclose(fp);
char buffer[50]; return true;
// Convert the number of facets to little endian.
memcpy(buffer, &stl->stats.number_of_facets, 4);
stl_internal_reverse_quads(buffer, 4);
fwrite(buffer, 4, 1, fp);
for (i = 0; i < stl->stats.number_of_facets; ++ i) {
memcpy(buffer, stl->facet_start + i, 50);
// Convert to little endian.
stl_internal_reverse_quads(buffer, 48);
fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
}
#endif /* BOOST_LITTLE_ENDIAN */
fclose(fp);
} }
void void stl_write_vertex(stl_file *stl, int facet, int vertex)
stl_write_vertex(stl_file *stl, int facet, int vertex) { {
if (stl->error) return; printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
stl->facet_start[facet].vertex[vertex](0), stl->facet_start[facet].vertex[vertex](0),
stl->facet_start[facet].vertex[vertex](1), stl->facet_start[facet].vertex[vertex](1),
stl->facet_start[facet].vertex[vertex](2)); stl->facet_start[facet].vertex[vertex](2));
} }
void void stl_write_facet(stl_file *stl, char *label, int facet)
stl_write_facet(stl_file *stl, char *label, int facet) { {
if (stl->error) return; printf("facet (%d)/ %s\n", facet, label);
printf("facet (%d)/ %s\n", facet, label); stl_write_vertex(stl, facet, 0);
stl_write_vertex(stl, facet, 0); stl_write_vertex(stl, facet, 1);
stl_write_vertex(stl, facet, 1); stl_write_vertex(stl, facet, 2);
stl_write_vertex(stl, facet, 2);
} }
void void stl_write_neighbor(stl_file *stl, int facet)
stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) { {
if (stl->error) return; printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label); stl->neighbors_start[facet].neighbor[0],
if(edge.which_edge < 3) { stl->neighbors_start[facet].neighbor[1],
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); stl->neighbors_start[facet].neighbor[2],
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); stl->neighbors_start[facet].which_vertex_not[0],
} else { stl->neighbors_start[facet].which_vertex_not[1],
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); stl->neighbors_start[facet].which_vertex_not[2]);
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
}
} }
void bool stl_write_quad_object(stl_file *stl, char *file)
stl_write_neighbor(stl_file *stl, int facet) { {
if (stl->error) return; stl_vertex connect_color = stl_vertex::Zero();
printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, stl_vertex uncon_1_color = stl_vertex::Zero();
stl->neighbors_start[facet].neighbor[0], stl_vertex uncon_2_color = stl_vertex::Zero();
stl->neighbors_start[facet].neighbor[1], stl_vertex uncon_3_color = stl_vertex::Zero();
stl->neighbors_start[facet].neighbor[2], stl_vertex color;
stl->neighbors_start[facet].which_vertex_not[0],
stl->neighbors_start[facet].which_vertex_not[1],
stl->neighbors_start[facet].which_vertex_not[2]);
}
void FILE *fp = boost::nowide::fopen(file, "w");
stl_write_quad_object(stl_file *stl, char *file) { if (fp == nullptr) {
FILE *fp; BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing";
int i; return false;
int j; }
char *error_msg;
stl_vertex connect_color = stl_vertex::Zero();
stl_vertex uncon_1_color = stl_vertex::Zero();
stl_vertex uncon_2_color = stl_vertex::Zero();
stl_vertex uncon_3_color = stl_vertex::Zero();
stl_vertex color;
if (stl->error) return; fprintf(fp, "CQUAD\n");
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
/* Open the file */ switch (stl->neighbors_start[i].num_neighbors_missing()) {
fp = boost::nowide::fopen(file, "w"); case 0: color = connect_color; break;
if(fp == NULL) { case 1: color = uncon_1_color; break;
error_msg = (char*) case 2: color = uncon_2_color; break;
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ default: color = uncon_3_color;
sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing", }
file); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
perror(error_msg); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
free(error_msg); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
stl->error = 1; fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
return;
}
fprintf(fp, "CQUAD\n");
for(i = 0; i < stl->stats.number_of_facets; i++) {
j = ((stl->neighbors_start[i].neighbor[0] == -1) +
(stl->neighbors_start[i].neighbor[1] == -1) +
(stl->neighbors_start[i].neighbor[2] == -1));
if(j == 0) {
color = connect_color;
} else if(j == 1) {
color = uncon_1_color;
} else if(j == 2) {
color = uncon_2_color;
} else {
color = uncon_3_color;
}
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[0](0),
stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[1](0),
stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2](0),
stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2](0),
stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
} }
fclose(fp); fclose(fp);
return true;
} }
void bool stl_write_dxf(stl_file *stl, const char *file, char *label)
stl_write_dxf(stl_file *stl, const char *file, char *label) { {
int i; FILE *fp = boost::nowide::fopen(file, "w");
FILE *fp; if (fp == nullptr) {
char *error_msg; BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing";
return false;
}
if (stl->error) return; fprintf(fp, "999\n%s\n", label);
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\
0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
/* Open the file */ fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
fp = boost::nowide::fopen(file, "w");
if(fp == NULL) {
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;
}
fprintf(fp, "999\n%s\n", label); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); fprintf(fp, "0\n3DFACE\n8\n0\n");
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\ fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2));
0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n"); fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2));
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
}
fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); fprintf(fp, "0\nENDSEC\n0\nEOF\n");
fclose(fp);
for(i = 0; i < stl->stats.number_of_facets; i++) { return true;
fprintf(fp, "0\n3DFACE\n8\n0\n");
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n",
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0](2));
fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n",
stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1](2));
fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n",
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2](2));
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n",
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2](2));
}
fprintf(fp, "0\nENDSEC\n0\nEOF\n");
fclose(fp);
}
void
stl_clear_error(stl_file *stl) {
stl->error = 0;
}
void
stl_exit_on_error(stl_file *stl) {
if (!stl->error) return;
stl->error = 0;
stl_close(stl);
exit(1);
}
int
stl_get_error(stl_file *stl) {
return stl->error;
} }

View file

@ -26,6 +26,7 @@
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#include <boost/detail/endian.hpp> #include <boost/detail/endian.hpp>
@ -35,351 +36,236 @@
#error "SEEK_SET not defined" #error "SEEK_SET not defined"
#endif #endif
void static FILE* stl_open_count_facets(stl_file *stl, const char *file)
stl_open(stl_file *stl, const char *file) { {
stl_initialize(stl); // Open the file in binary mode first.
stl_count_facets(stl, file); FILE *fp = boost::nowide::fopen(file, "rb");
stl_allocate(stl); if (fp == nullptr) {
stl_read(stl, 0, true); BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading";
if (stl->fp != nullptr) { return nullptr;
fclose(stl->fp); }
stl->fp = nullptr; // Find size of file.
} fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
// Check for binary or ASCII file.
fseek(fp, HEADER_SIZE, SEEK_SET);
unsigned char chtest[128];
if (! fread(chtest, sizeof(chtest), 1, fp)) {
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The input is an empty file: " << file;
fclose(fp);
return nullptr;
}
stl->stats.type = ascii;
for (size_t s = 0; s < sizeof(chtest); s++) {
if (chtest[s] > 127) {
stl->stats.type = binary;
break;
}
}
rewind(fp);
uint32_t num_facets = 0;
// Get the header and the number of facets in the .STL file.
// If the .STL file is binary, then do the following:
if (stl->stats.type == binary) {
// Test if the STL file has the right size.
if (((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) || (file_size < STL_MIN_FILE_SIZE)) {
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The file " << file << " has the wrong size.";
fclose(fp);
return nullptr;
}
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
// Read the header.
if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79)
stl->stats.header[80] = '\0';
// Read the int following the header. This should contain # of facets.
uint32_t header_num_facets;
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0;
#ifndef BOOST_LITTLE_ENDIAN
// Convert from little endian to big endian.
stl_internal_reverse_quads((char*)&header_num_facets, 4);
#endif /* BOOST_LITTLE_ENDIAN */
if (! header_num_faces_read || num_facets != header_num_facets)
BOOST_LOG_TRIVIAL(info) << "stl_open_count_facets: Warning: File size doesn't match number of facets in the header: " << file;
}
// Otherwise, if the .STL file is ASCII, then do the following:
else
{
// Reopen the file in text mode (for getting correct newlines on Windows)
// fix to silence a warning about unused return value.
// obviously if it fails we have problems....
fp = boost::nowide::freopen(file, "r", fp);
// do another null check to be safe
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading";
fclose(fp);
return nullptr;
}
// Find the number of facets.
char linebuf[100];
int num_lines = 1;
while (fgets(linebuf, 100, fp) != nullptr) {
// Don't count short lines.
if (strlen(linebuf) <= 4)
continue;
// Skip solid/endsolid lines as broken STL file generators may put several of them.
if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0)
continue;
++ num_lines;
}
rewind(fp);
// Get the header.
int i = 0;
for (; i < 80 && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ;
stl->stats.header[i] = '\0'; // Lose the '\n'
stl->stats.header[80] = '\0';
num_facets = num_lines / ASCII_LINES_PER_FACET;
}
stl->stats.number_of_facets += num_facets;
stl->stats.original_num_facets = stl->stats.number_of_facets;
return fp;
} }
void /* Reads the contents of the file pointed to by fp into the stl structure,
stl_initialize(stl_file *stl) { starting at facet first_facet. The second argument says if it's our first
memset(stl, 0, sizeof(stl_file)); time running this for the stl and therefore we should reset our max and min stats. */
stl->stats.volume = -1.0; static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
{
if (stl->stats.type == binary)
fseek(fp, HEADER_SIZE, SEEK_SET);
else
rewind(fp);
char normal_buf[3][32];
for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) {
stl_facet facet;
if (stl->stats.type == binary) {
// Read a single facet from a binary .STL file. We assume little-endian architecture!
if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET)
return false;
#ifndef BOOST_LITTLE_ENDIAN
// Convert the loaded little endian data to big endian.
stl_internal_reverse_quads((char*)&facet, 48);
#endif /* BOOST_LITTLE_ENDIAN */
} else {
// Read a single facet from an ASCII .STL file
// skip solid/endsolid
// (in this order, otherwise it won't work when they are paired in the middle of a file)
fscanf(fp, "endsolid%*[^\n]\n");
fscanf(fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
assert(res_normal == 3);
int res_outer_loop = fscanf(fp, " outer loop");
assert(res_outer_loop == 0);
int res_vertex1 = fscanf(fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3);
int res_vertex2 = fscanf(fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3);
int res_vertex3 = fscanf(fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3);
int res_endloop = fscanf(fp, " endloop");
assert(res_endloop == 0);
// There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines.
int res_endfacet = fscanf(fp, " endfacet ");
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
BOOST_LOG_TRIVIAL(error) << "Something is syntactically very wrong with this ASCII STL! ";
return false;
}
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal));
}
}
#if 0
// Report close to zero vertex coordinates. Due to the nature of the floating point numbers,
// close to zero values may be represented with singificantly higher precision than the rest of the vertices.
// It may be worth to round these numbers to zero during loading to reduce the number of errors reported
// during the STL import.
for (size_t j = 0; j < 3; ++ j) {
if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
}
#endif
// Write the facet into memory.
stl->facet_start[i] = facet;
stl_facet_stats(stl, facet, first);
}
stl->stats.size = stl->stats.max - stl->stats.min;
stl->stats.bounding_diameter = stl->stats.size.norm();
return true;
}
bool stl_open(stl_file *stl, const char *file)
{
stl->clear();
FILE *fp = stl_open_count_facets(stl, file);
if (fp == nullptr)
return false;
stl_allocate(stl);
bool result = stl_read(stl, fp, 0, true);
fclose(fp);
return result;
} }
#ifndef BOOST_LITTLE_ENDIAN #ifndef BOOST_LITTLE_ENDIAN
extern void stl_internal_reverse_quads(char *buf, size_t cnt); extern void stl_internal_reverse_quads(char *buf, size_t cnt);
#endif /* BOOST_LITTLE_ENDIAN */ #endif /* BOOST_LITTLE_ENDIAN */
void void stl_allocate(stl_file *stl)
stl_count_facets(stl_file *stl, const char *file) { {
long file_size; // Allocate memory for the entire .STL file.
uint32_t header_num_facets; stl->facet_start.assign(stl->stats.number_of_facets, stl_facet());
uint32_t num_facets; // Allocate memory for the neighbors list.
int i; stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors());
size_t s;
unsigned char chtest[128];
int num_lines = 1;
char *error_msg;
if (stl->error) return;
/* Open the file in binary mode first */
stl->fp = boost::nowide::fopen(file, "rb");
if(stl->fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Find size of file */
fseek(stl->fp, 0, SEEK_END);
file_size = ftell(stl->fp);
/* Check for binary or ASCII file */
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
if (!fread(chtest, sizeof(chtest), 1, stl->fp)) {
perror("The input is an empty file");
stl->error = 1;
return;
}
stl->stats.type = ascii;
for(s = 0; s < sizeof(chtest); s++) {
if(chtest[s] > 127) {
stl->stats.type = binary;
break;
}
}
rewind(stl->fp);
/* Get the header and the number of facets in the .STL file */
/* If the .STL file is binary, then do the following */
if(stl->stats.type == binary) {
/* Test if the STL file has the right size */
if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0)
|| (file_size < STL_MIN_FILE_SIZE)) {
fprintf(stderr, "The file %s has the wrong size.\n", file);
stl->error = 1;
return;
}
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
/* Read the header */
if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) {
stl->stats.header[80] = '\0';
}
/* Read the int following the header. This should contain # of facets */
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0;
#ifndef BOOST_LITTLE_ENDIAN
// Convert from little endian to big endian.
stl_internal_reverse_quads((char*)&header_num_facets, 4);
#endif /* BOOST_LITTLE_ENDIAN */
if (! header_num_faces_read || num_facets != header_num_facets) {
fprintf(stderr,
"Warning: File size doesn't match number of facets in the header\n");
}
}
/* Otherwise, if the .STL file is ASCII, then do the following */
else {
/* Reopen the file in text mode (for getting correct newlines on Windows) */
// fix to silence a warning about unused return value.
// obviously if it fails we have problems....
stl->fp = boost::nowide::freopen(file, "r", stl->fp);
// do another null check to be safe
if(stl->fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Find the number of facets */
char linebuf[100];
while (fgets(linebuf, 100, stl->fp) != NULL) {
/* don't count short lines */
if (strlen(linebuf) <= 4) continue;
/* skip solid/endsolid lines as broken STL file generators may put several of them */
if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue;
++num_lines;
}
rewind(stl->fp);
/* Get the header */
for(i = 0;
(i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++);
stl->stats.header[i] = '\0'; /* Lose the '\n' */
stl->stats.header[80] = '\0';
num_facets = num_lines / ASCII_LINES_PER_FACET;
}
stl->stats.number_of_facets += num_facets;
stl->stats.original_num_facets = stl->stats.number_of_facets;
} }
void void stl_reallocate(stl_file *stl)
stl_allocate(stl_file *stl) { {
if (stl->error) return; stl->facet_start.resize(stl->stats.number_of_facets);
stl->neighbors_start.resize(stl->stats.number_of_facets);
/* Allocate memory for the entire .STL file */
stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets,
sizeof(stl_facet));
if(stl->facet_start == NULL) perror("stl_initialize");
stl->stats.facets_malloced = stl->stats.number_of_facets;
/* Allocate memory for the neighbors list */
stl->neighbors_start = (stl_neighbors*)
calloc(stl->stats.number_of_facets, sizeof(stl_neighbors));
if(stl->facet_start == NULL) perror("stl_initialize");
}
void
stl_open_merge(stl_file *stl, char *file_to_merge) {
int num_facets_so_far;
stl_type origStlType;
FILE *origFp;
stl_file stl_to_merge;
if (stl->error) return;
/* Record how many facets we have so far from the first file. We will start putting
facets in the next position. Since we're 0-indexed, it'l be the same position. */
num_facets_so_far = stl->stats.number_of_facets;
/* Record the file type we started with: */
origStlType=stl->stats.type;
/* Record the file pointer too: */
origFp=stl->fp;
/* Initialize the sturucture with zero stats, header info and sizes: */
stl_initialize(&stl_to_merge);
stl_count_facets(&stl_to_merge, file_to_merge);
/* Copy what we need to into stl so that we can read the file_to_merge directly into it
using stl_read: Save the rest of the valuable info: */
stl->stats.type=stl_to_merge.stats.type;
stl->fp=stl_to_merge.fp;
/* Add the number of facets we already have in stl with what we we found in stl_to_merge but
haven't read yet. */
stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets;
/* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */
stl_reallocate(stl);
/* Read the file to merge directly into stl, adding it to what we have already.
Start at num_facets_so_far, the index to the first unused facet. Also say
that this isn't our first time so we should augment stats like min and max
instead of erasing them. */
stl_read(stl, num_facets_so_far, false);
/* Restore the stl information we overwrote (for stl_read) so that it still accurately
reflects the subject part: */
stl->stats.type=origStlType;
stl->fp=origFp;
}
extern void
stl_reallocate(stl_file *stl) {
if (stl->error) return;
/* Reallocate more memory for the .STL file(s) */
stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets *
sizeof(stl_facet));
if(stl->facet_start == NULL) perror("stl_initialize");
stl->stats.facets_malloced = stl->stats.number_of_facets;
/* Reallocate more memory for the neighbors list */
stl->neighbors_start = (stl_neighbors*)
realloc(stl->neighbors_start, stl->stats.number_of_facets *
sizeof(stl_neighbors));
if(stl->facet_start == NULL) perror("stl_initialize");
}
/* Reads the contents of the file pointed to by stl->fp into the stl structure,
starting at facet first_facet. The second argument says if it's our first
time running this for the stl and therefore we should reset our max and min stats. */
void stl_read(stl_file *stl, int first_facet, bool first) {
stl_facet facet;
if (stl->error) return;
if(stl->stats.type == binary) {
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
} else {
rewind(stl->fp);
}
char normal_buf[3][32];
for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) {
if(stl->stats.type == binary)
/* Read a single facet from a binary .STL file */
{
/* we assume little-endian architecture! */
if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) {
stl->error = 1;
return;
}
#ifndef BOOST_LITTLE_ENDIAN
// Convert the loaded little endian data to big endian.
stl_internal_reverse_quads((char*)&facet, 48);
#endif /* BOOST_LITTLE_ENDIAN */
} else
/* Read a single facet from an ASCII .STL file */
{
// skip solid/endsolid
// (in this order, otherwise it won't work when they are paired in the middle of a file)
fscanf(stl->fp, "endsolid%*[^\n]\n");
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
assert(res_normal == 3);
int res_outer_loop = fscanf(stl->fp, " outer loop");
assert(res_outer_loop == 0);
int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3);
int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3);
int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3);
int res_endloop = fscanf(stl->fp, " endloop");
assert(res_endloop == 0);
// There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines.
int res_endfacet = fscanf(stl->fp, " endfacet ");
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
perror("Something is syntactically very wrong with this ASCII STL!");
stl->error = 1;
return;
}
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal));
}
}
#if 0
// Report close to zero vertex coordinates. Due to the nature of the floating point numbers,
// close to zero values may be represented with singificantly higher precision than the rest of the vertices.
// It may be worth to round these numbers to zero during loading to reduce the number of errors reported
// during the STL import.
for (size_t j = 0; j < 3; ++ j) {
if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
}
#endif
/* Write the facet into memory. */
stl->facet_start[i] = facet;
stl_facet_stats(stl, facet, first);
}
stl->stats.size = stl->stats.max - stl->stats.min;
stl->stats.bounding_diameter = stl->stats.size.norm();
} }
void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
{ {
if (stl->error) // While we are going through all of the facets, let's find the
return; // maximum and minimum values for x, y, and z
// While we are going through all of the facets, let's find the if (first) {
// maximum and minimum values for x, y, and z // Initialize the max and min values the first time through
stl->stats.min = facet.vertex[0];
stl->stats.max = facet.vertex[0];
stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs();
stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
first = false;
}
if (first) { // Now find the max and min values.
// Initialize the max and min values the first time through for (size_t i = 0; i < 3; ++ i) {
stl->stats.min = facet.vertex[0]; stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
stl->stats.max = facet.vertex[0]; stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs(); }
stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
first = false;
}
// Now find the max and min values.
for (size_t i = 0; i < 3; ++ i) {
stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
}
}
void stl_close(stl_file *stl)
{
assert(stl->fp == nullptr);
assert(stl->heads == nullptr);
assert(stl->tail == nullptr);
if (stl->facet_start != NULL)
free(stl->facet_start);
if (stl->neighbors_start != NULL)
free(stl->neighbors_start);
if (stl->v_indices != NULL)
free(stl->v_indices);
if (stl->v_shared != NULL)
free(stl->v_shared);
memset(stl, 0, sizeof(stl_file));
} }

View file

@ -25,435 +25,375 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <boost/log/trivial.hpp>
#include "stl.h" #include "stl.h"
static void stl_rotate(float *x, float *y, const double c, const double s); void stl_verify_neighbors(stl_file *stl)
static float get_area(stl_facet *facet); {
static float get_volume(stl_file *stl); stl->stats.backwards_edges = 0;
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
void for (int j = 0; j < 3; ++ j) {
stl_verify_neighbors(stl_file *stl) { struct stl_edge {
int i; stl_vertex p1;
int j; stl_vertex p2;
stl_edge edge_a; int facet_number;
stl_edge edge_b; };
int neighbor; stl_edge edge_a;
int vnot; edge_a.p1 = stl->facet_start[i].vertex[j];
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
if (stl->error) return; int neighbor = stl->neighbors_start[i].neighbor[j];
if (neighbor == -1)
stl->stats.backwards_edges = 0; continue; // this edge has no neighbor... Continue.
int vnot = stl->neighbors_start[i].which_vertex_not[j];
for(i = 0; i < stl->stats.number_of_facets; i++) { stl_edge edge_b;
for(j = 0; j < 3; j++) { if (vnot < 3) {
edge_a.p1 = stl->facet_start[i].vertex[j]; edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
neighbor = stl->neighbors_start[i].neighbor[j]; } else {
vnot = stl->neighbors_start[i].which_vertex_not[j]; stl->stats.backwards_edges += 1;
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
if(neighbor == -1) edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
continue; /* this edge has no neighbor... Continue. */ }
if(vnot < 3) { if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; // These edges should match but they don't. Print results.
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; BOOST_LOG_TRIVIAL(info) << "edge " << j << " of facet " << i << " doesn't match edge " << (vnot + 1) << " of facet " << neighbor;
} else { stl_write_facet(stl, (char*)"first facet", i);
stl->stats.backwards_edges += 1; stl_write_facet(stl, (char*)"second facet", neighbor);
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; }
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; }
} }
if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
/* These edges should match but they don't. Print results. */
printf("edge %d of facet %d doesn't match edge %d of facet %d\n",
j, i, vnot + 1, neighbor);
stl_write_facet(stl, (char*)"first facet", i);
stl_write_facet(stl, (char*)"second facet", neighbor);
}
}
}
} }
void stl_translate(stl_file *stl, float x, float y, float z) void stl_translate(stl_file *stl, float x, float y, float z)
{ {
if (stl->error) stl_vertex new_min(x, y, z);
return; stl_vertex shift = new_min - stl->stats.min;
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
stl_vertex new_min(x, y, z); for (int j = 0; j < 3; ++ j)
stl_vertex shift = new_min - stl->stats.min; stl->facet_start[i].vertex[j] += shift;
for (int i = 0; i < stl->stats.number_of_facets; ++ i) stl->stats.min = new_min;
for (int j = 0; j < 3; ++ j) stl->stats.max += shift;
stl->facet_start[i].vertex[j] += shift;
stl->stats.min = new_min;
stl->stats.max += shift;
stl_invalidate_shared_vertices(stl);
} }
/* Translates the stl by x,y,z, relatively from wherever it is currently */ /* Translates the stl by x,y,z, relatively from wherever it is currently */
void stl_translate_relative(stl_file *stl, float x, float y, float z) void stl_translate_relative(stl_file *stl, float x, float y, float z)
{ {
if (stl->error) stl_vertex shift(x, y, z);
return; for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
stl_vertex shift(x, y, z); stl->facet_start[i].vertex[j] += shift;
for (int i = 0; i < stl->stats.number_of_facets; ++ i) stl->stats.min += shift;
for (int j = 0; j < 3; ++ j) stl->stats.max += shift;
stl->facet_start[i].vertex[j] += shift;
stl->stats.min += shift;
stl->stats.max += shift;
stl_invalidate_shared_vertices(stl);
} }
void stl_scale_versor(stl_file *stl, const stl_vertex &versor) void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
{ {
if (stl->error) // Scale extents.
return; auto s = versor.array();
stl->stats.min.array() *= s;
// Scale extents. stl->stats.max.array() *= s;
auto s = versor.array(); // Scale size.
stl->stats.min.array() *= s; stl->stats.size.array() *= s;
stl->stats.max.array() *= s; // Scale volume.
// Scale size. if (stl->stats.volume > 0.0)
stl->stats.size.array() *= s; stl->stats.volume *= versor(0) * versor(1) * versor(2);
// Scale volume. // Scale the mesh.
if (stl->stats.volume > 0.0) for (int i = 0; i < stl->stats.number_of_facets; ++ i)
stl->stats.volume *= versor(0) * versor(1) * versor(2); for (int j = 0; j < 3; ++ j)
// Scale the mesh. stl->facet_start[i].vertex[j].array() *= s;
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j].array() *= s;
stl_invalidate_shared_vertices(stl);
} }
static void calculate_normals(stl_file *stl) static void calculate_normals(stl_file *stl)
{ {
if (stl->error) stl_normal normal;
return; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normal normal; stl_normalize_vector(normal);
for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { stl->facet_start[i].normal = normal;
stl_calculate_normal(normal, &stl->facet_start[i]); }
stl_normalize_vector(normal);
stl->facet_start[i].normal = normal;
}
} }
void static inline void rotate_point_2d(float &x, float &y, const double c, const double s)
stl_rotate_x(stl_file *stl, float angle) { {
int i; double xold = x;
int j; double yold = y;
double radian_angle = (angle / 180.0) * M_PI; x = float(c * xold - s * yold);
double c = cos(radian_angle); y = float(s * xold + c * yold);
double s = sin(radian_angle);
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j](1),
&stl->facet_start[i].vertex[j](2), c, s);
}
}
stl_get_size(stl);
calculate_normals(stl);
} }
void void stl_rotate_x(stl_file *stl, float angle)
stl_rotate_y(stl_file *stl, float angle) { {
int i; double radian_angle = (angle / 180.0) * M_PI;
int j; double c = cos(radian_angle);
double radian_angle = (angle / 180.0) * M_PI; double s = sin(radian_angle);
double c = cos(radian_angle); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
double s = sin(radian_angle); for (int j = 0; j < 3; ++ j)
rotate_point_2d(stl->facet_start[i].vertex[j](1), stl->facet_start[i].vertex[j](2), c, s);
if (stl->error) return; stl_get_size(stl);
calculate_normals(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j](2),
&stl->facet_start[i].vertex[j](0), c, s);
}
}
stl_get_size(stl);
calculate_normals(stl);
} }
void void stl_rotate_y(stl_file *stl, float angle)
stl_rotate_z(stl_file *stl, float angle) { {
int i; double radian_angle = (angle / 180.0) * M_PI;
int j; double c = cos(radian_angle);
double radian_angle = (angle / 180.0) * M_PI; double s = sin(radian_angle);
double c = cos(radian_angle); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
double s = sin(radian_angle); for (int j = 0; j < 3; ++ j)
rotate_point_2d(stl->facet_start[i].vertex[j](2), stl->facet_start[i].vertex[j](0), c, s);
if (stl->error) return; stl_get_size(stl);
calculate_normals(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j](0),
&stl->facet_start[i].vertex[j](1), c, s);
}
}
stl_get_size(stl);
calculate_normals(stl);
} }
void stl_rotate_z(stl_file *stl, float angle)
{
double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle);
double s = sin(radian_angle);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
rotate_point_2d(stl->facet_start[i].vertex[j](0), stl->facet_start[i].vertex[j](1), c, s);
stl_get_size(stl);
calculate_normals(stl);
}
void its_rotate_x(indexed_triangle_set &its, float angle)
{
double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle);
double s = sin(radian_angle);
for (stl_vertex &v : its.vertices)
rotate_point_2d(v(1), v(2), c, s);
}
static void void its_rotate_y(indexed_triangle_set& its, float angle)
stl_rotate(float *x, float *y, const double c, const double s) { {
double xold = *x; double radian_angle = (angle / 180.0) * M_PI;
double yold = *y; double c = cos(radian_angle);
*x = float(c * xold - s * yold); double s = sin(radian_angle);
*y = float(s * xold + c * yold); for (stl_vertex& v : its.vertices)
rotate_point_2d(v(2), v(0), c, s);
}
void its_rotate_z(indexed_triangle_set& its, float angle)
{
double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle);
double s = sin(radian_angle);
for (stl_vertex& v : its.vertices)
rotate_point_2d(v(0), v(1), c, s);
} }
void stl_get_size(stl_file *stl) void stl_get_size(stl_file *stl)
{ {
if (stl->error || stl->stats.number_of_facets == 0) if (stl->stats.number_of_facets == 0)
return; return;
stl->stats.min = stl->facet_start[0].vertex[0]; stl->stats.min = stl->facet_start[0].vertex[0];
stl->stats.max = stl->stats.min; stl->stats.max = stl->stats.min;
for (int i = 0; i < stl->stats.number_of_facets; ++ i) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
const stl_facet &face = stl->facet_start[i]; const stl_facet &face = stl->facet_start[i];
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < 3; ++ j) {
stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]); stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]);
stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]); stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]);
} }
} }
stl->stats.size = stl->stats.max - stl->stats.min; stl->stats.size = stl->stats.max - stl->stats.min;
stl->stats.bounding_diameter = stl->stats.size.norm(); stl->stats.bounding_diameter = stl->stats.size.norm();
} }
void stl_mirror_xy(stl_file *stl) void stl_mirror_xy(stl_file *stl)
{ {
if (stl->error) for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
return; for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j](2) *= -1.0;
for(int i = 0; i < stl->stats.number_of_facets; i++) { float temp_size = stl->stats.min(2);
for(int j = 0; j < 3; j++) { stl->stats.min(2) = stl->stats.max(2);
stl->facet_start[i].vertex[j](2) *= -1.0; stl->stats.max(2) = temp_size;
} stl->stats.min(2) *= -1.0;
} stl->stats.max(2) *= -1.0;
float temp_size = stl->stats.min(2); stl_reverse_all_facets(stl);
stl->stats.min(2) = stl->stats.max(2); stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
stl->stats.max(2) = temp_size;
stl->stats.min(2) *= -1.0;
stl->stats.max(2) *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
} }
void stl_mirror_yz(stl_file *stl) void stl_mirror_yz(stl_file *stl)
{ {
if (stl->error) return; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; j++)
for (int i = 0; i < stl->stats.number_of_facets; i++) { stl->facet_start[i].vertex[j](0) *= -1.0;
for (int j = 0; j < 3; j++) { float temp_size = stl->stats.min(0);
stl->facet_start[i].vertex[j](0) *= -1.0; stl->stats.min(0) = stl->stats.max(0);
} stl->stats.max(0) = temp_size;
} stl->stats.min(0) *= -1.0;
float temp_size = stl->stats.min(0); stl->stats.max(0) *= -1.0;
stl->stats.min(0) = stl->stats.max(0); stl_reverse_all_facets(stl);
stl->stats.max(0) = temp_size; stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
stl->stats.min(0) *= -1.0;
stl->stats.max(0) *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
} }
void stl_mirror_xz(stl_file *stl) void stl_mirror_xz(stl_file *stl)
{ {
if (stl->error) for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
return; for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j](1) *= -1.0;
for (int i = 0; i < stl->stats.number_of_facets; i++) { float temp_size = stl->stats.min(1);
for (int j = 0; j < 3; j++) { stl->stats.min(1) = stl->stats.max(1);
stl->facet_start[i].vertex[j](1) *= -1.0; stl->stats.max(1) = temp_size;
} stl->stats.min(1) *= -1.0;
} stl->stats.max(1) *= -1.0;
float temp_size = stl->stats.min(1); stl_reverse_all_facets(stl);
stl->stats.min(1) = stl->stats.max(1); stl->stats.facets_reversed -= stl->stats.number_of_facets; // for not altering stats
stl->stats.max(1) = temp_size;
stl->stats.min(1) *= -1.0;
stl->stats.max(1) *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
}
static float get_volume(stl_file *stl)
{
if (stl->error)
return 0;
// Choose a point, any point as the reference.
stl_vertex p0 = stl->facet_start[0].vertex[0];
float volume = 0.f;
for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
// Do dot product to get distance from point to plane.
float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
float area = get_area(&stl->facet_start[i]);
volume += (area * height) / 3.0f;
}
return volume;
}
void stl_calculate_volume(stl_file *stl)
{
if (stl->error) return;
stl->stats.volume = get_volume(stl);
if(stl->stats.volume < 0.0) {
stl_reverse_all_facets(stl);
stl->stats.volume = -stl->stats.volume;
}
} }
static float get_area(stl_facet *facet) static float get_area(stl_facet *facet)
{ {
/* cast to double before calculating cross product because large coordinates /* cast to double before calculating cross product because large coordinates
can result in overflowing product can result in overflowing product
(bad area is responsible for bad volume and bad facets reversal) */ (bad area is responsible for bad volume and bad facets reversal) */
double cross[3][3]; double cross[3][3];
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) - cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) -
((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1))); ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1)));
cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) - cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) -
((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2))); ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2)));
cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) - cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) -
((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0))); ((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0)));
} }
stl_normal sum; stl_normal sum;
sum(0) = cross[0][0] + cross[1][0] + cross[2][0]; sum(0) = cross[0][0] + cross[1][0] + cross[2][0];
sum(1) = cross[0][1] + cross[1][1] + cross[2][1]; sum(1) = cross[0][1] + cross[1][1] + cross[2][1];
sum(2) = cross[0][2] + cross[1][2] + cross[2][2]; sum(2) = cross[0][2] + cross[1][2] + cross[2][2];
// This should already be done. But just in case, let's do it again. // This should already be done. But just in case, let's do it again.
//FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy. //FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy.
stl_normal n; stl_normal n;
stl_calculate_normal(n, facet); stl_calculate_normal(n, facet);
stl_normalize_vector(n); stl_normalize_vector(n);
return 0.5f * n.dot(sum); return 0.5f * n.dot(sum);
} }
void stl_repair(stl_file *stl, static float get_volume(stl_file *stl)
int fixall_flag, {
int exact_flag, // Choose a point, any point as the reference.
int tolerance_flag, stl_vertex p0 = stl->facet_start[0].vertex[0];
float tolerance, float volume = 0.f;
int increment_flag, for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
float increment, // Do dot product to get distance from point to plane.
int nearby_flag, float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
int iterations, float area = get_area(&stl->facet_start[i]);
int remove_unconnected_flag, volume += (area * height) / 3.0f;
int fill_holes_flag, }
int normal_directions_flag, return volume;
int normal_values_flag, }
int reverse_all_flag,
int verbose_flag) { void stl_calculate_volume(stl_file *stl)
{
int i; stl->stats.volume = get_volume(stl);
int last_edges_fixed = 0; if (stl->stats.volume < 0.0) {
stl_reverse_all_facets(stl);
if (stl->error) return; stl->stats.volume = -stl->stats.volume;
}
if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag }
|| fill_holes_flag || normal_directions_flag) {
if (verbose_flag) void stl_repair(
printf("Checking exact...\n"); stl_file *stl,
exact_flag = 1; bool fixall_flag,
stl_check_facets_exact(stl); bool exact_flag,
stl->stats.facets_w_1_bad_edge = bool tolerance_flag,
(stl->stats.connected_facets_2_edge - float tolerance,
stl->stats.connected_facets_3_edge); bool increment_flag,
stl->stats.facets_w_2_bad_edge = float increment,
(stl->stats.connected_facets_1_edge - bool nearby_flag,
stl->stats.connected_facets_2_edge); int iterations,
stl->stats.facets_w_3_bad_edge = bool remove_unconnected_flag,
(stl->stats.number_of_facets - bool fill_holes_flag,
stl->stats.connected_facets_1_edge); bool normal_directions_flag,
} bool normal_values_flag,
bool reverse_all_flag,
if(nearby_flag || fixall_flag) { bool verbose_flag)
if(!tolerance_flag) { {
tolerance = stl->stats.shortest_edge; if (exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) {
} if (verbose_flag)
if(!increment_flag) { printf("Checking exact...\n");
increment = stl->stats.bounding_diameter / 10000.0; exact_flag = true;
} stl_check_facets_exact(stl);
stl->stats.facets_w_1_bad_edge = (stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge);
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { stl->stats.facets_w_2_bad_edge = (stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge);
for(i = 0; i < iterations; i++) { stl->stats.facets_w_3_bad_edge = (stl->stats.number_of_facets - stl->stats.connected_facets_1_edge);
if(stl->stats.connected_facets_3_edge < }
stl->stats.number_of_facets) {
if (verbose_flag) if (nearby_flag || fixall_flag) {
printf("\ if (! tolerance_flag)
Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance = stl->stats.shortest_edge;
tolerance, i + 1, iterations); if (! increment_flag)
stl_check_facets_nearby(stl, tolerance); increment = stl->stats.bounding_diameter / 10000.0;
if (verbose_flag) }
printf(" Fixed %d edges.\n",
stl->stats.edges_fixed - last_edges_fixed); if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
last_edges_fixed = stl->stats.edges_fixed; int last_edges_fixed = 0;
tolerance += increment; for (int i = 0; i < iterations; ++ i) {
} else { if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag) if (verbose_flag)
printf("\ printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
All facets connected. No further nearby check necessary.\n"); stl_check_facets_nearby(stl, tolerance);
break; if (verbose_flag)
} printf(" Fixed %d edges.\n", stl->stats.edges_fixed - last_edges_fixed);
} last_edges_fixed = stl->stats.edges_fixed;
} else { tolerance += increment;
if (verbose_flag) } else {
printf("All facets connected. No nearby check necessary.\n"); if (verbose_flag)
} printf("All facets connected. No further nearby check necessary.\n");
} break;
}
if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { }
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { } else if (verbose_flag)
if (verbose_flag) printf("All facets connected. No nearby check necessary.\n");
printf("Removing unconnected facets...\n");
stl_remove_unconnected_facets(stl); if (remove_unconnected_flag || fixall_flag || fill_holes_flag) {
} else if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag) if (verbose_flag)
printf("No unconnected need to be removed.\n"); printf("Removing unconnected facets...\n");
} stl_remove_unconnected_facets(stl);
} else if (verbose_flag)
if(fill_holes_flag || fixall_flag) { printf("No unconnected need to be removed.\n");
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { }
if (verbose_flag)
printf("Filling holes...\n"); if (fill_holes_flag || fixall_flag) {
stl_fill_holes(stl); if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
} else if (verbose_flag)
if (verbose_flag) printf("Filling holes...\n");
printf("No holes need to be filled.\n"); stl_fill_holes(stl);
} } else if (verbose_flag)
printf("No holes need to be filled.\n");
if(reverse_all_flag) { }
if (verbose_flag)
printf("Reversing all facets...\n"); if (reverse_all_flag) {
stl_reverse_all_facets(stl); if (verbose_flag)
} printf("Reversing all facets...\n");
stl_reverse_all_facets(stl);
if(normal_directions_flag || fixall_flag) { }
if (verbose_flag)
printf("Checking normal directions...\n"); if (normal_directions_flag || fixall_flag) {
stl_fix_normal_directions(stl); if (verbose_flag)
} printf("Checking normal directions...\n");
stl_fix_normal_directions(stl);
if(normal_values_flag || fixall_flag) { }
if (verbose_flag)
printf("Checking normal values...\n"); if (normal_values_flag || fixall_flag) {
stl_fix_normal_values(stl); if (verbose_flag)
} printf("Checking normal values...\n");
stl_fix_normal_values(stl);
/* Always calculate the volume. It shouldn't take too long */ }
if (verbose_flag)
printf("Calculating volume...\n"); // Always calculate the volume. It shouldn't take too long.
stl_calculate_volume(stl); if (verbose_flag)
printf("Calculating volume...\n");
if(exact_flag) { stl_calculate_volume(stl);
if (verbose_flag)
printf("Verifying neighbors...\n"); if (exact_flag) {
stl_verify_neighbors(stl); if (verbose_flag)
} printf("Verifying neighbors...\n");
stl_verify_neighbors(stl);
}
} }

View file

@ -15,7 +15,7 @@
#include "FillRectilinear3.hpp" #include "FillRectilinear3.hpp"
#define SLIC3R_DEBUG // #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG

View file

@ -1489,10 +1489,10 @@ namespace Slic3r {
} }
// splits volume out of imported geometry // splits volume out of imported geometry
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; TriangleMesh triangle_mesh;
ModelVolume* volume = object.add_volume(TriangleMesh()); stl_file &stl = triangle_mesh.stl;
stl_file& stl = volume->mesh.stl; unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
stl.stats.type = inmemory; stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)triangles_count; stl.stats.number_of_facets = (uint32_t)triangles_count;
stl.stats.original_num_facets = (int)stl.stats.number_of_facets; stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
stl_allocate(&stl); stl_allocate(&stl);
@ -1509,9 +1509,11 @@ namespace Slic3r {
} }
} }
stl_get_size(&stl); stl_get_size(&stl);
volume->mesh.repair(); triangle_mesh.repair();
volume->center_geometry();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
volume->center_geometry_after_creation();
volume->calculate_convex_hull(); volume->calculate_convex_hull();
// apply volume's name and config data // apply volume's name and config data
@ -1879,29 +1881,28 @@ namespace Slic3r {
if (volume == nullptr) if (volume == nullptr)
continue; continue;
if (!volume->mesh().repaired)
throw std::runtime_error("store_3mf() requires repair()");
if (!volume->mesh().has_shared_vertices())
throw std::runtime_error("store_3mf() requires shared vertices");
volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first;
if (!volume->mesh.repaired) const indexed_triangle_set &its = volume->mesh().its;
volume->mesh.repair(); if (its.vertices.empty())
stl_file& stl = volume->mesh.stl;
if (stl.v_shared == nullptr)
stl_generate_shared_vertices(&stl);
if (stl.stats.shared_vertices == 0)
{ {
add_error("Found invalid mesh"); add_error("Found invalid mesh");
return false; return false;
} }
vertices_count += stl.stats.shared_vertices; vertices_count += its.vertices.size();
const Transform3d& matrix = volume->get_matrix(); const Transform3d& matrix = volume->get_matrix();
for (int i = 0; i < stl.stats.shared_vertices; ++i) for (size_t i = 0; i < its.vertices.size(); ++i)
{ {
stream << " <" << VERTEX_TAG << " "; stream << " <" << VERTEX_TAG << " ";
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>(); Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
stream << "x=\"" << v(0) << "\" "; stream << "x=\"" << v(0) << "\" ";
stream << "y=\"" << v(1) << "\" "; stream << "y=\"" << v(1) << "\" ";
stream << "z=\"" << v(2) << "\" />\n"; stream << "z=\"" << v(2) << "\" />\n";
@ -1920,19 +1921,19 @@ namespace Slic3r {
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume); VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end()); assert(volume_it != volumes_offsets.end());
stl_file& stl = volume->mesh.stl; const indexed_triangle_set &its = volume->mesh().its;
// updates triangle offsets // updates triangle offsets
volume_it->second.first_triangle_id = triangles_count; volume_it->second.first_triangle_id = triangles_count;
triangles_count += stl.stats.number_of_facets; triangles_count += its.indices.size();
volume_it->second.last_triangle_id = triangles_count - 1; volume_it->second.last_triangle_id = triangles_count - 1;
for (uint32_t i = 0; i < stl.stats.number_of_facets; ++i) for (size_t i = 0; i < its.indices.size(); ++ i)
{ {
stream << " <" << TRIANGLE_TAG << " "; stream << " <" << TRIANGLE_TAG << " ";
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
{ {
stream << "v" << j + 1 << "=\"" << stl.v_indices[i].vertex[j] + volume_it->second.first_vertex_id << "\" "; stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
} }
stream << "/>\n"; stream << "/>\n";
} }

View file

@ -522,7 +522,8 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME: case NODE_TYPE_VOLUME:
{ {
assert(m_object && m_volume); assert(m_object && m_volume);
stl_file &stl = m_volume->mesh.stl; TriangleMesh mesh;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory; stl.stats.type = inmemory;
stl.stats.number_of_facets = int(m_volume_facets.size() / 3); stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
stl.stats.original_num_facets = stl.stats.number_of_facets; stl.stats.original_num_facets = stl.stats.number_of_facets;
@ -533,8 +534,9 @@ void AMFParserContext::endElement(const char * /* name */)
memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
} }
stl_get_size(&stl); stl_get_size(&stl);
m_volume->mesh.repair(); mesh.repair();
m_volume->center_geometry(); m_volume->set_mesh(std::move(mesh));
m_volume->center_geometry_after_creation();
m_volume->calculate_convex_hull(); m_volume->calculate_convex_hull();
m_volume_facets.clear(); m_volume_facets.clear();
m_volume = nullptr; m_volume = nullptr;
@ -923,23 +925,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
int num_vertices = 0; int num_vertices = 0;
for (ModelVolume *volume : object->volumes) { for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices); vertices_offsets.push_back(num_vertices);
if (! volume->mesh.repaired) if (! volume->mesh().repaired)
throw std::runtime_error("store_amf() requires repair()"); throw std::runtime_error("store_amf() requires repair()");
auto &stl = volume->mesh.stl; if (! volume->mesh().has_shared_vertices())
if (stl.v_shared == nullptr) throw std::runtime_error("store_amf() requires shared vertices");
stl_generate_shared_vertices(&stl); const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix(); const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) { for (size_t i = 0; i < its.vertices.size(); ++i) {
stream << " <vertex>\n"; stream << " <vertex>\n";
stream << " <coordinates>\n"; stream << " <coordinates>\n";
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>(); Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
stream << " <x>" << v(0) << "</x>\n"; stream << " <x>" << v(0) << "</x>\n";
stream << " <y>" << v(1) << "</y>\n"; stream << " <y>" << v(1) << "</y>\n";
stream << " <z>" << v(2) << "</z>\n"; stream << " <z>" << v(2) << "</z>\n";
stream << " </coordinates>\n"; stream << " </coordinates>\n";
stream << " </vertex>\n"; stream << " </vertex>\n";
} }
num_vertices += stl.stats.shared_vertices; num_vertices += its.vertices.size();
} }
stream << " </vertices>\n"; stream << " </vertices>\n";
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) { for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
@ -956,10 +958,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
if (volume->is_modifier()) if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n"; stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n"; stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (int i = 0; i < (int)volume->mesh.stl.stats.number_of_facets; ++i) { const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < (int)its.indices.size(); ++i) {
stream << " <triangle>\n"; stream << " <triangle>\n";
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
stream << " <v" << j + 1 << ">" << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "</v" << j + 1 << ">\n"; stream << " <v" << j + 1 << ">" << its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n";
stream << " </triangle>\n"; stream << " </triangle>\n";
} }
stream << " </volume>\n"; stream << " </volume>\n";

View file

@ -161,16 +161,15 @@ static void extract_model_from_archive(
else { else {
// Header has been extracted. Now read the faces. // Header has been extracted. Now read the faces.
stl_file &stl = mesh.stl; stl_file &stl = mesh.stl;
stl.error = 0;
stl.stats.type = inmemory; stl.stats.type = inmemory;
stl.stats.number_of_facets = header.nTriangles; stl.stats.number_of_facets = header.nTriangles;
stl.stats.original_num_facets = header.nTriangles; stl.stats.original_num_facets = header.nTriangles;
stl_allocate(&stl); stl_allocate(&stl);
if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) { if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
memcpy((char*)stl.facet_start, data.data() + sizeof(StlHeader), 50 * header.nTriangles); memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
if (sizeof(stl_facet) > SIZEOF_STL_FACET) { if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets. // The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
unsigned char *data = (unsigned char*)stl.facet_start; unsigned char *data = (unsigned char*)stl.facet_start.data();
for (size_t i = header.nTriangles - 1; i > 0; -- i) for (size_t i = header.nTriangles - 1; i > 0; -- i)
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET); memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
} }
@ -257,7 +256,7 @@ static void extract_model_from_archive(
stl.stats.number_of_facets = (uint32_t)facets.size(); stl.stats.number_of_facets = (uint32_t)facets.size();
stl.stats.original_num_facets = (int)facets.size(); stl.stats.original_num_facets = (int)facets.size();
stl_allocate(&stl); stl_allocate(&stl);
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
stl_get_size(&stl); stl_get_size(&stl);
mesh.repair(); mesh.repair();
// Add a mesh to a model. // Add a mesh to a model.

View file

@ -17,8 +17,7 @@ namespace Slic3r {
bool load_stl(const char *path, Model *model, const char *object_name_in) bool load_stl(const char *path, Model *model, const char *object_name_in)
{ {
TriangleMesh mesh; TriangleMesh mesh;
mesh.ReadSTLFile(path); if (! mesh.ReadSTLFile(path)) {
if (mesh.stl.error) {
// die "Failed to open $file\n" if !-e $path; // die "Failed to open $file\n" if !-e $path;
return false; return false;
} }

View file

@ -160,12 +160,6 @@ Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig
return model; return model;
} }
void Model::repair()
{
for (ModelObject *o : this->objects)
o->repair();
}
ModelObject* Model::add_object() ModelObject* Model::add_object()
{ {
this->objects.emplace_back(new ModelObject(this)); this->objects.emplace_back(new ModelObject(this));
@ -472,7 +466,7 @@ bool Model::looks_like_multipart_object() const
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1) if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
return false; return false;
for (const ModelVolume *vol : obj->volumes) { for (const ModelVolume *vol : obj->volumes) {
double zmin_this = vol->mesh.bounding_box().min(2); double zmin_this = vol->mesh().bounding_box().min(2);
if (zmin == std::numeric_limits<double>::max()) if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this; zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON) else if (std::abs(zmin - zmin_this) > EPSILON)
@ -679,7 +673,7 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
{ {
ModelVolume* v = new ModelVolume(this, mesh); ModelVolume* v = new ModelVolume(this, mesh);
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -688,7 +682,7 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
{ {
ModelVolume* v = new ModelVolume(this, std::move(mesh)); ModelVolume* v = new ModelVolume(this, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -697,8 +691,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
{ {
ModelVolume* v = new ModelVolume(this, other); ModelVolume* v = new ModelVolume(this, other);
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
this->invalidate_bounding_box(); // v->center_geometry_after_creation();
// this->invalidate_bounding_box();
return v; return v;
} }
@ -706,7 +701,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
{ {
ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -827,7 +822,7 @@ TriangleMesh ModelObject::raw_mesh() const
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
{ {
TriangleMesh vol_mesh(v->mesh); TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix()); vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
} }
@ -840,7 +835,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
TriangleMesh mesh; TriangleMesh mesh;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{ {
TriangleMesh vol_mesh(v->mesh); TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix()); vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
} }
@ -854,7 +849,7 @@ const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
m_raw_mesh_bounding_box.reset(); m_raw_mesh_bounding_box.reset();
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix())); m_raw_mesh_bounding_box.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
} }
return m_raw_mesh_bounding_box; return m_raw_mesh_bounding_box;
} }
@ -863,7 +858,7 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
bb.merge(v->mesh.transformed_bounding_box(v->get_matrix())); bb.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
return bb; return bb;
} }
@ -881,7 +876,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{ {
if (v->is_model_part()) if (v->is_model_part())
m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
} }
} }
return m_raw_bounding_box; return m_raw_bounding_box;
@ -895,7 +890,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{ {
if (v->is_model_part()) if (v->is_model_part())
bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
} }
return bb; return bb;
} }
@ -908,21 +903,20 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
Points pts; Points pts;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) { if (v->is_model_part()) {
const stl_file &stl = v->mesh.stl;
Transform3d trafo = trafo_instance * v->get_matrix(); Transform3d trafo = trafo_instance * v->get_matrix();
if (stl.v_shared == nullptr) { const indexed_triangle_set &its = v->mesh().its;
if (its.vertices.empty()) {
// Using the STL faces. // Using the STL faces.
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) { const stl_file& stl = v->mesh().stl;
const stl_facet &facet = stl.facet_start[i]; for (const stl_facet &facet : stl.facet_start)
for (size_t j = 0; j < 3; ++ j) { for (size_t j = 0; j < 3; ++ j) {
Vec3d p = trafo * facet.vertex[j].cast<double>(); Vec3d p = trafo * facet.vertex[j].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
} }
}
} else { } else {
// Using the shared vertices should be a bit quicker than using the STL faces. // Using the shared vertices should be a bit quicker than using the STL faces.
for (int i = 0; i < stl.stats.shared_vertices; ++ i) { for (size_t i = 0; i < its.vertices.size(); ++ i) {
Vec3d p = trafo * stl.v_shared[i].cast<double>(); Vec3d p = trafo * its.vertices[i].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
} }
} }
@ -1039,6 +1033,7 @@ void ModelObject::mirror(Axis axis)
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh(const Vec3d &versor) void ModelObject::scale_mesh(const Vec3d &versor)
{ {
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
@ -1062,14 +1057,14 @@ size_t ModelObject::facets_count() const
size_t num = 0; size_t num = 0;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets; num += v->mesh().stl.stats.number_of_facets;
return num; return num;
} }
bool ModelObject::needed_repair() const bool ModelObject::needed_repair() const
{ {
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part() && v->mesh.needed_repair()) if (v->is_model_part() && v->mesh().needed_repair())
return true; return true;
return false; return false;
} }
@ -1135,11 +1130,12 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
// Transform the mesh by the combined transformation matrix. // Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed. // Flip the triangles in case the composite transformation is left handed.
volume->mesh.transform(instance_matrix * volume_matrix, true); TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Perform cut // Perform cut
volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this TriangleMeshSlicer tms(&mesh);
TriangleMeshSlicer tms(&volume->mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh); tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset // Reset volume transformation except for offset
@ -1158,14 +1154,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper && upper_mesh.facets_count() > 0) { if (keep_upper && upper_mesh.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(upper_mesh); ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name; vol->name = volume->name;
vol->config = volume->config; vol->config = volume->config;
vol->set_material(volume->material_id(), *volume->material()); vol->set_material(volume->material_id(), *volume->material());
} }
if (keep_lower && lower_mesh.facets_count() > 0) { if (keep_lower && lower_mesh.facets_count() > 0) {
ModelVolume* vol = lower->add_volume(lower_mesh); ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name; vol->name = volume->name;
vol->config = volume->config; vol->config = volume->config;
vol->set_material(volume->material_id(), *volume->material()); vol->set_material(volume->material_id(), *volume->material());
// Compute the lower part instances' bounding boxes to figure out where to place // Compute the lower part instances' bounding boxes to figure out where to place
@ -1233,7 +1229,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
} }
ModelVolume* volume = this->volumes.front(); ModelVolume* volume = this->volumes.front();
TriangleMeshPtrs meshptrs = volume->mesh.split(); TriangleMeshPtrs meshptrs = volume->mesh().split();
for (TriangleMesh *mesh : meshptrs) { for (TriangleMesh *mesh : meshptrs) {
mesh->repair(); mesh->repair();
@ -1260,12 +1256,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return; return;
} }
void ModelObject::repair()
{
for (ModelVolume *v : this->volumes)
v->mesh.repair();
}
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
// This situation is solved by baking in the instance transformation into the mesh vertices. // This situation is solved by baking in the instance transformation into the mesh vertices.
@ -1295,8 +1285,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
// Adjust the meshes. // Adjust the meshes.
// Transformation to be applied to the meshes. // Transformation to be applied to the meshes.
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0); Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix(); Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
for (ModelVolume *model_volume : this->volumes) { for (ModelVolume *model_volume : this->volumes) {
const Geometry::Transformation volume_trafo = model_volume->get_transformation(); const Geometry::Transformation volume_trafo = model_volume->get_transformation();
bool volume_left_handed = volume_trafo.is_left_handed(); bool volume_left_handed = volume_trafo.is_left_handed();
@ -1306,7 +1296,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.; double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.;
// Transform the mesh. // Transform the mesh.
Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0); Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0);
model_volume->transform_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed); // Following method creates a new shared_ptr<TriangleMesh>
model_volume->transform_this_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
// Reset the rotation, scaling and mirroring. // Reset the rotation, scaling and mirroring.
model_volume->set_rotation(Vec3d(0., 0., 0.)); model_volume->set_rotation(Vec3d(0., 0., 0.));
model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor)); model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor));
@ -1347,13 +1338,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
Transform3d mv = mi * v->get_matrix(); Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull(); const TriangleMesh& hull = v->get_convex_hull();
for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) for (const stl_facet &facet : hull.stl.facet_start)
{ for (int i = 0; i < 3; ++ i)
const stl_facet* facet = hull.stl.facet_start + f; min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>()));
}
} }
return min_z + inst->get_offset(Z); return min_z + inst->get_offset(Z);
@ -1452,7 +1439,7 @@ std::string ModelObject::get_export_filename() const
stl_stats ModelObject::get_object_stl_stats() const stl_stats ModelObject::get_object_stl_stats() const
{ {
if (this->volumes.size() == 1) if (this->volumes.size() == 1)
return this->volumes[0]->mesh.stl.stats; return this->volumes[0]->mesh().stl.stats;
stl_stats full_stats; stl_stats full_stats;
memset(&full_stats, 0, sizeof(stl_stats)); memset(&full_stats, 0, sizeof(stl_stats));
@ -1463,7 +1450,7 @@ stl_stats ModelObject::get_object_stl_stats() const
if (volume->id() == this->volumes[0]->id()) if (volume->id() == this->volumes[0]->id())
continue; continue;
const stl_stats& stats = volume->mesh.stl.stats; const stl_stats& stats = volume->mesh().stl.stats;
// initialize full_stats (for repaired errors) // initialize full_stats (for repaired errors)
full_stats.degenerate_facets += stats.degenerate_facets; full_stats.degenerate_facets += stats.degenerate_facets;
@ -1531,30 +1518,30 @@ bool ModelVolume::is_splittable() const
{ {
// the call mesh.is_splittable() is expensive, so cache the value to calculate it only once // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
if (m_is_splittable == -1) if (m_is_splittable == -1)
m_is_splittable = (int)mesh.is_splittable(); m_is_splittable = (int)this->mesh().is_splittable();
return m_is_splittable == 1; return m_is_splittable == 1;
} }
void ModelVolume::center_geometry() void ModelVolume::center_geometry_after_creation()
{ {
Vec3d shift = mesh.bounding_box().center(); Vec3d shift = this->mesh().bounding_box().center();
if (!shift.isApprox(Vec3d::Zero())) if (!shift.isApprox(Vec3d::Zero()))
{ {
mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift); translate(shift);
} }
} }
void ModelVolume::calculate_convex_hull() void ModelVolume::calculate_convex_hull()
{ {
m_convex_hull = mesh.convex_hull_3d(); m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
} }
int ModelVolume::get_mesh_errors_count() const int ModelVolume::get_mesh_errors_count() const
{ {
const stl_stats& stats = this->mesh.stl.stats; const stl_stats& stats = this->mesh().stl.stats;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges; stats.facets_added + stats.facets_reversed + stats.backwards_edges;
@ -1562,7 +1549,7 @@ int ModelVolume::get_mesh_errors_count() const
const TriangleMesh& ModelVolume::get_convex_hull() const const TriangleMesh& ModelVolume::get_convex_hull() const
{ {
return m_convex_hull; return *m_convex_hull.get();
} }
ModelVolumeType ModelVolume::type_from_string(const std::string &s) ModelVolumeType ModelVolume::type_from_string(const std::string &s)
@ -1602,7 +1589,7 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object. // This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders) size_t ModelVolume::split(unsigned int max_extruders)
{ {
TriangleMeshPtrs meshptrs = this->mesh.split(); TriangleMeshPtrs meshptrs = this->mesh().split();
if (meshptrs.size() <= 1) { if (meshptrs.size() <= 1) {
delete meshptrs.front(); delete meshptrs.front();
return 1; return 1;
@ -1619,7 +1606,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
mesh->repair(); mesh->repair();
if (idx == 0) if (idx == 0)
{ {
this->mesh = std::move(*mesh); this->set_mesh(std::move(*mesh));
this->calculate_convex_hull(); this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated. // Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id(); this->set_new_unique_id();
@ -1628,7 +1615,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
this->object->volumes[ivolume]->center_geometry(); this->object->volumes[ivolume]->center_geometry_after_creation();
this->object->volumes[ivolume]->translate(offset); this->object->volumes[ivolume]->translate(offset);
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders));
@ -1694,24 +1681,33 @@ void ModelVolume::mirror(Axis axis)
set_mirror(mirror); set_mirror(mirror);
} }
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry(const Vec3d& versor) void ModelVolume::scale_geometry(const Vec3d& versor)
{ {
mesh.scale(versor); m_mesh->scale(versor);
m_convex_hull.scale(versor); m_convex_hull->scale(versor);
} }
void ModelVolume::transform_mesh(const Transform3d &mesh_trafo, bool fix_left_handed) void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
{ {
this->mesh.transform(mesh_trafo, fix_left_handed); TriangleMesh mesh = this->mesh();
this->m_convex_hull.transform(mesh_trafo, fix_left_handed); mesh.transform(mesh_trafo, fix_left_handed);
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(mesh_trafo, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }
void ModelVolume::transform_mesh(const Matrix3d &matrix, bool fix_left_handed) void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_handed)
{ {
this->mesh.transform(matrix, fix_left_handed); TriangleMesh mesh = this->mesh();
this->m_convex_hull.transform(matrix, fix_left_handed); mesh.transform(matrix, fix_left_handed);
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(matrix, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }

View file

@ -7,7 +7,9 @@
#include "Point.hpp" #include "Point.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -261,6 +263,7 @@ public:
void rotate(double angle, const Vec3d& axis); void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_mesh(const Vec3d& versor); void scale_mesh(const Vec3d& versor);
size_t materials_count() const; size_t materials_count() const;
@ -268,7 +271,6 @@ public:
bool needed_repair() const; bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
void split(ModelObjectPtrs* new_objects); void split(ModelObjectPtrs* new_objects);
void repair();
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
// This situation is solved by baking in the instance transformation into the mesh vertices. // This situation is solved by baking in the instance transformation into the mesh vertices.
@ -340,7 +342,12 @@ class ModelVolume : public ModelBase
public: public:
std::string name; std::string name;
// The triangular model. // The triangular model.
TriangleMesh mesh; const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
// Configuration parameters specific to an object model geometry or a modifier volume, // Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings. // overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config; DynamicPrintConfig config;
@ -377,13 +384,16 @@ public:
void rotate(double angle, const Vec3d& axis); void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_geometry(const Vec3d& versor); void scale_geometry(const Vec3d& versor);
// translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box // Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
void center_geometry(); // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
void center_geometry_after_creation();
void calculate_convex_hull(); void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const; const TriangleMesh& get_convex_hull() const;
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
// Get count of errors in the mesh // Get count of errors in the mesh
int get_mesh_errors_count() const; int get_mesh_errors_count() const;
@ -430,18 +440,20 @@ protected:
explicit ModelVolume(const ModelVolume &rhs) = default; explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; } void set_model_object(ModelObject *model_object) { object = model_object; }
void transform_mesh(const Transform3d& t, bool fix_left_handed); void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_mesh(const Matrix3d& m, bool fix_left_handed); void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private: private:
// Parent object owning this ModelVolume. // Parent object owning this ModelVolume.
ModelObject* object; ModelObject* object;
// The triangular model.
std::shared_ptr<TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume? // Is it an object to be printed, or a modifier volume?
ModelVolumeType m_type; ModelVolumeType m_type;
t_model_material_id m_material_id; t_model_material_id m_material_id;
// The convex hull of this model's mesh. // The convex hull of this model's mesh.
TriangleMesh m_convex_hull; std::shared_ptr<TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation; Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable // flag to optimize the checking if the volume is splittable
// -1 -> is unknown value (before first cheking) // -1 -> is unknown value (before first cheking)
@ -449,24 +461,24 @@ private:
// 1 -> is splittable // 1 -> is splittable
mutable int m_is_splittable{ -1 }; mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object) ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
{ {
if (mesh.stl.stats.number_of_facets > 1) if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull(); calculate_convex_hull();
} }
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {} m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
// Copying an existing volume, therefore this volume will get a copy of the ID assigned. // Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) : ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{ {
this->set_material_id(other.material_id()); this->set_material_id(other.material_id());
} }
// Providing a new mesh, therefore this volume will get a new unique ID assigned. // Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{ {
this->set_material_id(other.material_id()); this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1) if (mesh.stl.stats.number_of_facets > 1)
@ -597,10 +609,6 @@ public:
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
// Add a new ModelObject to this Model, generate a new ID for this ModelObject. // Add a new ModelObject to this Model, generate a new ID for this ModelObject.
ModelObject* add_object(); ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);

View file

@ -1797,7 +1797,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
if (! volumes.empty()) { if (! volumes.empty()) {
// Compose mesh. // Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volumes.front()->mesh); TriangleMesh mesh(volumes.front()->mesh());
mesh.transform(volumes.front()->get_matrix(), true); mesh.transform(volumes.front()->get_matrix(), true);
assert(mesh.repaired); assert(mesh.repaired);
if (volumes.size() == 1 && mesh.repaired) { if (volumes.size() == 1 && mesh.repaired) {
@ -1806,7 +1806,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
} }
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) { for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
const ModelVolume &model_volume = *volumes[idx_volume]; const ModelVolume &model_volume = *volumes[idx_volume];
TriangleMesh vol_mesh(model_volume.mesh); TriangleMesh vol_mesh(model_volume.mesh());
vol_mesh.transform(model_volume.get_matrix(), true); vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
} }
@ -1815,10 +1815,11 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
// apply XY shift // apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing // perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this // TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh.require_shared_vertices();
TriangleMeshSlicer mslicer;
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1832,7 +1833,7 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
// Compose mesh. // Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh); TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true); mesh.transform(volume.get_matrix(), true);
if (mesh.repaired) { if (mesh.repaired) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
@ -1846,7 +1847,8 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this // TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();

View file

@ -121,19 +121,10 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
V.resize(3*stl.stats.number_of_facets, 3); V.resize(3*stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
const stl_facet* facet = stl.facet_start+i; const stl_facet &facet = stl.facet_start[i];
V(3*i+0, 0) = double(facet->vertex[0](0)); V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>();
V(3*i+0, 1) = double(facet->vertex[0](1)); V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>();
V(3*i+0, 2) = double(facet->vertex[0](2)); V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>();
V(3*i+1, 0) = double(facet->vertex[1](0));
V(3*i+1, 1) = double(facet->vertex[1](1));
V(3*i+1, 2) = double(facet->vertex[1](2));
V(3*i+2, 0) = double(facet->vertex[2](0));
V(3*i+2, 1) = double(facet->vertex[2](1));
V(3*i+2, 2) = double(facet->vertex[2](2));
F(i, 0) = int(3*i+0); F(i, 0) = int(3*i+0);
F(i, 1) = int(3*i+1); F(i, 1) = int(3*i+1);
F(i, 2) = int(3*i+2); F(i, 2) = int(3*i+2);

View file

@ -227,7 +227,7 @@ std::vector<coordf_t> layer_height_profile_adaptive(
as.set_slicing_parameters(slicing_params); as.set_slicing_parameters(slicing_params);
for (const ModelVolume *volume : volumes) for (const ModelVolume *volume : volumes)
if (volume->is_model_part()) if (volume->is_model_part())
as.add_mesh(&volume->mesh); as.add_mesh(&volume->mesh());
as.prepare(); as.prepare();
// 2) Generate layers using the algorithm of @platsch // 2) Generate layers using the algorithm of @platsch

View file

@ -27,8 +27,8 @@ void SlicingAdaptive::prepare()
nfaces_total += (*it_mesh)->stl.stats.number_of_facets; nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
m_faces.reserve(nfaces_total); m_faces.reserve(nfaces_total);
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) for (const stl_facet &face : (*it_mesh)->stl.facet_start)
m_faces.push_back((*it_mesh)->stl.facet_start + i); m_faces.emplace_back(&face);
// 2) Sort faces lexicographically by their Z span. // 2) Sort faces lexicographically by their Z span.
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {

View file

@ -42,20 +42,17 @@
namespace Slic3r { namespace Slic3r {
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets) TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets) : repaired(false)
: repaired(false)
{ {
stl_initialize(&this->stl);
stl_file &stl = this->stl; stl_file &stl = this->stl;
stl.error = 0;
stl.stats.type = inmemory; stl.stats.type = inmemory;
// count facets and allocate memory // count facets and allocate memory
stl.stats.number_of_facets = facets.size(); stl.stats.number_of_facets = (uint32_t)facets.size();
stl.stats.original_num_facets = stl.stats.number_of_facets; stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl); stl_allocate(&stl);
for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
stl_facet facet; stl_facet facet;
facet.vertex[0] = points[facets[i](0)].cast<float>(); facet.vertex[0] = points[facets[i](0)].cast<float>();
facet.vertex[1] = points[facets[i](1)].cast<float>(); facet.vertex[1] = points[facets[i](1)].cast<float>();
@ -73,57 +70,37 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& f
stl_get_size(&stl); stl_get_size(&stl);
} }
TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
{
stl_close(&this->stl);
this->stl = other.stl;
this->repaired = other.repaired;
this->stl.heads = nullptr;
this->stl.tail = nullptr;
this->stl.error = other.stl.error;
if (other.stl.facet_start != nullptr) {
this->stl.facet_start = (stl_facet*)calloc(other.stl.stats.number_of_facets, sizeof(stl_facet));
std::copy(other.stl.facet_start, other.stl.facet_start + other.stl.stats.number_of_facets, this->stl.facet_start);
}
if (other.stl.neighbors_start != nullptr) {
this->stl.neighbors_start = (stl_neighbors*)calloc(other.stl.stats.number_of_facets, sizeof(stl_neighbors));
std::copy(other.stl.neighbors_start, other.stl.neighbors_start + other.stl.stats.number_of_facets, this->stl.neighbors_start);
}
if (other.stl.v_indices != nullptr) {
this->stl.v_indices = (v_indices_struct*)calloc(other.stl.stats.number_of_facets, sizeof(v_indices_struct));
std::copy(other.stl.v_indices, other.stl.v_indices + other.stl.stats.number_of_facets, this->stl.v_indices);
}
if (other.stl.v_shared != nullptr) {
this->stl.v_shared = (stl_vertex*)calloc(other.stl.stats.shared_vertices, sizeof(stl_vertex));
std::copy(other.stl.v_shared, other.stl.v_shared + other.stl.stats.shared_vertices, this->stl.v_shared);
}
return *this;
}
// #define SLIC3R_TRACE_REPAIR // #define SLIC3R_TRACE_REPAIR
void TriangleMesh::repair() void TriangleMesh::repair(bool update_shared_vertices)
{ {
if (this->repaired) return; if (this->repaired) {
if (update_shared_vertices)
this->require_shared_vertices();
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 */
assert(stl_validate(&this->stl));
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);
// checking nearby // checking nearby
//int last_edges_fixed = 0; //int last_edges_fixed = 0;
float tolerance = stl.stats.shortest_edge; float tolerance = (float)stl.stats.shortest_edge;
float increment = stl.stats.bounding_diameter / 10000.0; float increment = (float)stl.stats.bounding_diameter / 10000.0f;
int iterations = 2; int iterations = 2;
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) {
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
@ -141,6 +118,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 +126,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,28 +147,38 @@ 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;
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished"; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
// This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
// and it is risky to generate such a structure once the meshes are shared. Do it now.
this->its.clear();
if (update_shared_vertices)
this->require_shared_vertices();
} }
float TriangleMesh::volume() float TriangleMesh::volume()
@ -249,20 +238,24 @@ bool TriangleMesh::needed_repair() const
void TriangleMesh::WriteOBJFile(const char* output_file) void TriangleMesh::WriteOBJFile(const char* output_file)
{ {
stl_generate_shared_vertices(&stl); its_write_obj(this->its, output_file);
stl_write_obj(&stl, output_file);
} }
void TriangleMesh::scale(float factor) void TriangleMesh::scale(float factor)
{ {
stl_scale(&(this->stl), factor); stl_scale(&(this->stl), factor);
stl_invalidate_shared_vertices(&this->stl); for (stl_vertex& v : this->its.vertices)
v *= factor;
} }
void TriangleMesh::scale(const Vec3d &versor) void TriangleMesh::scale(const Vec3d &versor)
{ {
stl_scale_versor(&this->stl, versor.cast<float>()); stl_scale_versor(&this->stl, versor.cast<float>());
stl_invalidate_shared_vertices(&this->stl); for (stl_vertex& v : this->its.vertices) {
v.x() *= versor.x();
v.y() *= versor.y();
v.z() *= versor.z();
}
} }
void TriangleMesh::translate(float x, float y, float z) void TriangleMesh::translate(float x, float y, float z)
@ -270,7 +263,9 @@ void TriangleMesh::translate(float x, float y, float z)
if (x == 0.f && y == 0.f && z == 0.f) if (x == 0.f && y == 0.f && z == 0.f)
return; return;
stl_translate_relative(&(this->stl), x, y, z); stl_translate_relative(&(this->stl), x, y, z);
stl_invalidate_shared_vertices(&this->stl); stl_vertex shift(x, y, z);
for (stl_vertex& v : this->its.vertices)
v += shift;
} }
void TriangleMesh::translate(const Vec3f &displacement) void TriangleMesh::translate(const Vec3f &displacement)
@ -287,13 +282,15 @@ void TriangleMesh::rotate(float angle, const Axis &axis)
angle = Slic3r::Geometry::rad2deg(angle); angle = Slic3r::Geometry::rad2deg(angle);
if (axis == X) { if (axis == X) {
stl_rotate_x(&(this->stl), angle); stl_rotate_x(&this->stl, angle);
its_rotate_x(this->its, angle);
} else if (axis == Y) { } else if (axis == Y) {
stl_rotate_y(&(this->stl), angle); stl_rotate_y(&this->stl, angle);
its_rotate_y(this->its, angle);
} else if (axis == Z) { } else if (axis == Z) {
stl_rotate_z(&(this->stl), angle); stl_rotate_z(&this->stl, angle);
its_rotate_z(this->its, angle);
} }
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::rotate(float angle, const Vec3d& axis) void TriangleMesh::rotate(float angle, const Vec3d& axis)
@ -305,39 +302,49 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
Transform3d m = Transform3d::Identity(); Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(angle, axis_norm)); m.rotate(Eigen::AngleAxisd(angle, axis_norm));
stl_transform(&stl, m); stl_transform(&stl, m);
its_transform(its, m);
} }
void TriangleMesh::mirror(const Axis &axis) void TriangleMesh::mirror(const Axis &axis)
{ {
if (axis == X) { if (axis == X) {
stl_mirror_yz(&this->stl); stl_mirror_yz(&this->stl);
for (stl_vertex &v : this->its.vertices)
v(0) *= -1.0;
} else if (axis == Y) { } else if (axis == Y) {
stl_mirror_xz(&this->stl); stl_mirror_xz(&this->stl);
for (stl_vertex &v : this->its.vertices)
v(1) *= -1.0;
} else if (axis == Z) { } else if (axis == Z) {
stl_mirror_xy(&this->stl); stl_mirror_xy(&this->stl);
for (stl_vertex &v : this->its.vertices)
v(2) *= -1.0;
} }
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
{ {
stl_transform(&stl, t); stl_transform(&stl, t);
stl_invalidate_shared_vertices(&stl); its_transform(its, t);
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
this->repair(); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed) void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
{ {
stl_transform(&stl, m); stl_transform(&stl, m);
stl_invalidate_shared_vertices(&stl); its_transform(its, m);
if (fix_left_handed && m.determinant() < 0.) { if (fix_left_handed && m.determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
this->repair(); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
@ -355,7 +362,8 @@ void TriangleMesh::rotate(double angle, Point* center)
return; return;
Vec2f c = center->cast<float>(); Vec2f c = center->cast<float>();
this->translate(-c(0), -c(1), 0); this->translate(-c(0), -c(1), 0);
stl_rotate_z(&(this->stl), (float)angle); stl_rotate_z(&this->stl, (float)angle);
its_rotate_z(this->its, (float)angle);
this->translate(c(0), c(1), 0); this->translate(c(0), c(1), 0);
} }
@ -435,9 +443,8 @@ TriangleMeshPtrs TriangleMesh::split() const
TriangleMesh* mesh = new TriangleMesh; TriangleMesh* mesh = new TriangleMesh;
meshes.emplace_back(mesh); meshes.emplace_back(mesh);
mesh->stl.stats.type = inmemory; mesh->stl.stats.type = inmemory;
mesh->stl.stats.number_of_facets = facets.size(); mesh->stl.stats.number_of_facets = (uint32_t)facets.size();
mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl); stl_allocate(&mesh->stl);
// Assign the facets to the new mesh. // Assign the facets to the new mesh.
@ -455,7 +462,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
{ {
// reset stats and metadata // reset stats and metadata
int number_of_facets = this->stl.stats.number_of_facets; int number_of_facets = this->stl.stats.number_of_facets;
stl_invalidate_shared_vertices(&this->stl); this->its.clear();
this->repaired = false; this->repaired = false;
// update facet count and allocate more memory // update facet count and allocate more memory
@ -477,13 +484,12 @@ ExPolygons TriangleMesh::horizontal_projection() const
{ {
Polygons pp; Polygons pp;
pp.reserve(this->stl.stats.number_of_facets); pp.reserve(this->stl.stats.number_of_facets);
for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) { for (const stl_facet &facet : this->stl.facet_start) {
stl_facet* facet = &this->stl.facet_start[i];
Polygon p; Polygon p;
p.points.resize(3); p.points.resize(3);
p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1)); p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1)); p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1)); p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
pp.emplace_back(p); pp.emplace_back(p);
} }
@ -495,11 +501,10 @@ ExPolygons TriangleMesh::horizontal_projection() const
// 2D convex hull of a 3D mesh projected into the Z=0 plane. // 2D convex hull of a 3D mesh projected into the Z=0 plane.
Polygon TriangleMesh::convex_hull() Polygon TriangleMesh::convex_hull()
{ {
this->require_shared_vertices();
Points pp; Points pp;
pp.reserve(this->stl.stats.shared_vertices); pp.reserve(this->its.vertices.size());
for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) { for (size_t i = 0; i < this->its.vertices.size(); ++ i) {
const stl_vertex &v = this->stl.v_shared[i]; const stl_vertex &v = this->its.vertices[i];
pp.emplace_back(Point::new_scale(v(0), v(1))); pp.emplace_back(Point::new_scale(v(0), v(1)));
} }
return Slic3r::Geometry::convex_hull(pp); return Slic3r::Geometry::convex_hull(pp);
@ -517,49 +522,47 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
{ {
BoundingBoxf3 bbox; BoundingBoxf3 bbox;
if (stl.v_shared == nullptr) { if (this->its.vertices.empty()) {
// Using the STL faces. // Using the STL faces.
for (size_t i = 0; i < this->facets_count(); ++ i) { for (const stl_facet &facet : this->stl.facet_start)
const stl_facet &facet = this->stl.facet_start[i];
for (size_t j = 0; j < 3; ++ j) for (size_t j = 0; j < 3; ++ j)
bbox.merge(trafo * facet.vertex[j].cast<double>()); bbox.merge(trafo * facet.vertex[j].cast<double>());
}
} else { } else {
// Using the shared vertices should be a bit quicker than using the STL faces. // Using the shared vertices should be a bit quicker than using the STL faces.
for (int i = 0; i < stl.stats.shared_vertices; ++ i) for (const stl_vertex &v : this->its.vertices)
bbox.merge(trafo * this->stl.v_shared[i].cast<double>()); bbox.merge(trafo * v.cast<double>());
} }
return bbox; return bbox;
} }
TriangleMesh TriangleMesh::convex_hull_3d() const TriangleMesh TriangleMesh::convex_hull_3d() const
{ {
// Helper struct for qhull:
struct PointForQHull{
PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
realT x, y, z;
};
std::vector<PointForQHull> src_vertices;
// We will now fill the vector with input points for computation:
stl_facet* facet_ptr = stl.facet_start;
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
{
for (int i = 0; i < 3; ++i)
{
const stl_vertex& v = facet_ptr->vertex[i];
src_vertices.emplace_back(v(0), v(1), v(2));
}
facet_ptr += 1;
}
// The qhull call: // The qhull call:
orgQhull::Qhull qhull; orgQhull::Qhull qhull;
qhull.disableOutputStream(); // we want qhull to be quiet qhull.disableOutputStream(); // we want qhull to be quiet
try std::vector<realT> src_vertices;
try
{ {
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt"); if (this->has_shared_vertices()) {
#if REALfloat
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
#else
src_vertices.reserve(this->its.vertices() * 3);
// We will now fill the vector with input points for computation:
for (const stl_vertex &v : ths->its.vertices.size())
for (int i = 0; i < 3; ++ i)
src_vertices.emplace_back(v(i));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
#endif
} else {
src_vertices.reserve(this->stl.facet_start.size() * 9);
// We will now fill the vector with input points for computation:
for (const stl_facet &f : this->stl.facet_start)
for (int i = 0; i < 3; ++ i)
for (int j = 0; j < 3; ++ j)
src_vertices.emplace_back(f.vertex[i](j));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
}
} }
catch (...) catch (...)
{ {
@ -587,34 +590,20 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
TriangleMesh output_mesh(dst_vertices, facets); TriangleMesh output_mesh(dst_vertices, facets);
output_mesh.repair(); output_mesh.repair();
output_mesh.require_shared_vertices();
return output_mesh; return output_mesh;
} }
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->its.vertices.empty()) {
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, this->its);
} }
#ifdef _DEBUG assert(stl_validate(&this->stl, this->its));
// 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";
} }
@ -626,10 +615,9 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac
throw_on_cancel(); throw_on_cancel();
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); v_scaled_shared.assign(_mesh->its.vertices.size(), stl_vertex());
// Scale the copied vertices. for (size_t i = 0; i < v_scaled_shared.size(); ++ i)
for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) this->v_scaled_shared[i] = _mesh->its.vertices[i] / float(SCALING_FACTOR);
this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR);
// Create a mapping from triangle edge into face. // Create a mapping from triangle edge into face.
struct EdgeToFace { struct EdgeToFace {
@ -649,8 +637,8 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
for (int i = 0; i < 3; ++ i) { for (int i = 0; i < 3; ++ i) {
EdgeToFace &e2f = edges_map[facet_idx*3+i]; EdgeToFace &e2f = edges_map[facet_idx*3+i];
e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i]; e2f.vertex_low = this->mesh->its.indices[facet_idx][i];
e2f.vertex_high = this->mesh->stl.v_indices[facet_idx].vertex[(i + 1) % 3]; e2f.vertex_high = this->mesh->its.indices[facet_idx][(i + 1) % 3];
e2f.face = facet_idx; e2f.face = facet_idx;
// 1 based indexing, to be always strictly positive. // 1 based indexing, to be always strictly positive.
e2f.face_edge = i + 1; e2f.face_edge = i + 1;
@ -818,7 +806,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
const std::vector<float> &z) const const std::vector<float> &z) const
{ {
const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx]; const stl_facet &facet = m_use_quaternion ? (this->mesh->stl.facet_start.data() + facet_idx)->rotated(m_quaternion) : *(this->mesh->stl.facet_start.data() + facet_idx);
// find facet extents // find facet extents
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2))); const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
@ -887,7 +875,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
// Reorder vertices so that the first one is the one with lowest Z. // Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order // This is needed to get all intersection lines in a consistent order
// (external on the right of the line) // (external on the right of the line)
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; const stl_triangle_vertex_indices &vertices = this->mesh->its.indices[facet_idx];
int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0); int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0);
// These are used only if the cut plane is tilted: // These are used only if the cut plane is tilted:
@ -1714,7 +1702,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object";
float scaled_z = scale_(z); float scaled_z = scale_(z);
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) { for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; const stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents // find facet extents
float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2))); float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2)));
@ -1736,10 +1724,12 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
if (min_z > z || (min_z == z && max_z > z)) { if (min_z > z || (min_z == z && max_z > z)) {
// facet is above the cut plane and does not belong to it // facet is above the cut plane and does not belong to it
if (upper != NULL) stl_add_facet(&upper->stl, facet); if (upper != nullptr)
stl_add_facet(&upper->stl, facet);
} else if (max_z < z || (max_z == z && min_z < z)) { } else if (max_z < z || (max_z == z && min_z < z)) {
// facet is below the cut plane and does not belong to it // facet is below the cut plane and does not belong to it
if (lower != NULL) stl_add_facet(&lower->stl, facet); if (lower != nullptr)
stl_add_facet(&lower->stl, facet);
} else if (min_z < z && max_z > z) { } else if (min_z < z && max_z > z) {
// Facet is cut by the slicing plane. // Facet is cut by the slicing plane.
@ -1786,22 +1776,24 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
quadrilateral[1].vertex[2] = v0v1; quadrilateral[1].vertex[2] = v0v1;
if (v0(2) > z) { if (v0(2) > z) {
if (upper != NULL) stl_add_facet(&upper->stl, &triangle); if (upper != nullptr)
if (lower != NULL) { stl_add_facet(&upper->stl, &triangle);
if (lower != nullptr) {
stl_add_facet(&lower->stl, &quadrilateral[0]); stl_add_facet(&lower->stl, &quadrilateral[0]);
stl_add_facet(&lower->stl, &quadrilateral[1]); stl_add_facet(&lower->stl, &quadrilateral[1]);
} }
} else { } else {
if (upper != NULL) { if (upper != nullptr) {
stl_add_facet(&upper->stl, &quadrilateral[0]); stl_add_facet(&upper->stl, &quadrilateral[0]);
stl_add_facet(&upper->stl, &quadrilateral[1]); stl_add_facet(&upper->stl, &quadrilateral[1]);
} }
if (lower != NULL) stl_add_facet(&lower->stl, &triangle); if (lower != nullptr)
stl_add_facet(&lower->stl, &triangle);
} }
} }
} }
if (upper != NULL) { if (upper != nullptr) {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part";
ExPolygons section; ExPolygons section;
this->make_expolygons_simple(upper_lines, &section); this->make_expolygons_simple(upper_lines, &section);
@ -1815,7 +1807,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
} }
} }
if (lower != NULL) { if (lower != nullptr) {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part";
ExPolygons section; ExPolygons section;
this->make_expolygons_simple(lower_lines, &section); this->make_expolygons_simple(lower_lines, &section);
@ -1905,10 +1897,10 @@ TriangleMesh make_cylinder(double r, double h, double fa)
//FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html //FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
TriangleMesh make_sphere(double radius, double fa) TriangleMesh make_sphere(double radius, double fa)
{ {
int sectorCount = ceil(2. * M_PI / fa); int sectorCount = int(ceil(2. * M_PI / fa));
int stackCount = ceil(M_PI / fa); int stackCount = int(ceil(M_PI / fa));
float sectorStep = 2. * M_PI / sectorCount; float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = M_PI / stackCount; float stackStep = float(M_PI / stackCount);
Pointf3s vertices; Pointf3s vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2); vertices.reserve((stackCount - 1) * sectorCount + 2);

View file

@ -21,19 +21,13 @@ typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
class TriangleMesh class TriangleMesh
{ {
public: public:
TriangleMesh() : repaired(false) { stl_initialize(&this->stl); } TriangleMesh() : repaired(false) {}
TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets); TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets);
TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; } void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); } bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
~TriangleMesh() { clear(); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
TriangleMesh& operator=(const TriangleMesh &other); bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; } void repair(bool update_shared_vertices = true);
void clear() { stl_close(&this->stl); this->repaired = false; }
void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); }
void repair();
float volume(); float volume();
void check_topology(); void check_topology();
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; } bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
@ -58,7 +52,7 @@ public:
TriangleMeshPtrs split() const; TriangleMeshPtrs split() const;
void merge(const TriangleMesh &mesh); void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const; ExPolygons horizontal_projection() const;
const float* first_vertex() const { return this->stl.facet_start ? &this->stl.facet_start->vertex[0](0) : nullptr; } const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); }
// 2D convex hull of a 3D mesh projected into the Z=0 plane. // 2D convex hull of a 3D mesh projected into the Z=0 plane.
Polygon convex_hull(); Polygon convex_hull();
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
@ -69,12 +63,13 @@ public:
void reset_repair_stats(); void reset_repair_stats();
bool needed_repair() const; bool needed_repair() const;
void require_shared_vertices(); void require_shared_vertices();
bool has_shared_vertices() const { return stl.v_shared != NULL; } bool has_shared_vertices() const { return ! this->its.vertices.empty(); }
size_t facets_count() const { return this->stl.stats.number_of_facets; } size_t facets_count() const { return this->stl.stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; } bool empty() const { return this->facets_count() == 0; }
bool is_splittable() const; bool is_splittable() const;
stl_file stl; stl_file stl;
indexed_triangle_set its;
bool repaired; bool repaired;
private: private:

View file

@ -198,6 +198,11 @@ size_t Index::load(const boost::filesystem::path &path)
size_t idx_line = 0; size_t idx_line = 0;
Version ver; Version ver;
while (std::getline(ifs, line)) { while (std::getline(ifs, line)) {
#ifndef _MSVCVER
// On a Unix system, getline does not remove the trailing carriage returns, if the index is shared over a Windows filesystem. Remove them manually.
while (! line.empty() && line.back() == '\r')
line.pop_back();
#endif
++ idx_line; ++ idx_line;
// Skip the initial white spaces. // Skip the initial white spaces.
char *key = left_trim(const_cast<char*>(line.data())); char *key = left_trim(const_cast<char*>(line.data()));

View file

@ -241,8 +241,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
: m_transformed_bounding_box_dirty(true) : m_transformed_bounding_box_dirty(true)
, m_sla_shift_z(0.0) , m_sla_shift_z(0.0)
, m_transformed_convex_hull_bounding_box_dirty(true) , m_transformed_convex_hull_bounding_box_dirty(true)
, m_convex_hull(nullptr)
, m_convex_hull_owned(false)
// geometry_id == 0 -> invalid // geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0)) , geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0) , extruder_id(0)
@ -268,12 +266,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
set_render_color(r, g, b, a); set_render_color(r, g, b, a);
} }
GLVolume::~GLVolume()
{
if (m_convex_hull_owned)
delete m_convex_hull;
}
void GLVolume::set_render_color(float r, float g, float b, float a) void GLVolume::set_render_color(float r, float g, float b, float a)
{ {
render_color[0] = r; render_color[0] = r;
@ -335,12 +327,6 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume)
color[3] = model_volume->is_model_part() ? 1.f : 0.5f; color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
} }
void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned)
{
m_convex_hull = convex_hull;
m_convex_hull_owned = owned;
}
Transform3d GLVolume::world_matrix() const Transform3d GLVolume::world_matrix() const
{ {
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
@ -377,7 +363,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
{ {
return (m_convex_hull != nullptr && m_convex_hull->stl.stats.number_of_facets > 0) ? return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
m_convex_hull->transformed_bounding_box(trafo) : m_convex_hull->transformed_bounding_box(trafo) :
bounding_box.transformed(trafo); bounding_box.transformed(trafo);
} }
@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume(
const ModelVolume *model_volume = model_object->volumes[volume_idx]; const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id(); const int extruder_id = model_volume->extruder_id();
const ModelInstance *instance = model_object->instances[instance_idx]; const ModelInstance *instance = model_object->instances[instance_idx];
const TriangleMesh& mesh = model_volume->mesh; const TriangleMesh& mesh = model_volume->mesh();
float color[4]; float color[4];
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
/* if (model_volume->is_support_blocker()) { /* if (model_volume->is_support_blocker()) {
@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume(
if (model_volume->is_model_part()) if (model_volume->is_model_part())
{ {
// GLVolume will reference a convex hull from model_volume! // GLVolume will reference a convex hull from model_volume!
v.set_convex_hull(&model_volume->get_convex_hull(), false); v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
if (extruder_id != -1) if (extruder_id != -1)
v.extruder_id = extruder_id; v.extruder_id = extruder_id;
} }
@ -656,7 +642,10 @@ void GLVolumeCollection::load_object_auxiliary(
v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first); v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id); v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true); if (&instance_idx == &instances.back())
v.set_convex_hull(std::move(convex_hull));
else
v.set_convex_hull(convex_hull);
v.is_modifier = false; v.is_modifier = false;
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation()); v.set_instance_transformation(model_instance.get_transformation());

View file

@ -10,6 +10,7 @@
#include "slic3r/GUI/GLCanvas3DManager.hpp" #include "slic3r/GUI/GLCanvas3DManager.hpp"
#include <functional> #include <functional>
#include <memory>
#ifndef NDEBUG #ifndef NDEBUG
#define HAS_GLSAFE #define HAS_GLSAFE
@ -243,7 +244,6 @@ public:
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
~GLVolume();
private: private:
Geometry::Transformation m_instance_transformation; Geometry::Transformation m_instance_transformation;
@ -255,10 +255,8 @@ private:
mutable BoundingBoxf3 m_transformed_bounding_box; mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the transformed bounding box. // Whether or not is needed to recalculate the transformed bounding box.
mutable bool m_transformed_bounding_box_dirty; mutable bool m_transformed_bounding_box_dirty;
// Pointer to convex hull of the original mesh, if any. // Convex hull of the volume, if any.
// This object may or may not own the convex hull instance based on m_convex_hull_owned std::shared_ptr<const TriangleMesh> m_convex_hull;
const TriangleMesh* m_convex_hull;
bool m_convex_hull_owned;
// Bounding box of this volume, in unscaled coordinates. // Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
// Whether or not is needed to recalculate the transformed convex hull bounding box. // Whether or not is needed to recalculate the transformed convex hull bounding box.
@ -395,7 +393,9 @@ public:
double get_sla_shift_z() const { return m_sla_shift_z; } double get_sla_shift_z() const { return m_sla_shift_z; }
void set_sla_shift_z(double z) { m_sla_shift_z = z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; }
void set_convex_hull(const TriangleMesh *convex_hull, bool owned); void set_convex_hull(std::shared_ptr<const TriangleMesh> convex_hull) { m_convex_hull = std::move(convex_hull); }
void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(convex_hull); }
void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(std::move(convex_hull)); }
int object_idx() const { return this->composite_id.object_id; } int object_idx() const { return this->composite_id.object_id; }
int volume_idx() const { return this->composite_id.volume_id; } int volume_idx() const { return this->composite_id.volume_id; }

View file

@ -89,7 +89,7 @@ void BackgroundSlicingProcess::process_fff()
// Perform the final post-processing of the export path by applying the print statistics over the file name. // Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
if (copy_file(m_temp_output_path, export_path) != 0) if (copy_file(m_temp_output_path, export_path) != 0)
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
m_print->set_status(95, _utf8(L("Running post-processing scripts"))); m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config()); run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());

View file

@ -5502,7 +5502,7 @@ void GLCanvas3D::_load_sla_shells()
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
v.set_convex_hull(new TriangleMesh(std::move(mesh.convex_hull_3d())), true); v.set_convex_hull(mesh.convex_hull_3d());
}; };
// adds objects' volumes // adds objects' volumes

View file

@ -261,7 +261,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
const stl_stats& stats = vol_idx == -1 ? const stl_stats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() : (*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats; (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
std::map<std::string, int> error_msg = { std::map<std::string, int> error_msg = {
{ L("degenerate facets"), stats.degenerate_facets }, { L("degenerate facets"), stats.degenerate_facets },
@ -1592,7 +1592,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
// First (any) GLVolume of the selected instance. They all share the same instance matrix. // First (any) GLVolume of the selected instance. They all share the same instance matrix.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
// Transform the new modifier to be aligned with the print bed. // Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box(); const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
// Set the modifier position. // Set the modifier position.
auto offset = (type_name == "Slab") ? auto offset = (type_name == "Slab") ?

View file

@ -27,6 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_i
: GLGizmoBase(parent, sprite_id) : GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS #endif // ENABLE_SVG_ICONS
, m_quadric(nullptr) , m_quadric(nullptr)
, m_its(nullptr)
{ {
m_quadric = ::gluNewQuadric(); m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr) if (m_quadric != nullptr)
@ -379,36 +380,23 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
bool GLGizmoSlaSupports::is_mesh_update_necessary() const bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{ {
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
&& ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0); && ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr);
} }
void GLGizmoSlaSupports::update_mesh() void GLGizmoSlaSupports::update_mesh()
{ {
wxBusyCursor wait; wxBusyCursor wait;
Eigen::MatrixXf& V = m_V;
Eigen::MatrixXi& F = m_F;
// We rely on SLA model object having a single volume,
// this way we can use that mesh directly. // this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset. // This mesh does not account for the possible Z up SLA offset.
m_mesh = &m_model_object->volumes.front()->mesh; m_mesh = &m_model_object->volumes.front()->mesh();
const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this m_its = &m_mesh->its;
const stl_file& stl = m_mesh->stl;
V.resize(3 * stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
const stl_facet* facet = stl.facet_start+i;
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
F(i, 0) = 3*i+0;
F(i, 1) = 3*i+1;
F(i, 2) = 3*i+2;
}
m_current_mesh_model_id = m_model_object->id(); m_current_mesh_model_id = m_model_object->id();
m_editing_mode = false; m_editing_mode = false;
m_AABB = igl::AABB<Eigen::MatrixXf,3>(); m_AABB.deinit();
m_AABB.init(m_V, m_F); m_AABB.init(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
} }
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet. // Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
@ -416,7 +404,7 @@ void GLGizmoSlaSupports::update_mesh()
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
{ {
// if the gizmo doesn't have the V, F structures for igl, calculate them first: // if the gizmo doesn't have the V, F structures for igl, calculate them first:
if (m_V.size() == 0) if (m_its == nullptr)
update_mesh(); update_mesh();
const Camera& camera = m_parent.get_camera(); const Camera& camera = m_parent.get_camera();
@ -442,7 +430,10 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
point1 = inv * point1; point1 = inv * point1;
point2 = inv * point2; point2 = inv * point2;
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hits)) if (!m_AABB.intersect_ray(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
point1.cast<float>(), (point2-point1).cast<float>(), hits))
throw std::invalid_argument("unproject_on_mesh(): No intersection found."); throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
@ -457,9 +448,9 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
igl::Hit& hit = hits[i]; igl::Hit& hit = hits[i];
int fid = hit.id; // facet id int fid = hit.id; // facet id
bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>())) if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>()))
break; break;
} }
@ -564,15 +555,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// Cast a ray in the direction of the camera and look for intersection with the mesh: // Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<igl::Hit> hits; std::vector<igl::Hit> hits;
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { if (m_AABB.intersect_ray(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
if (m_clipping_plane_distance != 0.f) { if (m_clipping_plane_distance != 0.f) {
// If the closest hit facet normal points in the same direction as the ray, // If the closest hit facet normal points in the same direction as the ray,
// we are looking through the mesh and should therefore discard the point: // we are looking through the mesh and should therefore discard the point:
int fid = hits.front().id; // facet id int fid = hits.front().id; // facet id
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f) if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
is_obscured = true; is_obscured = true;
@ -582,7 +576,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
int fid = hit.id; // facet id int fid = hit.id; // facet id
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
if (is_point_clipped(hit_pos.cast<double>())) { if (is_point_clipped(hit_pos.cast<double>())) {
hits.erase(hits.begin()+j); hits.erase(hits.begin()+j);
--j; --j;
@ -759,9 +753,12 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
int idx = 0; int idx = 0;
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos; Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
Eigen::Matrix<float, 1, 3> cc; Eigen::Matrix<float, 1, 3> cc;
m_AABB.squared_distance(m_V, m_F, pp, idx, cc); m_AABB.squared_distance(
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0))); MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0))); MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
pp, idx, cc);
Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]);
Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]);
m_editing_mode_cache[i].normal = a.cross(b); m_editing_mode_cache[i].normal = a.cross(b);
} }
@ -1067,8 +1064,7 @@ void GLGizmoSlaSupports::on_set_state()
m_clipping_plane_distance = 0.f; m_clipping_plane_distance = 0.f;
// Release triangle mesh slicer and the AABB spatial search structure. // Release triangle mesh slicer and the AABB spatial search structure.
m_AABB.deinit(); m_AABB.deinit();
m_V = Eigen::MatrixXf(); m_its = nullptr;
m_F = Eigen::MatrixXi();
m_tms.reset(); m_tms.reset();
m_supports_tms.reset(); m_supports_tms.reset();
}); });

View file

@ -35,10 +35,11 @@ private:
const float RenderPointScale = 1.f; const float RenderPointScale = 1.f;
GLUquadricObj* m_quadric; GLUquadricObj* m_quadric;
Eigen::MatrixXf m_V; // vertices typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
Eigen::MatrixXi m_F; // facets indices typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
igl::AABB<Eigen::MatrixXf,3> m_AABB; igl::AABB<MapMatrixXfUnaligned, 3> m_AABB;
const TriangleMesh* m_mesh; const TriangleMesh* m_mesh;
const indexed_triangle_set* m_its;
mutable const TriangleMesh* m_supports_mesh; mutable const TriangleMesh* m_supports_mesh;
mutable std::vector<Vec2f> m_triangles; mutable std::vector<Vec2f> m_triangles;
mutable std::vector<Vec2f> m_supports_triangles; mutable std::vector<Vec2f> m_supports_triangles;

View file

@ -3565,7 +3565,7 @@ void Plater::export_stl(bool extended, bool selection_only)
else else
{ {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
mesh = model_object->volumes[volume->volume_idx()]->mesh; mesh = model_object->volumes[volume->volume_idx()]->mesh();
mesh.transform(volume->get_volume_transformation().get_matrix()); mesh.transform(volume->get_volume_transformation().get_matrix());
mesh.translate(-model_object->origin_translation.cast<float>()); mesh.translate(-model_object->origin_translation.cast<float>());
} }

View file

@ -781,7 +781,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
if (i == 0) if (i == 0)
suffix[0] = 0; suffix[0] = 0;
else else
sprintf(suffix, "%d", i); sprintf(suffix, "%d", (int)i);
std::string new_name = name + suffix; std::string new_name = name + suffix;
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
new_name, std::move(cfg), i == 0); new_name, std::move(cfg), i == 0);
@ -837,7 +837,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
return preset_name_dst; return preset_name_dst;
// Try to generate another name. // Try to generate another name.
char buf[64]; char buf[64];
sprintf(buf, " (%d)", i); sprintf(buf, " (%d)", (int)i);
preset_name_dst = preset_name_src + buf + bundle_name; preset_name_dst = preset_name_src + buf + bundle_name;
} }
} }
@ -1379,7 +1379,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
for (size_t i = 0; i < this->filament_presets.size(); ++ i) { for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
char suffix[64]; char suffix[64];
if (i > 0) if (i > 0)
sprintf(suffix, "_%d", i); sprintf(suffix, "_%d", (int)i);
else else
suffix[0] = 0; suffix[0] = 0;
c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl;

View file

@ -389,10 +389,10 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
throw std::runtime_error(L("Repaired 3MF file does not contain any volume")); throw std::runtime_error(L("Repaired 3MF file does not contain any volume"));
if (model.objects.front()->volumes.size() > 1) if (model.objects.front()->volumes.size() > 1)
throw std::runtime_error(L("Repaired 3MF file contains more than one volume")); throw std::runtime_error(L("Repaired 3MF file contains more than one volume"));
meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh)); meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh()));
} }
for (size_t i = 0; i < volumes.size(); ++ i) { for (size_t i = 0; i < volumes.size(); ++ i) {
volumes[i]->mesh = std::move(meshes_repaired[i]); volumes[i]->set_mesh(std::move(meshes_repaired[i]));
volumes[i]->set_new_unique_id(); volumes[i]->set_new_unique_id();
} }
model_object.invalidate_bounding_box(); model_object.invalidate_bounding_box();

View file

@ -253,7 +253,7 @@ ModelMaterial::attributes()
Ref<DynamicPrintConfig> config() Ref<DynamicPrintConfig> config()
%code%{ RETVAL = &THIS->config; %}; %code%{ RETVAL = &THIS->config; %};
Ref<TriangleMesh> mesh() Ref<TriangleMesh> mesh()
%code%{ RETVAL = &THIS->mesh; %}; %code%{ RETVAL = &THIS->mesh(); %};
bool modifier() bool modifier()
%code%{ RETVAL = THIS->is_modifier(); %}; %code%{ RETVAL = THIS->is_modifier(); %};

View file

@ -46,7 +46,6 @@ TriangleMesh::ReadFromPerl(vertices, facets)
SV* facets SV* facets
CODE: CODE:
stl_file &stl = THIS->stl; stl_file &stl = THIS->stl;
stl.error = 0;
stl.stats.type = inmemory; stl.stats.type = inmemory;
// count facets and allocate memory // count facets and allocate memory
@ -99,20 +98,18 @@ SV*
TriangleMesh::vertices() TriangleMesh::vertices()
CODE: CODE:
if (!THIS->repaired) CONFESS("vertices() requires repair()"); if (!THIS->repaired) CONFESS("vertices() requires repair()");
THIS->require_shared_vertices();
if (THIS->stl.v_shared == NULL)
stl_generate_shared_vertices(&(THIS->stl));
// vertices // vertices
AV* vertices = newAV(); AV* vertices = newAV();
av_extend(vertices, THIS->stl.stats.shared_vertices); av_extend(vertices, THIS->its.vertices.size());
for (int i = 0; i < THIS->stl.stats.shared_vertices; i++) { for (size_t i = 0; i < THIS->its.vertices.size(); i++) {
AV* vertex = newAV(); AV* vertex = newAV();
av_store(vertices, i, newRV_noinc((SV*)vertex)); av_store(vertices, i, newRV_noinc((SV*)vertex));
av_extend(vertex, 2); av_extend(vertex, 2);
av_store(vertex, 0, newSVnv(THIS->stl.v_shared[i](0))); av_store(vertex, 0, newSVnv(THIS->its.vertices[i](0)));
av_store(vertex, 1, newSVnv(THIS->stl.v_shared[i](1))); av_store(vertex, 1, newSVnv(THIS->its.vertices[i](1)));
av_store(vertex, 2, newSVnv(THIS->stl.v_shared[i](2))); av_store(vertex, 2, newSVnv(THIS->its.vertices[i](2)));
} }
RETVAL = newRV_noinc((SV*)vertices); RETVAL = newRV_noinc((SV*)vertices);
@ -123,9 +120,7 @@ SV*
TriangleMesh::facets() TriangleMesh::facets()
CODE: CODE:
if (!THIS->repaired) CONFESS("facets() requires repair()"); if (!THIS->repaired) CONFESS("facets() requires repair()");
THIS->require_shared_vertices();
if (THIS->stl.v_shared == NULL)
stl_generate_shared_vertices(&(THIS->stl));
// facets // facets
AV* facets = newAV(); AV* facets = newAV();
@ -134,9 +129,9 @@ TriangleMesh::facets()
AV* facet = newAV(); AV* facet = newAV();
av_store(facets, i, newRV_noinc((SV*)facet)); av_store(facets, i, newRV_noinc((SV*)facet));
av_extend(facet, 2); av_extend(facet, 2);
av_store(facet, 0, newSVnv(THIS->stl.v_indices[i].vertex[0])); av_store(facet, 0, newSVnv(THIS->its.indices[i][0]));
av_store(facet, 1, newSVnv(THIS->stl.v_indices[i].vertex[1])); av_store(facet, 1, newSVnv(THIS->its.indices[i][1]));
av_store(facet, 2, newSVnv(THIS->stl.v_indices[i].vertex[2])); av_store(facet, 2, newSVnv(THIS->its.indices[i][2]));
} }
RETVAL = newRV_noinc((SV*)facets); RETVAL = newRV_noinc((SV*)facets);