Sharing TriangleMesh objects between the front end (UI) and back end
(background processing)
This commit is contained in:
parent
5fc465b7e8
commit
0bb8ee149e
@ -241,8 +241,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
|
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
|
||||||
std::vector<Model> new_models;
|
std::vector<Model> new_models;
|
||||||
for (auto &model : m_models) {
|
for (auto &model : m_models) {
|
||||||
model.repair();
|
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
|
||||||
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
|
|
||||||
size_t num_objects = model.objects.size();
|
size_t num_objects = model.objects.size();
|
||||||
for (size_t i = 0; i < num_objects; ++ i) {
|
for (size_t i = 0; i < num_objects; ++ i) {
|
||||||
|
|
||||||
@ -301,8 +300,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;
|
||||||
|
@ -59,7 +59,7 @@ struct HashEdge {
|
|||||||
|
|
||||||
// Ensure identical vertex ordering of equal edges.
|
// Ensure identical vertex ordering of equal edges.
|
||||||
// This method is numerically robust.
|
// This method is numerically robust.
|
||||||
if (stl_vertex_lower(*a, *b)) {
|
if (vertex_lower(*a, *b)) {
|
||||||
} else {
|
} else {
|
||||||
// This edge is loaded backwards.
|
// This edge is loaded backwards.
|
||||||
std::swap(a, b);
|
std::swap(a, b);
|
||||||
@ -110,6 +110,12 @@ struct HashEdge {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline bool 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)));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashTableEdges {
|
struct HashTableEdges {
|
||||||
@ -440,7 +446,9 @@ void stl_check_facets_exact(stl_file *stl)
|
|||||||
stl_facet &facet = stl->facet_start[i];
|
stl_facet &facet = stl->facet_start[i];
|
||||||
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
|
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
|
||||||
// Remove the degenerate facet.
|
// Remove the degenerate facet.
|
||||||
facet = stl->facet_start[--stl->stats.number_of_facets];
|
facet = stl->facet_start[-- stl->stats.number_of_facets];
|
||||||
|
stl->facet_start.pop_back();
|
||||||
|
stl->neighbors_start.pop_back();
|
||||||
stl->stats.facets_removed += 1;
|
stl->stats.facets_removed += 1;
|
||||||
stl->stats.degenerate_facets += 1;
|
stl->stats.degenerate_facets += 1;
|
||||||
} else
|
} else
|
||||||
@ -526,23 +534,25 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (facet_number == -- stl->stats.number_of_facets)
|
if (facet_number < -- stl->stats.number_of_facets) {
|
||||||
// Removing the last face is easy, just forget the last face.
|
// Removing a face, which was not the last one.
|
||||||
return;
|
// Copy the face and neighborship from the last face to facet_number.
|
||||||
|
stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets];
|
||||||
// Copy the face and neighborship from the last face to facet_number.
|
neighbors = stl->neighbors_start[stl->stats.number_of_facets];
|
||||||
stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets];
|
// Update neighborship of faces, which used to point to the last face, now moved to facet_number.
|
||||||
neighbors = stl->neighbors_start[stl->stats.number_of_facets];
|
for (int i = 0; i < 3; ++ i)
|
||||||
// Update neighborship of faces, which used to point to the last face, now moved to facet_number.
|
if (neighbors.neighbor[i] != -1) {
|
||||||
for (int i = 0; i < 3; ++ i)
|
int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3];
|
||||||
if (neighbors.neighbor[i] != -1) {
|
if (other_face_idx != stl->stats.number_of_facets) {
|
||||||
int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3];
|
BOOST_LOG_TRIVIAL(info) << "in remove_facet: neighbor = " << other_face_idx << " numfacets = " << stl->stats.number_of_facets << " this is wrong";
|
||||||
if (other_face_idx != stl->stats.number_of_facets) {
|
return;
|
||||||
BOOST_LOG_TRIVIAL(info) << "in remove_facet: neighbor = " << other_face_idx << " numfacets = " << stl->stats.number_of_facets << " this is wrong";
|
}
|
||||||
return;
|
other_face_idx = facet_number;
|
||||||
}
|
}
|
||||||
other_face_idx = facet_number;
|
}
|
||||||
}
|
|
||||||
|
stl->facet_start.pop_back();
|
||||||
|
stl->neighbors_start.pop_back();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto remove_degenerate = [stl, remove_facet](int facet)
|
auto remove_degenerate = [stl, remove_facet](int facet)
|
||||||
|
@ -128,6 +128,8 @@ struct indexed_triangle_set
|
|||||||
void clear() { indices.clear(); vertices.clear(); }
|
void clear() { indices.clear(); vertices.clear(); }
|
||||||
std::vector<stl_triangle_vertex_indices> indices;
|
std::vector<stl_triangle_vertex_indices> indices;
|
||||||
std::vector<stl_vertex> vertices;
|
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 bool stl_open(stl_file *stl, const char *file);
|
||||||
@ -186,7 +188,7 @@ 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)
|
||||||
{
|
{
|
||||||
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();
|
||||||
@ -199,7 +201,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)
|
||||||
{
|
{
|
||||||
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] = (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();
|
||||||
@ -209,6 +211,34 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
|
|||||||
stl_get_size(stl);
|
stl_get_size(stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
|
||||||
|
{
|
||||||
|
for (stl_vertex &v_dst : its.vertices) {
|
||||||
|
stl_vertex &v_dst = face.vertex[i_vertex];
|
||||||
|
stl_vertex v_src = v_dst;
|
||||||
|
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
|
||||||
|
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
||||||
|
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its);
|
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_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_off(const indexed_triangle_set &its, const char *file);
|
||||||
@ -225,10 +255,6 @@ 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, 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_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);
|
||||||
|
@ -147,7 +147,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
|
|||||||
rewind(fp);
|
rewind(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) {
|
||||||
stl_facet facet;
|
stl_facet facet;
|
||||||
|
|
||||||
if (stl->stats.type == binary) {
|
if (stl->stats.type == binary) {
|
||||||
|
@ -108,7 +108,7 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
|
|||||||
static void calculate_normals(stl_file *stl)
|
static void calculate_normals(stl_file *stl)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
@ -1489,10 +1489,10 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// splits volume out of imported geometry
|
// splits volume out of imported geometry
|
||||||
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
|
TriangleMesh triangle_mesh;
|
||||||
ModelVolume* volume = object.add_volume(TriangleMesh());
|
stl_file &stl = triangle_mesh.stl;
|
||||||
stl_file& stl = volume->mesh.stl;
|
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
|
||||||
stl.stats.type = inmemory;
|
stl.stats.type = inmemory;
|
||||||
stl.stats.number_of_facets = (uint32_t)triangles_count;
|
stl.stats.number_of_facets = (uint32_t)triangles_count;
|
||||||
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
|
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
|
||||||
stl_allocate(&stl);
|
stl_allocate(&stl);
|
||||||
@ -1509,9 +1509,11 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
volume->mesh.repair();
|
triangle_mesh.repair();
|
||||||
volume->center_geometry();
|
|
||||||
|
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
|
||||||
|
volume->center_geometry_after_creation();
|
||||||
volume->calculate_convex_hull();
|
volume->calculate_convex_hull();
|
||||||
|
|
||||||
// apply volume's name and config data
|
// apply volume's name and config data
|
||||||
@ -1879,11 +1881,14 @@ 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;
|
||||||
|
|
||||||
volume->mesh.require_shared_vertices();
|
const indexed_triangle_set &its = volume->mesh().its;
|
||||||
|
|
||||||
const indexed_triangle_set &its = volume->mesh.its;
|
|
||||||
if (its.vertices.empty())
|
if (its.vertices.empty())
|
||||||
{
|
{
|
||||||
add_error("Found invalid mesh");
|
add_error("Found invalid mesh");
|
||||||
@ -1916,7 +1921,7 @@ 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());
|
||||||
|
|
||||||
const indexed_triangle_set &its = volume->mesh.its;
|
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;
|
||||||
|
@ -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,10 +925,11 @@ 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()");
|
||||||
volume->mesh.require_shared_vertices();
|
if (! volume->mesh().has_shared_vertices())
|
||||||
const indexed_triangle_set &its = volume->mesh.its;
|
throw std::runtime_error("store_amf() requires shared vertices");
|
||||||
|
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 < its.vertices.size(); ++i) {
|
for (size_t i = 0; i < its.vertices.size(); ++i) {
|
||||||
stream << " <vertex>\n";
|
stream << " <vertex>\n";
|
||||||
@ -955,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 (size_t i = 0; i < (int)volume->mesh.its.indices.size(); ++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.its.indices[i][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";
|
||||||
|
@ -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,7 +691,7 @@ 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();
|
v->center_geometry_after_creation();
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@ -706,7 +700,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 +821,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 +834,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 +848,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 +857,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 +875,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 +889,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;
|
||||||
}
|
}
|
||||||
@ -909,10 +903,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
|
|||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (v->is_model_part()) {
|
if (v->is_model_part()) {
|
||||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||||
const indexed_triangle_set &its = v->mesh.its;
|
const indexed_triangle_set &its = v->mesh().its;
|
||||||
if (its.vertices.empty()) {
|
if (its.vertices.empty()) {
|
||||||
// Using the STL faces.
|
// Using the STL faces.
|
||||||
const stl_file& stl = v->mesh.stl;
|
const stl_file& stl = v->mesh().stl;
|
||||||
for (const stl_facet &facet : stl.facet_start)
|
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>();
|
||||||
@ -1038,6 +1032,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)
|
||||||
@ -1061,14 +1056,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;
|
||||||
}
|
}
|
||||||
@ -1134,11 +1129,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
|
||||||
@ -1157,14 +1153,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||||||
|
|
||||||
if (keep_upper && upper_mesh.facets_count() > 0) {
|
if (keep_upper && upper_mesh.facets_count() > 0) {
|
||||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||||
vol->name = volume->name;
|
vol->name = volume->name;
|
||||||
vol->config = volume->config;
|
vol->config = volume->config;
|
||||||
vol->set_material(volume->material_id(), *volume->material());
|
vol->set_material(volume->material_id(), *volume->material());
|
||||||
}
|
}
|
||||||
if (keep_lower && lower_mesh.facets_count() > 0) {
|
if (keep_lower && lower_mesh.facets_count() > 0) {
|
||||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||||
vol->name = volume->name;
|
vol->name = volume->name;
|
||||||
vol->config = volume->config;
|
vol->config = volume->config;
|
||||||
vol->set_material(volume->material_id(), *volume->material());
|
vol->set_material(volume->material_id(), *volume->material());
|
||||||
|
|
||||||
// Compute the lower part instances' bounding boxes to figure out where to place
|
// Compute the lower part instances' bounding boxes to figure out where to place
|
||||||
@ -1232,7 +1228,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();
|
||||||
|
|
||||||
@ -1259,12 +1255,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.
|
||||||
@ -1294,8 +1284,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
|
|||||||
|
|
||||||
// Adjust the meshes.
|
// Adjust the meshes.
|
||||||
// Transformation to be applied to the meshes.
|
// Transformation to be applied to the meshes.
|
||||||
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
|
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||||
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
|
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
|
||||||
for (ModelVolume *model_volume : this->volumes) {
|
for (ModelVolume *model_volume : this->volumes) {
|
||||||
const Geometry::Transformation volume_trafo = model_volume->get_transformation();
|
const Geometry::Transformation volume_trafo = model_volume->get_transformation();
|
||||||
bool volume_left_handed = volume_trafo.is_left_handed();
|
bool volume_left_handed = volume_trafo.is_left_handed();
|
||||||
@ -1305,7 +1295,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));
|
||||||
@ -1447,7 +1438,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));
|
||||||
@ -1458,7 +1449,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;
|
||||||
@ -1526,30 +1517,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;
|
||||||
@ -1557,7 +1548,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)
|
||||||
@ -1597,7 +1588,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;
|
||||||
@ -1614,7 +1605,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();
|
||||||
@ -1623,7 +1614,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));
|
||||||
@ -1689,24 +1680,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();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
#include "Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -261,6 +263,7 @@ public:
|
|||||||
void rotate(double angle, const Vec3d& axis);
|
void rotate(double angle, const Vec3d& axis);
|
||||||
void mirror(Axis axis);
|
void mirror(Axis axis);
|
||||||
|
|
||||||
|
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||||
void scale_mesh(const Vec3d& versor);
|
void scale_mesh(const Vec3d& versor);
|
||||||
|
|
||||||
size_t materials_count() const;
|
size_t materials_count() const;
|
||||||
@ -268,7 +271,6 @@ public:
|
|||||||
bool needed_repair() const;
|
bool needed_repair() const;
|
||||||
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
|
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
|
||||||
void split(ModelObjectPtrs* new_objects);
|
void split(ModelObjectPtrs* new_objects);
|
||||||
void repair();
|
|
||||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||||
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
||||||
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
||||||
@ -340,7 +342,12 @@ class ModelVolume : public ModelBase
|
|||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
// The triangular model.
|
// The triangular model.
|
||||||
TriangleMesh mesh;
|
const TriangleMesh& mesh() const { return *m_mesh.get(); }
|
||||||
|
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
|
||||||
|
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
|
||||||
|
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
|
||||||
|
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
||||||
|
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
|
||||||
// Configuration parameters specific to an object model geometry or a modifier volume,
|
// Configuration parameters specific to an object model geometry or a modifier volume,
|
||||||
// overriding the global Slic3r settings and the ModelObject settings.
|
// overriding the global Slic3r settings and the ModelObject settings.
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
@ -377,13 +384,16 @@ public:
|
|||||||
void rotate(double angle, const Vec3d& axis);
|
void rotate(double angle, const Vec3d& axis);
|
||||||
void mirror(Axis axis);
|
void mirror(Axis axis);
|
||||||
|
|
||||||
|
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||||
void scale_geometry(const Vec3d& versor);
|
void scale_geometry(const Vec3d& versor);
|
||||||
|
|
||||||
// translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box
|
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
|
||||||
void center_geometry();
|
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
|
||||||
|
void center_geometry_after_creation();
|
||||||
|
|
||||||
void calculate_convex_hull();
|
void calculate_convex_hull();
|
||||||
const TriangleMesh& get_convex_hull() const;
|
const TriangleMesh& get_convex_hull() const;
|
||||||
|
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
|
||||||
// Get count of errors in the mesh
|
// Get count of errors in the mesh
|
||||||
int get_mesh_errors_count() const;
|
int get_mesh_errors_count() const;
|
||||||
|
|
||||||
@ -430,18 +440,20 @@ protected:
|
|||||||
|
|
||||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||||
void set_model_object(ModelObject *model_object) { object = model_object; }
|
void set_model_object(ModelObject *model_object) { object = model_object; }
|
||||||
void transform_mesh(const Transform3d& t, bool fix_left_handed);
|
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
|
||||||
void transform_mesh(const Matrix3d& m, bool fix_left_handed);
|
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Parent object owning this ModelVolume.
|
// Parent object owning this ModelVolume.
|
||||||
ModelObject* object;
|
ModelObject* object;
|
||||||
|
// The triangular model.
|
||||||
|
std::shared_ptr<TriangleMesh> m_mesh;
|
||||||
// Is it an object to be printed, or a modifier volume?
|
// Is it an object to be printed, or a modifier volume?
|
||||||
ModelVolumeType m_type;
|
ModelVolumeType m_type;
|
||||||
t_model_material_id m_material_id;
|
t_model_material_id m_material_id;
|
||||||
// The convex hull of this model's mesh.
|
// The convex hull of this model's mesh.
|
||||||
TriangleMesh m_convex_hull;
|
std::shared_ptr<TriangleMesh> m_convex_hull;
|
||||||
Geometry::Transformation m_transformation;
|
Geometry::Transformation m_transformation;
|
||||||
|
|
||||||
// flag to optimize the checking if the volume is splittable
|
// flag to optimize the checking if the volume is splittable
|
||||||
// -1 -> is unknown value (before first cheking)
|
// -1 -> is unknown value (before first cheking)
|
||||||
@ -449,24 +461,24 @@ private:
|
|||||||
// 1 -> is splittable
|
// 1 -> is splittable
|
||||||
mutable int m_is_splittable{ -1 };
|
mutable int m_is_splittable{ -1 };
|
||||||
|
|
||||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object)
|
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
|
||||||
{
|
{
|
||||||
if (mesh.stl.stats.number_of_facets > 1)
|
if (mesh.stl.stats.number_of_facets > 1)
|
||||||
calculate_convex_hull();
|
calculate_convex_hull();
|
||||||
}
|
}
|
||||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
|
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
|
||||||
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {}
|
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
|
||||||
|
|
||||||
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||||
ModelBase(other), // copy the ID
|
ModelBase(other), // copy the ID
|
||||||
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||||
{
|
{
|
||||||
this->set_material_id(other.material_id());
|
this->set_material_id(other.material_id());
|
||||||
}
|
}
|
||||||
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||||
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||||
{
|
{
|
||||||
this->set_material_id(other.material_id());
|
this->set_material_id(other.material_id());
|
||||||
if (mesh.stl.stats.number_of_facets > 1)
|
if (mesh.stl.stats.number_of_facets > 1)
|
||||||
@ -597,10 +609,6 @@ public:
|
|||||||
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
|
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
|
||||||
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
|
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
|
||||||
|
|
||||||
/// Repair the ModelObjects of the current Model.
|
|
||||||
/// This function calls repair function on each TriangleMesh of each model object volume
|
|
||||||
void repair();
|
|
||||||
|
|
||||||
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
|
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
|
||||||
ModelObject* add_object();
|
ModelObject* add_object();
|
||||||
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
|
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -82,11 +82,14 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
|
|||||||
|
|
||||||
// #define SLIC3R_TRACE_REPAIR
|
// #define SLIC3R_TRACE_REPAIR
|
||||||
|
|
||||||
void TriangleMesh::repair()
|
void TriangleMesh::repair(bool update_shared_vertices)
|
||||||
{
|
{
|
||||||
if (this->repaired)
|
if (this->repaired) {
|
||||||
|
if (update_shared_vertices)
|
||||||
|
this->require_shared_vertices();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// admesh fails when repairing empty meshes
|
// admesh fails when repairing empty meshes
|
||||||
if (this->stl.stats.number_of_facets == 0)
|
if (this->stl.stats.number_of_facets == 0)
|
||||||
return;
|
return;
|
||||||
@ -97,6 +100,7 @@ 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));
|
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);
|
||||||
@ -179,6 +183,12 @@ void TriangleMesh::repair()
|
|||||||
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()
|
||||||
@ -238,8 +248,7 @@ 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);
|
its_write_obj(this->its, output_file);
|
||||||
its_write_obj(its, output_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::scale(float factor)
|
void TriangleMesh::scale(float factor)
|
||||||
@ -294,6 +303,7 @@ 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)
|
||||||
@ -311,22 +321,26 @@ void TriangleMesh::mirror(const Axis &axis)
|
|||||||
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);
|
||||||
this->its.clear();
|
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);
|
||||||
this->its.clear();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +496,6 @@ 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->its.vertices.size());
|
pp.reserve(this->its.vertices.size());
|
||||||
for (size_t i = 0; i < this->its.vertices.size(); ++ i) {
|
for (size_t i = 0; i < this->its.vertices.size(); ++ i) {
|
||||||
@ -519,26 +532,32 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
|
|||||||
|
|
||||||
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:
|
|
||||||
for (const stl_facet &facet : stl.facet_start)
|
|
||||||
for (int i = 0; i < 3; ++ i) {
|
|
||||||
const stl_vertex& v = facet.vertex[i];
|
|
||||||
src_vertices.emplace_back(v(0), v(1), v(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The qhull call:
|
// The qhull call:
|
||||||
orgQhull::Qhull qhull;
|
orgQhull::Qhull qhull;
|
||||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||||
try
|
std::vector<realT> src_vertices;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
|
if (this->has_shared_vertices()) {
|
||||||
|
#if REALfloat
|
||||||
|
qhull.runQhull("", 3, (int)this->its.vertices.size() / 3, (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 (...)
|
||||||
{
|
{
|
||||||
@ -566,7 +585,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ public:
|
|||||||
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
|
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
|
||||||
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
|
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
|
||||||
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
|
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
|
||||||
void repair();
|
void repair(bool update_shared_vertices = true);
|
||||||
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; }
|
||||||
|
@ -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());
|
||||||
|
@ -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 = 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; }
|
||||||
|
@ -5498,7 +5498,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
|
||||||
|
@ -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") ?
|
||||||
|
@ -388,8 +388,7 @@ void GLGizmoSlaSupports::update_mesh()
|
|||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
// 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;
|
m_its = &m_mesh->its;
|
||||||
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;
|
||||||
|
@ -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>());
|
||||||
}
|
}
|
||||||
|
@ -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(); %};
|
||||||
|
Loading…
Reference in New Issue
Block a user