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,7 +244,6 @@ 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,209 +25,50 @@
#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];
neighbor[0] = stl->neighbors_start[facet_num].neighbor[0]; stl->facet_start[facet_num].vertex[0] = stl->facet_start[facet_num].vertex[1];
neighbor[1] = stl->neighbors_start[facet_num].neighbor[1];
neighbor[2] = stl->neighbors_start[facet_num].neighbor[2];
vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0];
vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1];
vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2];
/* reverse the facet */
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; stl->facet_start[facet_num].vertex[1] = tmp_vertex;
/* fix the vnots of the neighboring facets */ // fix the vnots of the neighboring facets
if(neighbor[0] != -1) if (neighbor[0] != -1)
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = 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;
(stl->neighbors_start[neighbor[0]]. if (neighbor[1] != -1)
which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6; 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[1] != -1) if (neighbor[2] != -1)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = 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;
(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 */ // swap the neighbors of the facet that is being reversed
stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
/* swap the vnots of the facet that is being reversed */ // 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[1] = vnot[2];
stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1]; stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
/* reverse the values of the vnots 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].which_vertex_not[0] = 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[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[1] = stl->neighbors_start[facet_num].which_vertex_not[2] = (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
(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;
int reversed_count = 0;
int id;
int force_exit = 0;
if (stl->error) return;
// this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
if (stl->stats.number_of_facets == 0) return;
/* Initialize linked list. */
head = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(head == NULL) perror("stl_fix_normal_directions");
tail = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(tail == NULL) perror("stl_fix_normal_directions");
head->next = tail;
tail->next = tail;
/* Initialize list that keeps track of already fixed facets. */
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
if(norm_sw == NULL) perror("stl_fix_normal_directions");
/* Initialize list that keeps track of reversed facets. */
reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int));
if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids");
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) {
/* Returns 0 if the normal is within tolerance */
/* Returns 1 if the normal is not within tolerance, but direction is OK */
/* Returns 2 if the normal is not within tolerance and backwards */
/* Returns 4 if the status is unknown. */
stl_facet *facet;
facet = &stl->facet_start[facet_num];
stl_normal normal; stl_normal normal;
stl_calculate_normal(normal, facet); stl_calculate_normal(normal, facet);
@ -236,58 +77,160 @@ static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_
const float eps = 0.001f; const float eps = 0.001f;
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
/* It is not really necessary to change the values here */ // Normal is within tolerance. It is not really necessary to change the values here, but just for consistency, I will.
/* but just for consistency, I will. */
facet->normal = normal; facet->normal = normal;
return 0; return false;
} }
stl_normal test_norm = facet->normal; stl_normal test_norm = facet->normal;
stl_normalize_vector(test_norm); stl_normalize_vector(test_norm);
normal_dif = (normal - test_norm).cwiseAbs(); normal_dif = (normal - test_norm).cwiseAbs();
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
if(normal_fix_flag) { // The normal is not within tolerance, but direction is OK.
if (normal_fix_flag) {
facet->normal = normal; facet->normal = normal;
stl->stats.normals_fixed += 1; ++ stl->stats.normals_fixed;
} }
return 1; return false;
} }
test_norm *= -1.f; test_norm *= -1.f;
normal_dif = (normal - test_norm).cwiseAbs(); normal_dif = (normal - test_norm).cwiseAbs();
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
// Facet is backwards. // The normal is not within tolerance and backwards.
if(normal_fix_flag) { if (normal_fix_flag) {
facet->normal = normal; facet->normal = normal;
stl->stats.normals_fixed += 1; ++ stl->stats.normals_fixed;
} }
return 2; return true;
} }
if(normal_fix_flag) { if (normal_fix_flag) {
facet->normal = normal; facet->normal = normal;
stl->stats.normals_fixed += 1; ++ stl->stats.normals_fixed;
} }
return 4; // Status is unknown.
return false;
} }
void stl_fix_normal_values(stl_file *stl) { void stl_fix_normal_directions(stl_file *stl)
int i; {
// This may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
if (stl->stats.number_of_facets == 0)
return;
if (stl->error) return; struct stl_normal {
int facet_num;
stl_normal *next;
};
for(i = 0; i < stl->stats.number_of_facets; i++) { // Initialize linked list.
stl_check_normal_vector(stl, i, 1); boost::object_pool<stl_normal> pool;
stl_normal *head = pool.construct();
stl_normal *tail = pool.construct();
head->next = tail;
tail->next = tail;
// Initialize list that keeps track of already fixed facets.
std::vector<char> norm_sw(stl->stats.number_of_facets, 0);
// Initialize list that keeps track of reversed facets.
std::vector<int> reversed_ids(stl->stats.number_of_facets, 0);
int facet_num = 0;
int reversed_count = 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 (check_normal_vector(stl, 0, 0)) {
reverse_facet(stl, 0);
reversed_ids[reversed_count ++] = 0;
} }
// Say that we've fixed this facet:
norm_sw[facet_num] = 1;
int checked = 1;
for (;;) {
// Add neighbors_to_list. Add unconnected neighbors to the list.
bool force_exit = false;
for (int 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 (int id = reversed_count - 1; id >= 0; -- id)
reverse_facet(stl, reversed_ids[id]);
force_exit = true;
break;
}
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)
{
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
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)
return;
stl_normal normal; stl_normal normal;
for(int i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_reverse_facet(stl, i); reverse_facet(stl, i);
stl_calculate_normal(normal, &stl->facet_start[i]); stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl->facet_start[i].normal = normal; stl->facet_start[i].normal = normal;

View file

@ -23,92 +23,60 @@
#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;
}
}
void for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) {
stl_generate_shared_vertices(stl_file *stl) { for (int j = 0; j < 3; ++ j) {
int i; if (its.indices[facet_idx][j] != -1)
int j; // Shared vertex was already assigned.
int first_facet;
int direction;
int facet_num;
int vnot;
int next_edge;
int pivot_vertex;
int next_facet;
int reversed;
if (stl->error) return;
/* make sure this function is idempotent and does not leak memory */
stl_invalidate_shared_vertices(stl);
stl->v_indices = (v_indices_struct*)
calloc(stl->stats.number_of_facets, sizeof(v_indices_struct));
if(stl->v_indices == NULL) perror("stl_generate_shared_vertices");
stl->v_shared = (stl_vertex*)
calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex));
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
stl->stats.shared_vertices = 0;
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl->v_indices[i].vertex[0] = -1;
stl->v_indices[i].vertex[1] = -1;
stl->v_indices[i].vertex[2] = -1;
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
first_facet = i;
for(j = 0; j < 3; j++) {
if(stl->v_indices[i].vertex[j] != -1) {
continue; continue;
} // Create a new shared vertex.
if(stl->stats.shared_vertices == stl->stats.shared_malloced) { its.vertices.emplace_back(stl->facet_start[facet_idx].vertex[j]);
stl->stats.shared_malloced += 1024; // 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.
stl->v_shared = (stl_vertex*)realloc(stl->v_shared, int facet_in_fan_idx = facet_idx;
stl->stats.shared_malloced * sizeof(stl_vertex)); bool edge_direction = false;
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); bool traversal_reversed = false;
} int vnot = (j + 2) % 3;
// Increase the
stl->v_shared[stl->stats.shared_vertices] = ++ fan_traversal_stamp;
stl->facet_start[i].vertex[j]; for (;;) {
// Next edge on facet_in_fan_idx to be traversed. The edge is indexed by its starting vertex index.
direction = 0; int next_edge = 0;
reversed = 0; // Vertex index in facet_in_fan_idx, which is being pivoted around, and which is being assigned a new shared vertex.
facet_num = i; int pivot_vertex = 0;
vnot = (j + 2) % 3; if (vnot > 2) {
// The edge of facet_in_fan_idx opposite to vnot is equally oriented, therefore
for(;;) { // the neighboring facet is flipped.
if(vnot > 2) { if (! edge_direction) {
if(direction == 0) {
pivot_vertex = (vnot + 2) % 3; pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex; next_edge = pivot_vertex;
direction = 1;
} else { } else {
pivot_vertex = (vnot + 1) % 3; pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3; next_edge = vnot % 3;
direction = 0;
} }
edge_direction = ! edge_direction;
} else { } else {
if(direction == 0) { // The neighboring facet is correctly oriented.
if (! edge_direction) {
pivot_vertex = (vnot + 1) % 3; pivot_vertex = (vnot + 1) % 3;
next_edge = vnot; next_edge = vnot;
} else { } else {
@ -116,87 +84,73 @@ stl_generate_shared_vertices(stl_file *stl) {
next_edge = pivot_vertex; next_edge = pivot_vertex;
} }
} }
stl->v_indices[facet_num].vertex[pivot_vertex] = its.indices[facet_in_fan_idx][pivot_vertex] = its.vertices.size() - 1;
stl->stats.shared_vertices; fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp;
next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
if(next_facet == -1) { int next_facet = stl->neighbors_start[facet_in_fan_idx].neighbor[next_edge];
if(reversed) { 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; break;
} else { } else {
direction = 1; // Reached the first limit. Now try to reverse and traverse up to the other limit.
edge_direction = true;
vnot = (j + 1) % 3; vnot = (j + 1) % 3;
reversed = 1; traversal_reversed = true;
facet_num = first_facet; facet_in_fan_idx = facet_idx;
} }
} else if(next_facet != first_facet) { } else if (next_facet == facet_idx) {
vnot = stl->neighbors_start[facet_num]. // Traversed a closed fan all around.
which_vertex_not[next_edge]; // assert(! traversal_reversed);
facet_num = next_facet;
} else {
break; break;
} else if (next_facet >= (int)stl->stats.number_of_facets) {
// The mesh is not valid!
// assert(false);
break;
} else if (fan_traversal_facet_visited[next_facet] == fan_traversal_stamp) {
// Traversed a closed fan all around, but did not reach the starting face.
// This indicates an invalid geometry (non-manifold).
//assert(false);
break;
} else {
// Continue traversal.
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
vnot = stl->neighbors_start[facet_in_fan_idx].which_vertex_not[next_edge];
facet_in_fan_idx = next_facet;
} }
} }
stl->stats.shared_vertices += 1;
} }
} }
} }
void bool its_write_off(const indexed_triangle_set &its, const char *file)
stl_write_off(stl_file *stl, const char *file) { {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */ /* Open the file */
fp = boost::nowide::fopen(file, "w"); FILE *fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if (fp == nullptr) {
error_msg = (char*) BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ return false;
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, "OFF\n");
fprintf(fp, "%d %d 0\n", fprintf(fp, "%d %d 0\n", (int)its.vertices.size(), (int)its.indices.size());
stl->stats.shared_vertices, stl->stats.number_of_facets); 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(i = 0; i < stl->stats.shared_vertices; i++) { for (uint32_t i = 0; i < its.indices.size(); ++ i)
fprintf(fp, "\t%f %f %f\n", fprintf(fp, "\t3 %d %d %d\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
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); fclose(fp);
return true;
} }
void bool its_write_vrml(const indexed_triangle_set &its, const char *file)
stl_write_vrml(stl_file *stl, const char *file) { {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */ /* Open the file */
fp = boost::nowide::fopen(file, "w"); FILE *fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if (fp == nullptr) {
error_msg = (char*) BOOST_LOG_TRIVIAL(error) << "stl_write_vrml: Couldn't open " << file << " for writing";
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ return false;
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, "#VRML V1.0 ascii\n\n");
@ -214,51 +168,92 @@ stl_write_vrml(stl_file *stl, const char *file) {
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n"); fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
fprintf(fp, "\t\t\tpoint [\n"); fprintf(fp, "\t\t\tpoint [\n");
for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { int i = 0;
fprintf(fp, "\t\t\t\t%f %f %f,\n", for (; i + 1 < its.vertices.size(); ++ i)
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", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
} 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\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\t}\n");
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\t\tcoordIndex [\n"); fprintf(fp, "\t\t\tcoordIndex [\n");
for(i = 0; i < (stl->stats.number_of_facets - 1); i++) { for (size_t i = 0; i + 1 < its.indices.size(); ++ i)
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[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\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
fprintf(fp, "\t\t}\n"); fprintf(fp, "\t\t}\n");
fprintf(fp, "\t}\n"); fprintf(fp, "\t}\n");
fprintf(fp, "}\n"); fprintf(fp, "}\n");
fclose(fp); fclose(fp);
return true;
} }
void stl_write_obj (stl_file *stl, const char *file) { bool its_write_obj(const indexed_triangle_set &its, const char *file)
int i; {
FILE* fp;
if (stl->error) return; FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
/* Open the file */ BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
fp = boost::nowide::fopen(file, "w"); return false;
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);
} }
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); fclose(fp);
return true;
}
// Check validity of the mesh, assert on error.
bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
{
assert(! stl->facet_start.empty());
assert(stl->facet_start.size() == stl->stats.number_of_facets);
assert(stl->neighbors_start.size() == stl->stats.number_of_facets);
assert(stl->facet_start.size() == stl->neighbors_start.size());
assert(! stl->neighbors_start.empty());
assert((its.indices.empty()) == (its.vertices.empty()));
assert(stl->stats.number_of_facets > 0);
assert(its.vertices.empty() || its.indices.size() == stl->stats.number_of_facets);
#ifdef _DEBUG
// Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) {
const stl_neighbors &nbr = stl->neighbors_start[facet_idx];
const int *vertices = its.indices.empty() ? nullptr : its.indices[facet_idx].data();
for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) {
int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx];
assert(nbr_face < (int)stl->stats.number_of_facets);
if (nbr_face != -1) {
int nbr_vnot = nbr.which_vertex_not[nbr_idx];
assert(nbr_vnot >= 0 && nbr_vnot < 6);
// Neighbor of the neighbor is the original face.
assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx);
int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3];
assert(vnot_back >= 0 && vnot_back < 6);
assert((nbr_vnot < 3) == (vnot_back < 3));
assert(vnot_back % 3 == (nbr_idx + 2) % 3);
if (vertices != nullptr) {
// Has shared vertices.
if (nbr_vnot < 3) {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented.
assert((its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[nbr_idx]));
} else {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
assert((its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[nbr_idx]));
}
}
}
}
}
#endif /* _DEBUG */
return true;
}
// Check validity of the mesh, assert on error.
bool stl_validate(const stl_file *stl)
{
indexed_triangle_set its;
return stl_validate(stl, its);
} }

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,6 +41,7 @@
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");
@ -48,7 +50,7 @@ struct stl_facet {
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];
@ -67,39 +69,28 @@ 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 {
// Key of a hash edge: sorted vertices of the edge.
uint32_t key[6];
// Compare two keys.
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 {
// Index of a neighbor facet. // Index of a neighbor facet.
int neighbor[3]; int neighbor[3];
// Index of an opposite vertex at the neighbor face. // Index of an opposite vertex at the neighbor face.
char which_vertex_not[3]; char which_vertex_not[3];
} stl_neighbors; };
typedef struct { struct stl_stats {
int vertex[3]; stl_stats() { this->reset(); }
} v_indices_struct; void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; }
typedef struct {
char header[81]; char header[81];
stl_type type; stl_type type;
uint32_t number_of_facets; uint32_t number_of_facets;
@ -109,7 +100,6 @@ typedef struct {
float bounding_diameter; float bounding_diameter;
float shortest_edge; float shortest_edge;
float volume; float volume;
unsigned number_of_blocks;
int connected_edges; int connected_edges;
int connected_facets_1_edge; int connected_facets_1_edge;
int connected_facets_2_edge; int connected_facets_2_edge;
@ -126,45 +116,47 @@ typedef struct {
int backwards_edges; int backwards_edges;
int normals_fixed; int normals_fixed;
int number_of_parts; int number_of_parts;
int malloced; };
int freed;
int facets_malloced;
int collisions;
int shared_vertices;
int shared_malloced;
} stl_stats;
typedef struct { struct stl_file {
FILE *fp; stl_file() {}
stl_facet *facet_start;
stl_hash_edge **heads; void clear() {
stl_hash_edge *tail; this->facet_start.clear();
int M; this->neighbors_start.clear();
stl_neighbors *neighbors_start; this->stats.reset();
v_indices_struct *v_indices; }
stl_vertex *v_shared;
std::vector<stl_facet> facet_start;
std::vector<stl_neighbors> neighbors_start;
// Statistics
stl_stats stats; 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,9 +178,6 @@ 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)
return;
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
stl_facet &face = stl->facet_start[i_face]; stl_facet &face = stl->facet_start[i_face];
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
@ -211,11 +200,8 @@ extern void stl_transform(stl_file *stl, T *trafo3x4)
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,158 +22,85 @@
#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, "\
File type : ASCII STL file\n");
}
fprintf(file, "\
Header : %s\n", stl->stats.header);
fprintf(file, "============== Size ==============\n"); fprintf(file, "============== Size ==============\n");
fprintf(file, "Min X = % f, Max X = % f\n", fprintf(file, "Min X = % f, Max X = % f\n", stl->stats.min(0), stl->stats.max(0));
stl->stats.min(0), stl->stats.max(0)); fprintf(file, "Min Y = % f, Max Y = % f\n", stl->stats.min(1), stl->stats.max(1));
fprintf(file, "Min Y = % f, Max Y = % f\n", fprintf(file, "Min Z = % f, Max Z = % f\n", stl->stats.min(2), stl->stats.max(2));
stl->stats.min(1), stl->stats.max(1)); fprintf(file, "========= Facet Status ========== Original ============ Final ====\n");
fprintf(file, "Min Z = % f, Max Z = % f\n", fprintf(file, "Number of facets : %5d %5d\n", stl->stats.original_num_facets, stl->stats.number_of_facets);
stl->stats.min(2), stl->stats.max(2)); fprintf(file, "Facets with 1 disconnected edge : %5d %5d\n",
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge);
fprintf(file, "\ fprintf(file, "Facets with 2 disconnected edges : %5d %5d\n",
========= Facet Status ========== Original ============ Final ====\n"); stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge);
fprintf(file, "\ fprintf(file, "Facets with 3 disconnected edges : %5d %5d\n",
Number of facets : %5d %5d\n", stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_1_edge);
stl->stats.original_num_facets, stl->stats.number_of_facets); fprintf(file, "Total disconnected facets : %5d %5d\n",
fprintf(file, "\ 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);
Facets with 1 disconnected edge : %5d %5d\n", fprintf(file, "=== Processing Statistics === ===== Other Statistics =====\n");
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - fprintf(file, "Number of parts : %5d Volume : %f\n", stl->stats.number_of_parts, stl->stats.volume);
stl->stats.connected_facets_3_edge); fprintf(file, "Degenerate facets : %5d\n", stl->stats.degenerate_facets);
fprintf(file, "\ fprintf(file, "Edges fixed : %5d\n", stl->stats.edges_fixed);
Facets with 2 disconnected edges : %5d %5d\n", fprintf(file, "Facets removed : %5d\n", stl->stats.facets_removed);
stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - fprintf(file, "Facets added : %5d\n", stl->stats.facets_added);
stl->stats.connected_facets_2_edge); fprintf(file, "Facets reversed : %5d\n", stl->stats.facets_reversed);
fprintf(file, "\ fprintf(file, "Backwards edges : %5d\n", stl->stats.backwards_edges);
Facets with 3 disconnected edges : %5d %5d\n", fprintf(file, "Normals fixed : %5d\n", stl->stats.normals_fixed);
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;
char *error_msg;
if (stl->error) return;
/* Open the file */
FILE *fp = boost::nowide::fopen(file, "w"); FILE *fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if (fp == nullptr) {
error_msg = (char*) BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ return false;
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, "solid %s\n", label); fprintf(fp, "solid %s\n", label);
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
fprintf(fp, " facet normal % .8E % .8E % .8E\n", 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));
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, " outer loop\n");
fprintf(fp, " vertex % .8E % .8E % .8E\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));
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), 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));
stl->facet_start[i].vertex[0](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, " 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, " endloop\n");
fprintf(fp, " endfacet\n"); fprintf(fp, " endfacet\n");
} }
fprintf(fp, "endsolid %s\n", label); fprintf(fp, "endsolid %s\n", label);
fclose(fp); fclose(fp);
return true;
} }
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;
/* 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++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
i, i,
stl->neighbors_start[i].neighbor[0], stl->neighbors_start[i].neighbor[0],
@ -184,9 +111,10 @@ stl_print_neighbors(stl_file *stl, char *file) {
(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)
{ {
@ -197,36 +125,27 @@ void stl_internal_reverse_quads(char *buf, size_t cnt)
} }
#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;
/* Open the file */
fp = boost::nowide::fopen(file, "wb");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
} }
fprintf(fp, "%s", label); fprintf(fp, "%s", label);
for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); for (size_t i = strlen(label); i < LABEL_SIZE; ++ i)
putc(0, fp);
#if !defined(SEEK_SET)
#define SEEK_SET 0
#endif
fseek(fp, LABEL_SIZE, SEEK_SET); fseek(fp, LABEL_SIZE, SEEK_SET);
#ifdef BOOST_LITTLE_ENDIAN #if BOOST_ENDIAN_LITTLE_BYTE
fwrite(&stl->stats.number_of_facets, 4, 1, fp); fwrite(&stl->stats.number_of_facets, 4, 1, fp);
for (i = 0; i < stl->stats.number_of_facets; ++ i) for (const stl_facet &facet : stl->facet_start)
fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); fwrite(&facet, SIZEOF_STL_FACET, 1, fp);
#else /* BOOST_LITTLE_ENDIAN */ #else /* BOOST_ENDIAN_LITTLE_BYTE */
char buffer[50]; char buffer[50];
// Convert the number of facets to little endian. // Convert the number of facets to little endian.
memcpy(buffer, &stl->stats.number_of_facets, 4); memcpy(buffer, &stl->stats.number_of_facets, 4);
@ -238,44 +157,29 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) {
stl_internal_reverse_quads(buffer, 48); stl_internal_reverse_quads(buffer, 48);
fwrite(buffer, SIZEOF_STL_FACET, 1, fp); fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
} }
#endif /* BOOST_LITTLE_ENDIAN */ #endif /* BOOST_ENDIAN_LITTLE_BYTE */
fclose(fp); fclose(fp);
return true;
} }
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("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label);
if(edge.which_edge < 3) {
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
} else {
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
}
}
void
stl_write_neighbor(stl_file *stl, int facet) {
if (stl->error) return;
printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
stl->neighbors_start[facet].neighbor[0], stl->neighbors_start[facet].neighbor[0],
stl->neighbors_start[facet].neighbor[1], stl->neighbors_start[facet].neighbor[1],
@ -285,131 +189,62 @@ stl_write_neighbor(stl_file *stl, int facet) {
stl->neighbors_start[facet].which_vertex_not[2]); stl->neighbors_start[facet].which_vertex_not[2]);
} }
void bool stl_write_quad_object(stl_file *stl, char *file)
stl_write_quad_object(stl_file *stl, char *file) { {
FILE *fp;
int i;
int j;
char *error_msg;
stl_vertex connect_color = stl_vertex::Zero(); stl_vertex connect_color = stl_vertex::Zero();
stl_vertex uncon_1_color = stl_vertex::Zero(); stl_vertex uncon_1_color = stl_vertex::Zero();
stl_vertex uncon_2_color = stl_vertex::Zero(); stl_vertex uncon_2_color = stl_vertex::Zero();
stl_vertex uncon_3_color = stl_vertex::Zero(); stl_vertex uncon_3_color = stl_vertex::Zero();
stl_vertex color; stl_vertex color;
if (stl->error) return; FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
/* Open the file */ BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing";
fp = boost::nowide::fopen(file, "w"); return false;
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
} }
fprintf(fp, "CQUAD\n"); fprintf(fp, "CQUAD\n");
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
j = ((stl->neighbors_start[i].neighbor[0] == -1) + switch (stl->neighbors_start[i].num_neighbors_missing()) {
(stl->neighbors_start[i].neighbor[1] == -1) + case 0: color = connect_color; break;
(stl->neighbors_start[i].neighbor[2] == -1)); case 1: color = uncon_1_color; break;
if(j == 0) { case 2: color = uncon_2_color; break;
color = connect_color; default: color = uncon_3_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", 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));
stl->facet_start[i].vertex[0](0), 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));
stl->facet_start[i].vertex[0](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));
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[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[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;
/* 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, "999\n%s\n", label); fprintf(fp, "999\n%s\n", label);
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\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"); 0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
fprintf(fp, "0\n3DFACE\n8\n0\n"); fprintf(fp, "0\n3DFACE\n8\n0\n");
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\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));
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), 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));
stl->facet_start[i].vertex[0](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, "11\n%f\n21\n%f\n31\n%f\n", 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));
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"); fprintf(fp, "0\nENDSEC\n0\nEOF\n");
fclose(fp); fclose(fp);
} return true;
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,279 +36,152 @@
#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);
void // Check for binary or ASCII file.
stl_initialize(stl_file *stl) { fseek(fp, HEADER_SIZE, SEEK_SET);
memset(stl, 0, sizeof(stl_file));
stl->stats.volume = -1.0;
}
#ifndef BOOST_LITTLE_ENDIAN
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
#endif /* BOOST_LITTLE_ENDIAN */
void
stl_count_facets(stl_file *stl, const char *file) {
long file_size;
uint32_t header_num_facets;
uint32_t num_facets;
int i;
size_t s;
unsigned char chtest[128]; unsigned char chtest[128];
int num_lines = 1; if (! fread(chtest, sizeof(chtest), 1, fp)) {
char *error_msg; BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The input is an empty file: " << file;
fclose(fp);
if (stl->error) return; return nullptr;
/* 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; stl->stats.type = ascii;
for(s = 0; s < sizeof(chtest); s++) { for (size_t s = 0; s < sizeof(chtest); s++) {
if(chtest[s] > 127) { if (chtest[s] > 127) {
stl->stats.type = binary; stl->stats.type = binary;
break; break;
} }
} }
rewind(stl->fp); rewind(fp);
/* Get the header and the number of facets in the .STL file */ uint32_t num_facets = 0;
/* If the .STL file is binary, then do the following */
if(stl->stats.type == binary) { // Get the header and the number of facets in the .STL file.
/* Test if the STL file has the right size */ // If the .STL file is binary, then do the following:
if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) if (stl->stats.type == binary) {
|| (file_size < STL_MIN_FILE_SIZE)) { // Test if the STL file has the right size.
fprintf(stderr, "The file %s has the wrong size.\n", file); if (((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) || (file_size < STL_MIN_FILE_SIZE)) {
stl->error = 1; BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The file " << file << " has the wrong size.";
return; fclose(fp);
return nullptr;
} }
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
/* Read the header */ // Read the header.
if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) { if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79)
stl->stats.header[80] = '\0'; stl->stats.header[80] = '\0';
}
/* Read the int following the header. This should contain # of facets */ // 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; uint32_t header_num_facets;
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0;
#ifndef BOOST_LITTLE_ENDIAN #ifndef BOOST_LITTLE_ENDIAN
// Convert from little endian to big endian. // Convert from little endian to big endian.
stl_internal_reverse_quads((char*)&header_num_facets, 4); stl_internal_reverse_quads((char*)&header_num_facets, 4);
#endif /* BOOST_LITTLE_ENDIAN */ #endif /* BOOST_LITTLE_ENDIAN */
if (! header_num_faces_read || num_facets != header_num_facets) { if (! header_num_faces_read || num_facets != header_num_facets)
fprintf(stderr, BOOST_LOG_TRIVIAL(info) << "stl_open_count_facets: Warning: File size doesn't match number of facets in the header: " << file;
"Warning: File size doesn't match number of facets in the header\n");
} }
} // Otherwise, if the .STL file is ASCII, then do the following:
/* Otherwise, if the .STL file is ASCII, then do the following */ else
else { {
/* Reopen the file in text mode (for getting correct newlines on Windows) */ // Reopen the file in text mode (for getting correct newlines on Windows)
// fix to silence a warning about unused return value. // fix to silence a warning about unused return value.
// obviously if it fails we have problems.... // obviously if it fails we have problems....
stl->fp = boost::nowide::freopen(file, "r", stl->fp); fp = boost::nowide::freopen(file, "r", fp);
// do another null check to be safe // do another null check to be safe
if(stl->fp == NULL) { if (fp == nullptr) {
error_msg = (char*) BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading";
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ fclose(fp);
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", return nullptr;
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
} }
/* Find the number of facets */ // Find the number of facets.
char linebuf[100]; char linebuf[100];
while (fgets(linebuf, 100, stl->fp) != NULL) { int num_lines = 1;
/* don't count short lines */ while (fgets(linebuf, 100, fp) != nullptr) {
if (strlen(linebuf) <= 4) continue; // Don't count short lines.
if (strlen(linebuf) <= 4)
/* skip solid/endsolid lines as broken STL file generators may put several of them */ continue;
if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) 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)
++num_lines; continue;
++ num_lines;
} }
rewind(stl->fp); rewind(fp);
/* Get the header */ // Get the header.
for(i = 0; int i = 0;
(i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++); for (; i < 80 && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ;
stl->stats.header[i] = '\0'; /* Lose the '\n' */ stl->stats.header[i] = '\0'; // Lose the '\n'
stl->stats.header[80] = '\0'; stl->stats.header[80] = '\0';
num_facets = num_lines / ASCII_LINES_PER_FACET; num_facets = num_lines / ASCII_LINES_PER_FACET;
} }
stl->stats.number_of_facets += num_facets; stl->stats.number_of_facets += num_facets;
stl->stats.original_num_facets = stl->stats.number_of_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_allocate(stl_file *stl) {
if (stl->error) return;
/* 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 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. */ 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) { static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
stl_facet facet; {
if (stl->stats.type == binary)
if (stl->error) return; fseek(fp, HEADER_SIZE, SEEK_SET);
else
if(stl->stats.type == binary) { rewind(fp);
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
} else {
rewind(stl->fp);
}
char normal_buf[3][32]; char normal_buf[3][32];
for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) { for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) {
if(stl->stats.type == binary) stl_facet facet;
/* Read a single facet from a binary .STL file */
{ if (stl->stats.type == binary) {
/* we assume little-endian architecture! */ // 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) { if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET)
stl->error = 1; return false;
return;
}
#ifndef BOOST_LITTLE_ENDIAN #ifndef BOOST_LITTLE_ENDIAN
// Convert the loaded little endian data to big endian. // Convert the loaded little endian data to big endian.
stl_internal_reverse_quads((char*)&facet, 48); stl_internal_reverse_quads((char*)&facet, 48);
#endif /* BOOST_LITTLE_ENDIAN */ #endif /* BOOST_LITTLE_ENDIAN */
} else } else {
/* Read a single facet from an ASCII .STL file */ // Read a single facet from an ASCII .STL file
{
// skip solid/endsolid // skip solid/endsolid
// (in this order, otherwise it won't work when they are paired in the middle of a file) // (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(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") 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. // 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]); int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
assert(res_normal == 3); assert(res_normal == 3);
int res_outer_loop = fscanf(stl->fp, " outer loop"); int res_outer_loop = fscanf(fp, " outer loop");
assert(res_outer_loop == 0); 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)); 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); 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)); 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); 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)); 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); assert(res_vertex3 == 3);
int res_endloop = fscanf(stl->fp, " endloop"); int res_endloop = fscanf(fp, " endloop");
assert(res_endloop == 0); 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. // 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 "); 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) { 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!"); BOOST_LOG_TRIVIAL(error) << "Something is syntactically very wrong with this ASCII STL! ";
stl->error = 1; return false;
return;
} }
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
@ -335,19 +209,48 @@ void stl_read(stl_file *stl, int first_facet, bool first) {
} }
#endif #endif
/* Write the facet into memory. */ // Write the facet into memory.
stl->facet_start[i] = facet; stl->facet_start[i] = facet;
stl_facet_stats(stl, facet, first); stl_facet_stats(stl, facet, first);
} }
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();
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
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
#endif /* BOOST_LITTLE_ENDIAN */
void stl_allocate(stl_file *stl)
{
// Allocate memory for the entire .STL file.
stl->facet_start.assign(stl->stats.number_of_facets, stl_facet());
// Allocate memory for the neighbors list.
stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors());
}
void stl_reallocate(stl_file *stl)
{
stl->facet_start.resize(stl->stats.number_of_facets);
stl->neighbors_start.resize(stl->stats.number_of_facets);
} }
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)
return;
// While we are going through all of the facets, let's find the // While we are going through all of the facets, let's find the
// maximum and minimum values for x, y, and z // maximum and minimum values for x, y, and z
@ -366,20 +269,3 @@ void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
stl->stats.max = stl->stats.max.cwiseMax(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,36 +25,30 @@
#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);
void
stl_verify_neighbors(stl_file *stl) {
int i;
int j;
stl_edge edge_a;
stl_edge edge_b;
int neighbor;
int vnot;
if (stl->error) return;
stl->stats.backwards_edges = 0; stl->stats.backwards_edges = 0;
for(i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
for(j = 0; j < 3; j++) { for (int j = 0; j < 3; ++ j) {
struct stl_edge {
stl_vertex p1;
stl_vertex p2;
int facet_number;
};
stl_edge edge_a;
edge_a.p1 = stl->facet_start[i].vertex[j]; edge_a.p1 = stl->facet_start[i].vertex[j];
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
neighbor = stl->neighbors_start[i].neighbor[j]; int neighbor = stl->neighbors_start[i].neighbor[j];
vnot = stl->neighbors_start[i].which_vertex_not[j]; if (neighbor == -1)
continue; // this edge has no neighbor... Continue.
if(neighbor == -1) int vnot = stl->neighbors_start[i].which_vertex_not[j];
continue; /* this edge has no neighbor... Continue. */ stl_edge edge_b;
if(vnot < 3) { if (vnot < 3) {
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
} else { } else {
@ -63,9 +57,8 @@ stl_verify_neighbors(stl_file *stl) {
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 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) { if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
/* These edges should match but they don't. Print results. */ // 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", BOOST_LOG_TRIVIAL(info) << "edge " << j << " of facet " << i << " doesn't match edge " << (vnot + 1) << " of facet " << neighbor;
j, i, vnot + 1, neighbor);
stl_write_facet(stl, (char*)"first facet", i); stl_write_facet(stl, (char*)"first facet", i);
stl_write_facet(stl, (char*)"second facet", neighbor); stl_write_facet(stl, (char*)"second facet", neighbor);
} }
@ -75,9 +68,6 @@ stl_verify_neighbors(stl_file *stl) {
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)
return;
stl_vertex new_min(x, y, z); stl_vertex new_min(x, y, z);
stl_vertex shift = new_min - stl->stats.min; stl_vertex shift = new_min - stl->stats.min;
for (int i = 0; i < stl->stats.number_of_facets; ++ i) for (int i = 0; i < stl->stats.number_of_facets; ++ i)
@ -85,29 +75,21 @@ void stl_translate(stl_file *stl, float x, float y, float z)
stl->facet_start[i].vertex[j] += shift; stl->facet_start[i].vertex[j] += shift;
stl->stats.min = new_min; stl->stats.min = new_min;
stl->stats.max += shift; 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)
return;
stl_vertex shift(x, y, z); stl_vertex shift(x, y, z);
for (int i = 0; i < stl->stats.number_of_facets; ++ i) for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j) for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j] += shift; stl->facet_start[i].vertex[j] += shift;
stl->stats.min += shift; stl->stats.min += shift;
stl->stats.max += 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)
return;
// Scale extents. // Scale extents.
auto s = versor.array(); auto s = versor.array();
stl->stats.min.array() *= s; stl->stats.min.array() *= s;
@ -121,99 +103,96 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
for (int i = 0; i < stl->stats.number_of_facets; ++ i) for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j) for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j].array() *= s; 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)
return;
stl_normal normal; stl_normal normal;
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_calculate_normal(normal, &stl->facet_start[i]); stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl->facet_start[i].normal = 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;
x = float(c * xold - s * yold);
y = float(s * xold + c * yold);
}
void stl_rotate_x(stl_file *stl, float angle)
{
double radian_angle = (angle / 180.0) * M_PI; double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle); double c = cos(radian_angle);
double s = sin(radian_angle); double s = sin(radian_angle);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
if (stl->error) return; 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);
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); stl_get_size(stl);
calculate_normals(stl); calculate_normals(stl);
} }
void void stl_rotate_y(stl_file *stl, float angle)
stl_rotate_y(stl_file *stl, float angle) { {
int i;
int j;
double radian_angle = (angle / 180.0) * M_PI; double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle); double c = cos(radian_angle);
double s = sin(radian_angle); double s = sin(radian_angle);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
if (stl->error) return; 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);
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); stl_get_size(stl);
calculate_normals(stl); calculate_normals(stl);
} }
void void stl_rotate_z(stl_file *stl, float angle)
stl_rotate_z(stl_file *stl, float angle) { {
int i;
int j;
double radian_angle = (angle / 180.0) * M_PI; double radian_angle = (angle / 180.0) * M_PI;
double c = cos(radian_angle); double c = cos(radian_angle);
double s = sin(radian_angle); double s = sin(radian_angle);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
if (stl->error) return; 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);
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); stl_get_size(stl);
calculate_normals(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);
}
void its_rotate_y(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(2), v(0), c, s);
}
static void void its_rotate_z(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(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]);
@ -226,14 +205,9 @@ void stl_get_size(stl_file *stl)
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)
for(int i = 0; i < stl->stats.number_of_facets; i++) {
for(int j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j](2) *= -1.0; stl->facet_start[i].vertex[j](2) *= -1.0;
}
}
float temp_size = stl->stats.min(2); float temp_size = stl->stats.min(2);
stl->stats.min(2) = stl->stats.max(2); stl->stats.min(2) = stl->stats.max(2);
stl->stats.max(2) = temp_size; stl->stats.max(2) = temp_size;
@ -245,13 +219,9 @@ void stl_mirror_xy(stl_file *stl)
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++) {
for (int j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j](0) *= -1.0; stl->facet_start[i].vertex[j](0) *= -1.0;
}
}
float temp_size = stl->stats.min(0); float temp_size = stl->stats.min(0);
stl->stats.min(0) = stl->stats.max(0); stl->stats.min(0) = stl->stats.max(0);
stl->stats.max(0) = temp_size; stl->stats.max(0) = temp_size;
@ -263,48 +233,16 @@ void stl_mirror_yz(stl_file *stl)
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)
for (int i = 0; i < stl->stats.number_of_facets; i++) {
for (int j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j](1) *= -1.0; stl->facet_start[i].vertex[j](1) *= -1.0;
}
}
float temp_size = stl->stats.min(1); float temp_size = stl->stats.min(1);
stl->stats.min(1) = stl->stats.max(1); stl->stats.min(1) = stl->stats.max(1);
stl->stats.max(1) = temp_size; stl->stats.max(1) = temp_size;
stl->stats.min(1) *= -1.0; stl->stats.min(1) *= -1.0;
stl->stats.max(1) *= -1.0; stl->stats.max(1) *= -1.0;
stl_reverse_all_facets(stl); stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ 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)
@ -335,123 +273,125 @@ static float get_area(stl_facet *facet)
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 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)
{
stl->stats.volume = get_volume(stl);
if (stl->stats.volume < 0.0) {
stl_reverse_all_facets(stl);
stl->stats.volume = -stl->stats.volume;
}
}
void stl_repair(
stl_file *stl,
bool fixall_flag,
bool exact_flag,
bool tolerance_flag,
float tolerance, float tolerance,
int increment_flag, bool increment_flag,
float increment, float increment,
int nearby_flag, bool nearby_flag,
int iterations, int iterations,
int remove_unconnected_flag, bool remove_unconnected_flag,
int fill_holes_flag, bool fill_holes_flag,
int normal_directions_flag, bool normal_directions_flag,
int normal_values_flag, bool normal_values_flag,
int reverse_all_flag, bool reverse_all_flag,
int verbose_flag) { bool verbose_flag)
{
int i; if (exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) {
int last_edges_fixed = 0;
if (stl->error) return;
if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag
|| fill_holes_flag || normal_directions_flag) {
if (verbose_flag) if (verbose_flag)
printf("Checking exact...\n"); printf("Checking exact...\n");
exact_flag = 1; exact_flag = true;
stl_check_facets_exact(stl); stl_check_facets_exact(stl);
stl->stats.facets_w_1_bad_edge = stl->stats.facets_w_1_bad_edge = (stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_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.connected_facets_3_edge); stl->stats.facets_w_3_bad_edge = (stl->stats.number_of_facets - stl->stats.connected_facets_1_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);
} }
if(nearby_flag || fixall_flag) { if (nearby_flag || fixall_flag) {
if(!tolerance_flag) { if (! tolerance_flag)
tolerance = stl->stats.shortest_edge; tolerance = stl->stats.shortest_edge;
} if (! increment_flag)
if(!increment_flag) {
increment = stl->stats.bounding_diameter / 10000.0; increment = stl->stats.bounding_diameter / 10000.0;
} }
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
for(i = 0; i < iterations; i++) { int last_edges_fixed = 0;
if(stl->stats.connected_facets_3_edge < for (int i = 0; i < iterations; ++ i) {
stl->stats.number_of_facets) { 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);
Checking nearby. Tolerance= %f Iteration=%d of %d...",
tolerance, i + 1, iterations);
stl_check_facets_nearby(stl, tolerance); stl_check_facets_nearby(stl, tolerance);
if (verbose_flag) if (verbose_flag)
printf(" Fixed %d edges.\n", printf(" Fixed %d edges.\n", stl->stats.edges_fixed - last_edges_fixed);
stl->stats.edges_fixed - last_edges_fixed);
last_edges_fixed = stl->stats.edges_fixed; last_edges_fixed = stl->stats.edges_fixed;
tolerance += increment; tolerance += increment;
} else { } else {
if (verbose_flag) if (verbose_flag)
printf("\ printf("All facets connected. No further nearby check necessary.\n");
All facets connected. No further nearby check necessary.\n");
break; break;
} }
} }
} else { } else if (verbose_flag)
if (verbose_flag)
printf("All facets connected. No nearby check necessary.\n"); printf("All facets connected. No nearby check necessary.\n");
}
}
if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { if (remove_unconnected_flag || fixall_flag || fill_holes_flag) {
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag) if (verbose_flag)
printf("Removing unconnected facets...\n"); printf("Removing unconnected facets...\n");
stl_remove_unconnected_facets(stl); stl_remove_unconnected_facets(stl);
} else } else if (verbose_flag)
if (verbose_flag)
printf("No unconnected need to be removed.\n"); printf("No unconnected need to be removed.\n");
} }
if(fill_holes_flag || fixall_flag) { if (fill_holes_flag || fixall_flag) {
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag) if (verbose_flag)
printf("Filling holes...\n"); printf("Filling holes...\n");
stl_fill_holes(stl); stl_fill_holes(stl);
} else } else if (verbose_flag)
if (verbose_flag)
printf("No holes need to be filled.\n"); printf("No holes need to be filled.\n");
} }
if(reverse_all_flag) { if (reverse_all_flag) {
if (verbose_flag) if (verbose_flag)
printf("Reversing all facets...\n"); printf("Reversing all facets...\n");
stl_reverse_all_facets(stl); stl_reverse_all_facets(stl);
} }
if(normal_directions_flag || fixall_flag) { if (normal_directions_flag || fixall_flag) {
if (verbose_flag) if (verbose_flag)
printf("Checking normal directions...\n"); printf("Checking normal directions...\n");
stl_fix_normal_directions(stl); stl_fix_normal_directions(stl);
} }
if(normal_values_flag || fixall_flag) { if (normal_values_flag || fixall_flag) {
if (verbose_flag) if (verbose_flag)
printf("Checking normal values...\n"); printf("Checking normal values...\n");
stl_fix_normal_values(stl); stl_fix_normal_values(stl);
} }
/* Always calculate the volume. It shouldn't take too long */ // Always calculate the volume. It shouldn't take too long.
if (verbose_flag) if (verbose_flag)
printf("Calculating volume...\n"); printf("Calculating volume...\n");
stl_calculate_volume(stl); stl_calculate_volume(stl);
if(exact_flag) { if (exact_flag) {
if (verbose_flag) if (verbose_flag)
printf("Verifying neighbors...\n"); printf("Verifying neighbors...\n");
stl_verify_neighbors(stl); 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,9 +1489,9 @@ namespace Slic3r {
} }
// splits volume out of imported geometry // splits volume out of imported geometry
TriangleMesh triangle_mesh;
stl_file &stl = triangle_mesh.stl;
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
ModelVolume* volume = object.add_volume(TriangleMesh());
stl_file& stl = volume->mesh.stl;
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;
@ -1510,8 +1510,10 @@ 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
@ -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.
@ -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,17 +440,19 @@ 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
@ -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,41 +70,19 @@ 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";
@ -115,15 +90,17 @@ void TriangleMesh::repair()
#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
std::vector<realT> src_vertices;
try 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);