Eradicated admesh from TriangleMesh:

TriangleMesh newly only holds indexed_triangle_set and
TriangleMeshStats. TriangleMeshStats contains an excerpt of stl_stats.
TriangleMeshStats are updated when initializing with indexed_triangle_set.

Admesh triangle mesh fixing is newly only used when loading an STL.
AMF / 3MF / OBJ file formats are already indexed triangle sets, thus
they are no more converted to admesh stl_file format, nor fixed
through admesh repair machinery. When importing AMF / 3MF / OBJ files,
volume is calculated and if negative, all faces are flipped. Also
a bounding box and number of open edges is calculated.

Implemented its_number_of_patches(), its_num_open_edges()
Optimized its_split(), its_is_splittable() using a visitor pattern.

Reworked QHull integration into TriangleMesh:
    1) Face normals were not right.
    2) Indexed triangle set is newly emitted instead of duplicating
       vertices for each face.

Fixed cut_mesh(): Orient the triangulated faces correctly.
This commit is contained in:
Vojtech Bubnik 2021-09-20 17:12:22 +02:00
parent f484953a5a
commit 8a2a9dba2f
59 changed files with 1056 additions and 1758 deletions

View File

@ -139,7 +139,6 @@ sub mesh {
my $mesh = Slic3r::TriangleMesh->new;
$mesh->ReadFromPerl($vertices, $facets);
$mesh->repair;
$mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz};
$mesh->translate(@{$params{translate}}) if $params{translate};
return $mesh;

View File

@ -212,8 +212,7 @@ int main(const int argc, const char *argv[])
return -1;
}
mesh.repair();
if (mesh.facets_count() == 0) {
if (mesh.empty()) {
std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
return -1;
}

View File

@ -24,7 +24,6 @@ int main(const int argc, const char * argv[])
TriangleMesh input;
input.ReadSTLFile(argv[1]);
input.repair();
Benchmark bench;

View File

@ -409,7 +409,6 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@ -417,14 +416,12 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>();
mshinst.translate(-center);
mshinst.require_shared_vertices();
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
m_csgsettings.get_convexity());
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
holemesh.require_shared_vertices();
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
}
}

View File

@ -43,7 +43,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@ -51,15 +50,11 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>();
mshinst.translate(-center);
mshinst.require_shared_vertices();
add_mesh(mshinst);
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
holemesh.require_shared_vertices();
add_mesh(holemesh);
}
for (const sla::DrainHole &holept : holedata)
add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
}
repaint();

View File

@ -397,7 +397,7 @@ int CLI::run(int argc, char **argv)
TriangleMesh mesh = model.mesh();
mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
std::vector<TriangleMesh> meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
size_t i = 0;
for (TriangleMesh* m : meshes) {
Model out;

View File

@ -239,6 +239,7 @@ private:
return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b;
}
// Connect edge_a with edge_b, update edge connection statistics.
static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)
{
// Facet a's neighbor is facet b
@ -249,7 +250,7 @@ private:
stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */
if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) {
if ((edge_a.which_edge < 3 && edge_b.which_edge < 3) || (edge_a.which_edge > 2 && edge_b.which_edge > 2)) {
// These facets are oriented in opposite directions, their normals are probably messed up.
stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3;
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3;
@ -479,12 +480,13 @@ void stl_check_facets_exact(stl_file *stl)
void stl_check_facets_nearby(stl_file *stl, float tolerance)
{
if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
&& (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
&& (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
assert(stl->stats.connected_facets_3_edge <= stl->stats.connected_facets_2_edge);
assert(stl->stats.connected_facets_2_edge <= stl->stats.connected_facets_1_edge);
assert(stl->stats.connected_facets_1_edge <= stl->stats.number_of_facets);
if (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)
// No need to check any further. All facets are connected.
return;
}
HashTableEdges hash_table(stl->stats.number_of_facets);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
@ -514,22 +516,12 @@ void stl_remove_unconnected_facets(stl_file *stl)
/* Update list of connected edges */
stl_neighbors &neighbors = stl->neighbors_start[facet_number];
// Update statistics on unconnected triangle edges.
switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) {
case 0: // Facet has 3 neighbors
-- stl->stats.connected_facets_3_edge;
-- stl->stats.connected_facets_2_edge;
-- stl->stats.connected_facets_1_edge;
break;
case 1: // Facet has 2 neighbors
-- stl->stats.connected_facets_2_edge;
-- stl->stats.connected_facets_1_edge;
break;
case 2: // Facet has 1 neighbor
-- stl->stats.connected_facets_1_edge;
case 3: // Facet has 0 neighbors
break;
default:
assert(false);
switch (neighbors.num_neighbors()) {
case 3: -- stl->stats.connected_facets_3_edge; // fall through
case 2: -- stl->stats.connected_facets_2_edge; // fall through
case 1: -- stl->stats.connected_facets_1_edge; // fall through
case 0: break;
default: assert(false);
}
if (facet_number < int(-- stl->stats.number_of_facets)) {
@ -555,20 +547,14 @@ void stl_remove_unconnected_facets(stl_file *stl)
auto remove_degenerate = [stl, remove_facet](int facet)
{
// Update statistics on face connectivity.
auto stl_update_connects_remove_1 = [stl](int facet_num) {
//FIXME when decreasing 3_edge, should I increase 2_edge etc?
switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) {
case 0: // Facet has 3 neighbors
-- stl->stats.connected_facets_3_edge; break;
case 1: // Facet has 2 neighbors
-- stl->stats.connected_facets_2_edge; break;
case 2: // Facet has 1 neighbor
-- stl->stats.connected_facets_1_edge; break;
case 3: // Facet has 0 neighbors
break;
default:
assert(false);
// Update statistics on face connectivity after one edge was disconnected on the facet "facet_num".
auto update_connects_remove_1 = [stl](int facet_num) {
switch (stl->neighbors_start[facet_num].num_neighbors()) {
case 0: assert(false); break;
case 1: -- stl->stats.connected_facets_1_edge; break;
case 2: -- stl->stats.connected_facets_2_edge; break;
case 3: -- stl->stats.connected_facets_3_edge; break;
default: assert(false);
}
};
@ -604,9 +590,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
// Update statistics on edge connectivity.
if ((neighbor[0] == -1) && (neighbor[1] != -1))
stl_update_connects_remove_1(neighbor[1]);
update_connects_remove_1(neighbor[1]);
if ((neighbor[1] == -1) && (neighbor[0] != -1))
stl_update_connects_remove_1(neighbor[0]);
update_connects_remove_1(neighbor[0]);
if (neighbor[0] >= 0) {
if (neighbor[1] >= 0) {
@ -634,7 +620,7 @@ void stl_remove_unconnected_facets(stl_file *stl)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
}
if (neighbor[2] >= 0) {
stl_update_connects_remove_1(neighbor[2]);
update_connects_remove_1(neighbor[2]);
stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
}
@ -652,11 +638,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
++ i;
if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) {
// remove completely unconnected facets
// There are some faces with no connected edge at all. Remove completely unconnected facets.
for (uint32_t i = 0; i < stl->stats.number_of_facets;)
if (stl->neighbors_start[i].neighbor[0] == -1 &&
stl->neighbors_start[i].neighbor[1] == -1 &&
stl->neighbors_start[i].neighbor[2] == -1) {
if (stl->neighbors_start[i].num_neighbors() == 0) {
// This facet is completely unconnected. Remove it.
remove_facet(i);
assert(stl_validate(stl));

View File

@ -79,8 +79,7 @@ struct stl_neighbors {
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(); }
int num_neighbors() const { return 3 - ((this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
// Index of a neighbor facet.
int neighbor[3];
@ -92,28 +91,44 @@ struct stl_stats {
stl_stats() { memset(&header, 0, 81); }
char header[81];
stl_type type = (stl_type)0;
// Should always match the number of facets stored inside stl_file::facet_start.
uint32_t number_of_facets = 0;
// Bounding box.
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float bounding_diameter = 0.f;
float shortest_edge = 0.f;
// After repair, the volume shall always be positive.
float volume = -1.f;
// Number of face edges connected to another face.
// Don't use this statistics after repair, use the connected_facets_1/2/3_edge instead!
int connected_edges = 0;
// Faces with >=1, >=2 and 3 edges connected to another face.
int connected_facets_1_edge = 0;
int connected_facets_2_edge = 0;
int connected_facets_3_edge = 0;
// Faces with 1, 2 and 3 open edges after exact chaining, but before repair.
int facets_w_1_bad_edge = 0;
int facets_w_2_bad_edge = 0;
int facets_w_3_bad_edge = 0;
// Number of faces read form an STL file.
int original_num_facets = 0;
// Number of edges connected one to another by snapping their end vertices.
int edges_fixed = 0;
// Number of faces removed because they were degenerated.
int degenerate_facets = 0;
// Total number of facets removed: Degenerate faces and unconnected faces.
int facets_removed = 0;
// Number of faces added by hole filling.
int facets_added = 0;
// Number of faces reversed because of negative volume or because one patch was connected to another patch with incompatible normals.
int facets_reversed = 0;
// Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
int backwards_edges = 0;
// Number of triangles, which were flipped during the fixing process.
int normals_fixed = 0;
// Number of connected triangle patches.
int number_of_parts = 0;
void clear() { *this = stl_stats(); }
@ -135,13 +150,11 @@ struct stl_file {
std::vector<stl_facet> facet_start;
std::vector<stl_neighbors> neighbors_start;
// Statistics
stl_stats stats;
stl_stats stats;
};
struct indexed_triangle_set
{
indexed_triangle_set() {}
void clear() { indices.clear(); vertices.clear(); }
size_t memsize() const {
@ -149,9 +162,7 @@ struct indexed_triangle_set
}
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
std::vector<stl_vertex> vertices;
bool empty() const { return indices.empty() || vertices.empty(); }
};

View File

@ -205,11 +205,12 @@ bool stl_write_quad_object(stl_file *stl, char *file)
fprintf(fp, "CQUAD\n");
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
switch (stl->neighbors_start[i].num_neighbors_missing()) {
case 0: color = connect_color; break;
case 1: color = uncon_1_color; break;
case 2: color = uncon_2_color; break;
default: color = uncon_3_color;
switch (stl->neighbors_start[i].num_neighbors()) {
case 0:
default: color = uncon_3_color; break;
case 1: color = uncon_2_color; break;
case 2: color = uncon_1_color; break;
case 3: color = connect_color; break;
}
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));

View File

@ -305,8 +305,8 @@ namespace Slic3r {
struct Geometry
{
std::vector<float> vertices;
std::vector<unsigned int> triangles;
std::vector<Vec3f> vertices;
std::vector<Vec3i> triangles;
std::vector<std::string> custom_supports;
std::vector<std::string> custom_seam;
std::vector<std::string> mmu_segmentation;
@ -720,7 +720,7 @@ namespace Slic3r {
}
// use the geometry to create the volumes in the new model objects
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() / 3 - 1 });
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
// for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry
@ -793,7 +793,7 @@ namespace Slic3r {
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() / 3 - 1);
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
// select as volumes
volumes_ptr = &volumes;
@ -1559,9 +1559,10 @@ namespace Slic3r {
{
// appends the vertex coordinates
// missing values are set equal to ZERO
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
m_curr_object.geometry.vertices.emplace_back(
m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
return true;
}
@ -1595,9 +1596,10 @@ namespace Slic3r {
// appends the triangle's vertices indices
// missing values are set equal to ZERO
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.triangles.emplace_back(
get_attribute_value_int(attributes, num_attributes, V1_ATTR),
get_attribute_value_int(attributes, num_attributes, V2_ATTR),
get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
@ -1886,7 +1888,7 @@ namespace Slic3r {
return false;
}
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
unsigned int renamed_volumes_count = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
@ -1897,77 +1899,50 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = false;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
break;
}
}
// 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;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)triangles_count;
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
stl_allocate(&stl);
unsigned int src_start_id = volume_data.first_triangle_id * 3;
for (unsigned int i = 0; i < triangles_count; ++i) {
unsigned int ii = i * 3;
stl_facet& facet = stl.facet_start[i];
for (unsigned int v = 0; v < 3; ++v) {
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
if (tri_id + 2 >= geometry.vertices.size()) {
add_error("Malformed triangle mesh");
std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
const size_t triangles_count = faces.size();
for (Vec3i face : faces)
for (unsigned int tri_id : face)
if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
add_error("Found invalid vertex id");
return false;
}
facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
}
}
stl_get_size(&stl);
triangle_mesh.repair();
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
// This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
stl.stats.facets_reversed = 0;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
TriangleMesh triangle_mesh(std::move(geometry.vertices), std::move(faces));
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix());
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
volume->calculate_convex_hull();
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
for (unsigned i=0; i<triangles_count; ++i) {
size_t index = src_start_id/3 + i;
for (size_t i=0; i<triangles_count; ++i) {
size_t index = volume_data.first_triangle_id + i;
assert(index < geometry.custom_supports.size());
assert(index < geometry.custom_seam.size());
assert(index < geometry.mmu_segmentation.size());
@ -2543,11 +2518,6 @@ namespace Slic3r {
if (volume == nullptr)
continue;
if (!volume->mesh().repaired)
throw Slic3r::FileIOError("store_3mf() requires repair()");
if (!volume->mesh().has_shared_vertices())
throw Slic3r::FileIOError("store_3mf() requires shared vertices");
volumes_offsets.insert({ volume, Offsets(vertices_count) });
const indexed_triangle_set &its = volume->mesh().its;
@ -2588,10 +2558,7 @@ namespace Slic3r {
if (volume == nullptr)
continue;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end());
@ -2606,7 +2573,6 @@ namespace Slic3r {
{
const Vec3i &idx = its.indices[i];
char *ptr = buf;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
@ -2614,15 +2580,6 @@ namespace Slic3r {
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
#else
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
"\" v3=\"" << boost::spirit::int_ << "\"",
idx[0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[2] + volume_it->second.first_vertex_id);
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
*ptr = '\0';
output_buffer += buf;
}

View File

@ -244,11 +244,11 @@ struct AMFParserContext
// Map from obect name to object idx & instances.
std::map<std::string, Object> m_object_instances_map;
// Vertices parsed for the current m_object.
std::vector<float> m_object_vertices;
std::vector<Vec3f> m_object_vertices;
// Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume *m_volume { nullptr };
// Faces collected for the current m_volume.
std::vector<int> m_volume_facets;
std::vector<Vec3i> m_volume_facets;
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform;
// Current material allocated for an amf/metadata subtree.
@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX:
assert(m_object);
// Parse the vertex data
m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[1].c_str())));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Faces of the current volume:
case NODE_TYPE_TRIANGLE:
assert(m_object && m_volume);
m_volume_facets.emplace_back(atoi(m_value[0].c_str()));
m_volume_facets.emplace_back(atoi(m_value[1].c_str()));
m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
m_volume_facets.emplace_back(atoi(m_value[0].c_str()), atoi(m_value[1].c_str()), atoi(m_value[2].c_str()));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@ -621,44 +617,36 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME:
{
assert(m_object && m_volume);
TriangleMesh mesh;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = m_volume_facets[i++] * 3;
if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) {
// Verify validity of face indices.
for (Vec3i face : m_volume_facets)
for (unsigned int tri_id : face)
if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
this->stop("Malformed triangle mesh");
return;
}
facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
}
}
stl_get_size(&stl);
mesh.repair();
m_volume->set_mesh(std::move(mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
{
TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
m_volume->set_mesh(std::move(triangle_mesh));
}
// stores the volume matrix taken from the metadata, if present
if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation();
}
else
} else
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_object_vertices.clear();
m_volume = nullptr;
break;
}
@ -1187,10 +1175,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
int num_vertices = 0;
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh().repaired)
throw Slic3r::FileIOError("store_amf() requires repair()");
if (! volume->mesh().has_shared_vertices())
throw Slic3r::FileIOError("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {

View File

@ -19,7 +19,8 @@ namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr)
{
if(meshptr == nullptr) return false;
if (meshptr == nullptr)
return false;
// Parse the OBJ file.
ObjParser::ObjData data;
@ -31,84 +32,69 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
// Count the faces and verify, that all faces are triangular.
size_t num_faces = 0;
size_t num_quads = 0;
for (size_t i = 0; i < data.vertices.size(); ) {
for (size_t i = 0; i < data.vertices.size(); ++ i) {
// Find the end of face.
size_t j = i;
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
if (i == j)
continue;
size_t face_vertices = j - i;
if (face_vertices != 3 && face_vertices != 4) {
// Non-triangular and non-quad faces are not supported as of now.
return false;
if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
if (num_face_vertices > 4) {
// Non-triangular and non-quad faces are not supported as of now.
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
return false;
} else if (num_face_vertices < 3) {
// Non-triangular and non-quad faces are not supported as of now.
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
return false;
}
if (num_face_vertices == 4)
++ num_quads;
++ num_faces;
i = j;
}
if (face_vertices == 4)
++ num_quads;
++ num_faces;
i = j + 1;
}
// Convert ObjData into STL.
TriangleMesh &mesh = *meshptr;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = uint32_t(num_faces + num_quads);
stl.stats.original_num_facets = int(num_faces + num_quads);
// stl_allocate clears all the allocated data to zero, all normals are set to zeros as well.
stl_allocate(&stl);
size_t i_face = 0;
for (size_t i = 0; i < data.vertices.size(); ++ i) {
if (data.vertices[i].coordIdx == -1)
continue;
stl_facet &facet = stl.facet_start[i_face ++];
size_t num_normals = 0;
stl_normal normal(stl_normal::Zero());
for (unsigned int v = 0; v < 3; ++ v) {
const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
normal(0) += data.normals[vertex.normalIdx*3];
normal(1) += data.normals[vertex.normalIdx*3+1];
normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
}
// Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
// This is a quad. Produce the other triangle.
stl_facet &facet2 = stl.facet_start[i_face++];
facet2.vertex[0] = facet.vertex[0];
facet2.vertex[1] = facet.vertex[2];
const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
normal(0) += data.normals[vertex.normalIdx*3];
normal(1) += data.normals[vertex.normalIdx*3+1];
normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
if (num_normals == 4) {
// Normalize an average normal of a quad.
float len = facet.normal.norm();
if (len > EPSILON) {
normal /= len;
facet.normal = normal;
facet2.normal = normal;
}
}
} else if (num_normals == 3) {
// Normalize an average normal of a triangle.
float len = facet.normal.norm();
if (len > EPSILON)
facet.normal = normal / len;
}
// Convert ObjData into indexed triangle set.
indexed_triangle_set its;
size_t num_vertices = data.coordinates.size() / 4;
its.vertices.reserve(num_vertices);
its.indices.reserve(num_faces + num_quads);
for (size_t i = 0; i < num_vertices; ++ i) {
size_t j = i << 2;
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
}
stl_get_size(&stl);
mesh.repair();
if (mesh.facets_count() == 0) {
int indices[4];
for (size_t i = 0; i < data.vertices.size();)
if (data.vertices[i].coordIdx == -1)
++ i;
else {
int cnt = 0;
while (i < data.vertices.size())
if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
break;
} else {
assert(cnt < 4);
if (vertex.coordIdx < 0 || vertex.coordIdx >= its.vertices.size()) {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
return false;
}
indices[cnt ++] = vertex.coordIdx;
}
if (cnt) {
assert(cnt == 3 || cnt == 4);
// Insert one or two faces (triangulate a quad).
its.indices.emplace_back(indices[0], indices[1], indices[2]);
if (cnt == 4)
its.indices.emplace_back(indices[0], indices[2], indices[3]);
}
}
*meshptr = TriangleMesh(std::move(its));
if (meshptr->empty()) {
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false;
}
if (meshptr->volume() < 0)
meshptr->flip_triangles();
return true;
}

View File

@ -1,333 +0,0 @@
#include <string.h>
#include <exception>
#include <boost/algorithm/string.hpp>
#include "miniz_extension.hpp"
#include <Eigen/Geometry>
#include "../libslic3r.h"
#include "../Model.hpp"
#include "PRUS.hpp"
#if 0
// Enable debugging and assert in this file.
#define DEBUG
#define _DEBUG
#undef NDEBUG
#endif
#include <assert.h>
namespace Slic3r
{
struct StlHeader
{
char comment[80];
uint32_t nTriangles;
};
static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
// Buffered line reader to a string buffer.
class LineReader
{
public:
LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {}
const char* next_line() {
// Skip empty lines.
while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
++ m_pos;
if (m_pos == m_len) {
// End of file.
return nullptr;
}
// The buffer is nonempty and it does not start with end of lines. Find the first end of line.
int end = m_pos + 1;
while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
++ end;
char *ptr_out = m_buffer.data() + m_pos;
m_pos = end + 1;
m_buffer[end] = 0;
return ptr_out;
}
int next_line_scanf(const char *format, ...)
{
const char *line = next_line();
if (line == nullptr)
return -1;
int result;
va_list arglist;
va_start(arglist, format);
result = vsscanf(line, format, arglist);
va_end(arglist);
return result;
}
private:
std::vector<char> &m_buffer;
int m_pos;
int m_len;
};
static void extract_model_from_archive(
// name of the model file
const char *name,
// path to the archive
const char *path,
// content of scene.xml
const std::vector<char> &scene_xml_data,
// loaded data of this STL
std::vector<char> &data,
// Model, to which the newly loaded objects will be added
Model *model,
// To map multiple STLs into a single model object for multi-material prints.
std::map<int, ModelObject*> &group_to_model_object)
{
// Find the model entry in the XML data.
char model_name_tag[1024];
sprintf(model_name_tag, "<model name=\"%s\">", name);
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
const char *zero_tag = "<zero>";
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
Vec3d instance_rotation = Vec3d::Zero();
Vec3d instance_scaling_factor = Vec3d::Ones();
Vec3d instance_offset = Vec3d::Zero();
bool trafo_set = false;
unsigned int group_id = (unsigned int)-1;
unsigned int extruder_id = (unsigned int)-1;
ModelObject *model_object = nullptr;
if (model_xml != nullptr) {
model_xml += strlen(model_name_tag);
const char *position_tag = "<position>";
const char *position_xml = strstr(model_xml, position_tag);
const char *rotation_tag = "<rotation>";
const char *rotation_xml = strstr(model_xml, rotation_tag);
const char *scale_tag = "<scale>";
const char *scale_xml = strstr(model_xml, scale_tag);
float position[3], rotation[3], scale[3], zero[3];
if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
sscanf(position_xml+strlen(position_tag),
"[%f, %f, %f]", position, position+1, position+2) == 3 &&
sscanf(rotation_xml+strlen(rotation_tag),
"[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
sscanf(scale_xml+strlen(scale_tag),
"[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
sscanf(zero_xml+strlen(zero_tag),
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
trafo_set = true;
}
const char *group_tag = "<group>";
const char *group_xml = strstr(model_xml, group_tag);
const char *extruder_tag = "<extruder>";
const char *extruder_xml = strstr(model_xml, extruder_tag);
if (group_xml != nullptr) {
int group = atoi(group_xml + strlen(group_tag));
if (group > 0) {
group_id = group;
auto it = group_to_model_object.find(group_id);
if (it != group_to_model_object.end())
model_object = it->second;
}
}
if (extruder_xml != nullptr) {
int e = atoi(extruder_xml + strlen(extruder_tag));
if (e > 0)
extruder_id = e;
}
}
if (! trafo_set)
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
// Extract the STL.
StlHeader header;
TriangleMesh mesh;
bool mesh_valid = false;
bool stl_ascii = false;
if (data.size() > sizeof(StlHeader)) {
memcpy((char*)&header, data.data(), sizeof(StlHeader));
if (strncmp(header.comment, "solid ", 6) == 0)
stl_ascii = true;
else {
// Header has been extracted. Now read the faces.
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = header.nTriangles;
stl.stats.original_num_facets = header.nTriangles;
stl_allocate(&stl);
if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
unsigned char *data = (unsigned char*)stl.facet_start.data();
for (size_t i = header.nTriangles - 1; i > 0; -- i)
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
}
// All the faces have been read.
stl_get_size(&stl);
mesh.repair();
if (std::abs(stl.stats.min(2)) < EPSILON)
stl.stats.min(2) = 0.;
// Add a mesh to a model.
if (mesh.facets_count() > 0)
mesh_valid = true;
}
}
} else
stl_ascii = true;
if (stl_ascii) {
// Try to parse ASCII STL.
char normal_buf[3][32];
stl_facet facet;
std::vector<stl_facet> facets;
LineReader line_reader(data);
std::string solid_name;
facet.extra[0] = facet.extra[1] = 0;
for (;;) {
const char *line = line_reader.next_line();
if (line == nullptr)
// End of file.
break;
if (strncmp(line, "solid", 5) == 0) {
// Opening the "solid" block.
if (! solid_name.empty()) {
// Error, solid block is already open.
facets.clear();
break;
}
solid_name = line + 5;
if (solid_name.empty())
solid_name = "unknown";
continue;
}
if (strncmp(line, "endsolid", 8) == 0) {
// Closing the "solid" block.
if (solid_name.empty()) {
// Error, no solid block is open.
facets.clear();
break;
}
solid_name.clear();
continue;
}
// Line has to start with the word solid.
int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
assert(res_normal == 3);
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
assert(res_outer_loop == 0);
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3);
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3);
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3);
int res_endloop = line_reader.next_line_scanf(" endloop");
assert(res_endloop == 0);
int res_endfacet = line_reader.next_line_scanf(" endfacet");
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
// perror("Something is syntactically very wrong with this ASCII STL!");
facets.clear();
break;
}
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it.
facet.normal = stl_normal::Zero();
}
facets.emplace_back(facet);
}
if (! facets.empty() && solid_name.empty()) {
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)facets.size();
stl.stats.original_num_facets = (int)facets.size();
stl_allocate(&stl);
memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
stl_get_size(&stl);
mesh.repair();
// Add a mesh to a model.
if (mesh.facets_count() > 0)
mesh_valid = true;
}
}
if (! mesh_valid)
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
// Add this mesh to the model.
ModelVolume *volume = nullptr;
if (model_object == nullptr) {
// This is a first mesh of a group. Create a new object & volume.
model_object = model->add_object(name, path, std::move(mesh));
volume = model_object->volumes.front();
ModelInstance *instance = model_object->add_instance();
instance->set_rotation(instance_rotation);
instance->set_scaling_factor(instance_scaling_factor);
instance->set_offset(instance_offset);
if (group_id != (unsigned int)(-1))
group_to_model_object[group_id] = model_object;
} else {
// This is not the 1st mesh of a group. Add it to the ModelObject.
volume = model_object->add_volume(std::move(mesh));
volume->name = name;
}
// Set the extruder to the volume.
if (extruder_id != (unsigned int)-1)
volume->config.set("extruder", int(extruder_id));
}
// Load a PrusaControl project file into a provided model.
bool load_prus(const char *path, Model *model)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
size_t n_models_initial = model->objects.size();
mz_bool res = MZ_FALSE;
try {
if (!open_zip_reader(&archive, path))
throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
std::vector<char> scene_xml_data;
// For grouping multiple STLs into a single ModelObject for multi-material prints.
std::map<int, ModelObject*> group_to_model_object;
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
for (mz_uint i = 0; i < num_entries; ++ i) {
mz_zip_archive_file_stat stat;
if (! mz_zip_reader_file_stat(&archive, i, &stat))
continue;
std::vector<char> buffer;
buffer.assign((size_t)stat.m_uncomp_size, 0);
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == MZ_FALSE)
throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
if (strcmp(stat.m_filename, "scene.xml") == 0) {
if (! scene_xml_data.empty())
throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
scene_xml_data = std::move(buffer);
} else if (boost::iends_with(stat.m_filename, ".stl")) {
// May throw std::exception
extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object);
}
}
} catch (std::exception &ex) {
close_zip_reader(&archive);
throw ex;
}
close_zip_reader(&archive);
return model->objects.size() > n_models_initial;
}
}; // namespace Slic3r

View File

@ -1,11 +0,0 @@
#define slic3r_Format_PRUS_hpp_
namespace Slic3r {
class TriangleMesh;
class Model;
// Load a PrusaControl project file into a provided model.
extern bool load_prus(const char *path, Model *model);
}; // namespace Slic3r

View File

@ -21,8 +21,7 @@ bool load_stl(const char *path, Model *model, const char *object_name_in)
// die "Failed to open $file\n" if !-e $path;
return false;
}
mesh.repair();
if (mesh.facets_count() == 0) {
if (mesh.empty()) {
// die "This STL file couldn't be read because it's empty.\n"
return false;
}

View File

@ -841,26 +841,16 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.extruders_count = extruders_count;
m_extruder_offsets.resize(extruders_count);
for (size_t i = 0; i < extruders_count; ++i) {
Vec2f offset = config.extruder_offset.get_at(i).cast<float>();
m_extruder_offsets[i] = { offset(0), offset(1), 0.0f };
}
m_extruder_colors.resize(extruders_count);
for (size_t i = 0; i < extruders_count; ++i) {
m_extruder_colors[i] = static_cast<unsigned char>(i);
}
m_result.filament_diameters.resize(extruders_count);
m_result.filament_densities.resize(extruders_count);
m_extruder_temps.resize(extruders_count);
m_result.filament_diameters.resize(config.filament_diameter.values.size());
for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) {
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
}
m_result.filament_densities.resize(config.filament_density.values.size());
for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
m_result.filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
for (size_t i = 0; i < extruders_count; ++ i) {
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f);
m_extruder_colors[i] = static_cast<unsigned char>(i);
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
}
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {

View File

@ -29,18 +29,17 @@ TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
{
auto &VC = emesh.first; auto &FC = emesh.second;
Pointf3s points(size_t(VC.rows()));
std::vector<Vec3i> facets(size_t(FC.rows()));
indexed_triangle_set its;
its.vertices.reserve(size_t(VC.rows()));
its.indices.reserve(size_t(FC.rows()));
for (Eigen::Index i = 0; i < VC.rows(); ++i)
points[size_t(i)] = VC.row(i);
its.vertices.emplace_back(VC.row(i).cast<float>());
for (Eigen::Index i = 0; i < FC.rows(); ++i)
facets[size_t(i)] = FC.row(i);
its.indices.emplace_back(FC.row(i));
TriangleMesh out{points, facets};
out.require_shared_vertices();
return out;
return TriangleMesh { std::move(its) };
}
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh)
@ -131,28 +130,27 @@ void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
}
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
inline Vec3f to_vec3f(const _EpicMesh::Point& v)
{
return {v.x(), v.y(), v.z()};
return { float(v.x()), float(v.y()), float(v.z()) };
}
inline Vec3d to_vec3d(const _EpecMesh::Point &v)
inline Vec3f to_vec3f(const _EpecMesh::Point& v)
{
CGAL::Cartesian_converter<EpecKernel, EpicKernel> cvt;
auto iv = cvt(v);
return {iv.x(), iv.y(), iv.z()};
return { float(iv.x()), float(iv.y()), float(iv.z()) };
}
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{
Pointf3s points;
std::vector<Vec3i> facets;
points.reserve(cgalmesh.num_vertices());
facets.reserve(cgalmesh.num_faces());
indexed_triangle_set its;
its.vertices.reserve(cgalmesh.num_vertices());
its.indices.reserve(cgalmesh.num_faces());
for (auto &vi : cgalmesh.vertices()) {
auto &v = cgalmesh.point(vi); // Don't ask...
points.emplace_back(to_vec3d(v));
its.vertices.emplace_back(to_vec3f(v));
}
for (auto &face : cgalmesh.faces()) {
@ -166,12 +164,10 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
}
if (i == 3)
facets.emplace_back(facet);
its.indices.emplace_back(facet);
}
TriangleMesh out{points, facets};
out.repair();
return out;
return TriangleMesh(std::move(its));
}
std::unique_ptr<CGALMesh, CGALMeshDeleter>

View File

@ -28,65 +28,84 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
}
};
// Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
// and return them.
// Discover connected patches of facets one by one.
template<class NeighborIndex>
std::vector<size_t> its_find_unvisited_neighbors(
const indexed_triangle_set &its,
const NeighborIndex & neighbor_index,
std::vector<char> & visited)
{
using stack_el = size_t;
auto facestack = reserve_vector<stack_el>(its.indices.size());
auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); };
auto pop = [&facestack] () -> stack_el {
stack_el ret = facestack.back();
facestack.pop_back();
return ret;
};
// find the next unvisited facet and push the index
auto facet = std::find(visited.begin(), visited.end(), false);
std::vector<size_t> ret;
if (facet != visited.end()) {
ret.reserve(its.indices.size());
auto idx = size_t(facet - visited.begin());
push(idx);
ret.emplace_back(idx);
visited[idx] = true;
struct NeighborVisitor {
NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
its(its), neighbor_index(neighbor_index) {
m_visited.assign(its.indices.size(), false);
m_facestack.reserve(its.indices.size());
}
NeighborVisitor(const indexed_triangle_set &its, NeighborIndex &&aneighbor_index) :
its(its), neighbor_index(m_neighbor_index_data), m_neighbor_index_data(std::move(aneighbor_index)) {
m_visited.assign(its.indices.size(), false);
m_facestack.reserve(its.indices.size());
}
while (!facestack.empty()) {
size_t facet_idx = pop();
const auto &neighbors = neighbor_index[facet_idx];
for (auto neighbor_idx : neighbors) {
if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) {
visited[size_t(neighbor_idx)] = true;
push(stack_el(neighbor_idx));
ret.emplace_back(size_t(neighbor_idx));
template<typename Visitor>
void visit(Visitor visitor)
{
// find the next unvisited facet and push the index
auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
m_seed = facet - m_visited.begin();
if (facet != m_visited.end()) {
// Skip this element in the next round.
auto idx = m_seed ++;
if (! visitor(idx))
return;
this->push(idx);
m_visited[idx] = true;
while (! m_facestack.empty()) {
size_t facet_idx = this->pop();
for (auto neighbor_idx : neighbor_index[facet_idx]) {
assert(neighbor_idx < int(m_visited.size()));
if (neighbor_idx >= 0 && !m_visited[neighbor_idx]) {
if (! visitor(size_t(neighbor_idx)))
return;
m_visited[neighbor_idx] = true;
this->push(stack_el(neighbor_idx));
}
}
}
}
}
return ret;
}
const indexed_triangle_set &its;
const NeighborIndex &neighbor_index;
private:
// If initialized with &&neighbor_index, take the ownership of the data.
const NeighborIndex m_neighbor_index_data;
std::vector<char> m_visited;
using stack_el = size_t;
std::vector<stack_el> m_facestack;
void push(const stack_el &s) { m_facestack.emplace_back(s); }
stack_el pop() { stack_el ret = m_facestack.back(); m_facestack.pop_back(); return ret; }
// Last face visited.
size_t m_seed { 0 };
};
} // namespace meshsplit_detail
// Funky wrapper for timinig of its_split() using various neighbor index creating methods, see sandboxes/its_neighbor_index/main.cpp
template<class IndexT> struct ItsNeighborsWrapper
{
using Index = IndexT;
const indexed_triangle_set *its;
IndexT index;
const indexed_triangle_set &its;
const IndexT &index_ref;
const IndexT index;
ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx)
: its{&m}, index{std::move(idx)}
{}
// Keeping a reference to index, the caller is responsible for keeping the index alive.
ItsNeighborsWrapper(const indexed_triangle_set &its, const IndexT &index) : its{its}, index_ref{index} {}
// Taking ownership of the index.
ItsNeighborsWrapper(const indexed_triangle_set &its, IndexT &&aindex) : its{its}, index_ref{index}, index(std::move(aindex)) {}
const auto& get_its() const noexcept { return *its; }
const auto& get_index() const noexcept { return index; }
const auto& get_its() const noexcept { return its; }
const auto& get_index() const noexcept { return index_ref; }
};
// Splits a mesh into multiple meshes when possible.
@ -97,20 +116,19 @@ void its_split(const Its &m, OutputIt out_it)
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
std::vector<char> visited(its.indices.size(), false);
struct VertexConv {
size_t part_id = std::numeric_limits<size_t>::max();
size_t vertex_image;
};
std::vector<VertexConv> vidx_conv(its.vertices.size());
const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
meshsplit_detail::NeighborVisitor visitor(its, meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
std::vector<size_t> facets;
for (size_t part_id = 0;; ++part_id) {
std::vector<size_t> facets =
its_find_unvisited_neighbors(its, neighbor_index, visited);
// Collect all faces of the next patch.
facets.clear();
visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
if (facets.empty())
break;
@ -150,17 +168,34 @@ std::vector<indexed_triangle_set> its_split(const Its &its)
return ret;
}
template<class Its> bool its_is_splittable(const Its &m)
template<class Its>
bool its_is_splittable(const Its &m)
{
using namespace meshsplit_detail;
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
bool has_some = false;
bool has_some2 = false;
// Traverse the 1st patch fully.
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
if (has_some)
// Just check whether there is any face of the 2nd patch.
visitor.visit([&has_some2](size_t idx) { has_some2 = true; return false; });
return has_some && has_some2;
}
std::vector<char> visited(its.indices.size(), false);
its_find_unvisited_neighbors(its, neighbor_index, visited);
auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited);
return !faces.empty();
template<class Its>
size_t its_number_of_patches(const Its &m)
{
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
size_t num_patches = 0;
for (;;) {
bool has_some = false;
// Traverse the 1st patch fully.
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
if (! has_some)
break;
++ num_patches;
}
return num_patches;
}
template<class ExPolicy>

View File

@ -475,10 +475,10 @@ bool Model::looks_like_imperial_units() const
void Model::convert_from_imperial_units(bool only_small_volumes)
{
static constexpr const double in_to_mm = 25.4;
static constexpr const float in_to_mm = 25.4f;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) {
obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
obj->scale_mesh_after_creation(in_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_meters);
v->source.is_converted_from_inches = true;
@ -505,7 +505,7 @@ void Model::convert_from_meters(bool only_small_volumes)
static constexpr const double m_to_mm = 1000;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) {
obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
obj->scale_mesh_after_creation(m_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_inches);
v->source.is_converted_from_meters = true;
@ -1062,11 +1062,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
void ModelObject::scale_mesh_after_creation(const float scale)
{
for (ModelVolume *v : this->volumes) {
v->scale_geometry_after_creation(versor);
v->set_offset(versor.cwiseProduct(v->get_offset()));
v->scale_geometry_after_creation(scale);
v->set_offset(Vec3d(scale, scale, scale).cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
}
@ -1077,9 +1077,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
ModelObject* new_object = new_clone(*this);
double koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4 : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787 :
conv_type == ConversionType::CONV_FROM_METER ? 1000 : conv_type == ConversionType::CONV_TO_METER ? 0.001 : 1;
const Vec3d versor = Vec3d(koef, koef, koef);
float koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4f : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787f :
conv_type == ConversionType::CONV_FROM_METER ? 1000.f : conv_type == ConversionType::CONV_TO_METER ? 0.001f : 1.f;
new_object->set_model(nullptr);
new_object->sla_support_points.clear();
@ -1092,7 +1091,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
for (ModelVolume* volume : volumes) {
if (!volume->mesh().empty()) {
TriangleMesh mesh(volume->mesh());
mesh.require_shared_vertices();
ModelVolume* vol = new_object->add_volume(mesh);
vol->name = volume->name;
@ -1118,8 +1116,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
vol->scale_geometry_after_creation(versor);
vol->set_offset(versor.cwiseProduct(volume->get_offset()));
vol->scale_geometry_after_creation(koef);
vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH)
vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
@ -1164,14 +1162,6 @@ size_t ModelObject::parts_count() const
return num;
}
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
if (v->is_model_part() && v->mesh().needed_repair())
return true;
return false;
}
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
@ -1253,21 +1243,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
upper_mesh.repair();
upper_mesh.reset_repair_stats();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
lower_mesh.repair();
lower_mesh.reset_repair_stats();
}
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper_mesh.facets_count() > 0) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@ -1276,7 +1259,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower_mesh.facets_count() > 0) {
if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@ -1346,24 +1329,22 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (volume->type() != ModelVolumeType::MODEL_PART)
continue;
TriangleMeshPtrs meshptrs = volume->mesh().split();
std::vector<TriangleMesh> meshes = volume->mesh().split();
size_t counter = 1;
for (TriangleMesh* mesh : meshptrs) {
for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied
if (mesh->facets_count() < 3) continue;
mesh->repair();
if (mesh.facets_count() < 3)
continue;
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
if (meshptrs.size() == 1) {
if (meshes.size() == 1) {
new_object->name = volume->name;
// Don't copy the config's ID.
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
}
else {
new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
new_object->name = this->name + (meshes.size() > 1 ? "_" + std::to_string(counter++) : "");
// Don't copy the config's ID.
new_object->config.assign_config(this->config);
}
@ -1372,7 +1353,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
new_object->instances.reserve(this->instances.size());
for (const ModelInstance* model_instance : this->instances)
new_object->add_instance(*model_instance);
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
for (ModelInstance* model_instance : new_object->instances)
{
@ -1384,7 +1365,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// reset the source to disable reload from disk
new_vol->source = ModelVolume::Source();
new_objects->emplace_back(new_object);
delete mesh;
}
}
}
@ -1402,7 +1382,6 @@ void ModelObject::merge()
for (ModelVolume* volume : volumes)
if (!volume->mesh().empty())
mesh.merge(volume->mesh());
mesh.repair();
this->clear_volumes();
ModelVolume* vol = this->add_volume(mesh);
@ -1569,7 +1548,6 @@ void ModelObject::print_info() const
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size();
cout << "size_x = " << size(0) << endl;
@ -1582,19 +1560,18 @@ void ModelObject::print_info() const
cout << "max_y = " << bb.max(1) << endl;
cout << "max_z = " << bb.max(2) << endl;
cout << "number_of_facets = " << mesh.facets_count() << endl;
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
if (! mesh.stats().manifold())
cout << "open_edges = " << mesh.stats().open_edges << endl;
mesh.repair(); // this calculates number_of_parts
if (mesh.needed_repair()) {
mesh.repair();
if (mesh.stats().repaired()) {
if (mesh.stats().degenerate_facets > 0)
cout << "degenerate_facets = " << mesh.stats().degenerate_facets << endl;
if (mesh.stats().edges_fixed > 0)
cout << "edges_fixed = " << mesh.stats().edges_fixed << endl;
if (mesh.stats().facets_removed > 0)
cout << "facets_removed = " << mesh.stats().facets_removed << endl;
if (mesh.stats().facets_added > 0)
cout << "facets_added = " << mesh.stats().facets_added << endl;
if (mesh.stats().facets_reversed > 0)
cout << "facets_reversed = " << mesh.stats().facets_reversed << endl;
if (mesh.stats().backwards_edges > 0)
@ -1624,24 +1601,23 @@ std::string ModelObject::get_export_filename() const
return ret;
}
stl_stats ModelObject::get_object_stl_stats() const
TriangleMeshStats ModelObject::get_object_stl_stats() const
{
if (this->volumes.size() == 1)
return this->volumes[0]->mesh().stats();
stl_stats full_stats;
TriangleMeshStats full_stats;
full_stats.volume = 0.f;
// fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes)
{
const stl_stats& stats = volume->mesh().stats();
const TriangleMeshStats& stats = volume->mesh().stats();
// initialize full_stats (for repaired errors)
full_stats.degenerate_facets += stats.degenerate_facets;
full_stats.edges_fixed += stats.edges_fixed;
full_stats.facets_removed += stats.facets_removed;
full_stats.facets_added += stats.facets_added;
full_stats.facets_reversed += stats.facets_reversed;
full_stats.backwards_edges += stats.backwards_edges;
@ -1660,10 +1636,10 @@ int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count();
const stl_stats& stats = get_object_stl_stats();
const TriangleMeshStats& stats = get_object_stl_stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
stats.facets_reversed + stats.backwards_edges;
}
void ModelVolume::set_material_id(t_model_material_id material_id)
@ -1727,14 +1703,15 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
void ModelVolume::calculate_convex_hull()
{
m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
assert(m_convex_hull.get());
}
int ModelVolume::get_mesh_errors_count() const
{
const stl_stats &stats = this->mesh().stats();
const TriangleMeshStats &stats = this->mesh().stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
stats.facets_reversed + stats.backwards_edges;
}
const TriangleMesh& ModelVolume::get_convex_hull() const
@ -1782,11 +1759,9 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders)
{
TriangleMeshPtrs meshptrs = this->mesh().split();
if (meshptrs.size() <= 1) {
delete meshptrs.front();
std::vector<TriangleMesh> meshes = this->mesh().split();
if (meshes.size() <= 1)
return 1;
}
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
@ -1795,15 +1770,14 @@ size_t ModelVolume::split(unsigned int max_extruders)
unsigned int extruder_counter = 0;
Vec3d offset = this->get_offset();
for (TriangleMesh *mesh : meshptrs) {
mesh->repair();
if (mesh->empty())
for (TriangleMesh &mesh : meshes) {
if (mesh.empty())
// Repair may have removed unconnected triangles, thus emptying the mesh.
continue;
if (idx == 0)
{
this->set_mesh(std::move(*mesh));
this->set_mesh(std::move(mesh));
this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id();
@ -1811,7 +1785,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->source = ModelVolume::Source();
}
else
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]->center_geometry_after_creation();
@ -1819,7 +1793,6 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
this->object->volumes[ivolume]->m_is_splittable = 0;
delete mesh;
++ idx;
}
@ -1888,7 +1861,7 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
void ModelVolume::scale_geometry_after_creation(const Vec3f& versor)
{
const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
@ -1921,8 +1894,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
void ModelVolume::convert_from_imperial_units()
{
assert(! this->source.is_converted_from_meters);
double in_to_mm = 25.4;
this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
this->scale_geometry_after_creation(25.4f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_inches = true;
}
@ -1930,8 +1902,7 @@ void ModelVolume::convert_from_imperial_units()
void ModelVolume::convert_from_meters()
{
assert(! this->source.is_converted_from_inches);
double m_to_mm = 1000;
this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
this->scale_geometry_after_creation(1000.f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_meters = true;
}

View File

@ -346,13 +346,12 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_mesh_after_creation(const Vec3d& versor);
void scale_mesh_after_creation(const float scale);
void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector<int> volume_idxs);
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
void merge();
@ -376,7 +375,7 @@ public:
std::string get_export_filename() const;
// Get full stl statistics for all object's meshes
stl_stats get_object_stl_stats() const;
TriangleMeshStats get_object_stl_stats() const;
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
@ -620,6 +619,8 @@ public:
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(indexed_triangle_set &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
@ -670,7 +671,8 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_geometry_after_creation(const Vec3d& versor);
void scale_geometry_after_creation(const Vec3f &versor);
void scale_geometry_after_creation(const float scale) { this->scale_geometry_after_creation(Vec3f(scale, scale, scale)); }
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!

View File

@ -286,8 +286,6 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
if (mesh.empty()) return;
mesh.require_shared_vertices();
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size())
@ -316,7 +314,6 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
remove_inside_triangles(mesh, interior);
mesh.merge(TriangleMesh{interior.mesh});
mesh.require_shared_vertices();
}
// Get the distance of p to the interior's zero iso_surface. Interior should
@ -557,8 +554,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
new_faces = {};
mesh = TriangleMesh{mesh.its};
mesh.repaired = true;
mesh.require_shared_vertices();
//FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles?
}
}} // namespace Slic3r::sla

View File

@ -33,7 +33,6 @@ inline void reproject_points_and_holes(ModelObject *object)
if (!object || (!has_holes && !has_sppoints)) return;
TriangleMesh rmsh = object->raw_mesh();
rmsh.require_shared_vertices();
IndexedMesh emesh{rmsh};
if (has_sppoints)

View File

@ -205,7 +205,6 @@ inline bool is_on_floor(const SLAPrintObjectConfig &cfg)
std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max_count)
{
TriangleMesh chull = mesh.convex_hull_3d();
chull.require_shared_vertices();
double chull2d_area = chull.convex_hull().area();
double area_threshold = chull2d_area / (scaled<double>(1e3) * scaled(1.));
@ -299,7 +298,6 @@ struct RotfinderBoilerplate {
static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
{
TriangleMesh mesh = mo.raw_mesh();
mesh.require_shared_vertices();
ModelInstance *mi = mo.instances[0];
auto rotation = Vec3d::Zero();
@ -437,7 +435,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo,
RotfinderBoilerplate<1000> bp{mo, params};
TriangleMesh chull = bp.mesh.convex_hull_3d();
chull.require_shared_vertices();
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];

View File

@ -896,7 +896,6 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
obj = m_model_object->raw_mesh();
if (!obj.empty()) {
obj.transform(m_trafo);
obj.require_shared_vertices();
}
})
{}

View File

@ -323,7 +323,6 @@ private:
{
support_tree_ptr = sla::SupportTree::create(*this, ctl);
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
tree_mesh.require_shared_vertices();
return support_tree_ptr;
}

View File

@ -526,7 +526,6 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
}
auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels;
assert(mesh.has_shared_vertices());
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
sla::Interior *interior = po.m_hollowing_data ?

View File

@ -14,10 +14,8 @@ void simplify_mesh(indexed_triangle_set &);
template<class...Args> void simplify_mesh(TriangleMesh &m, Args &&...a)
{
m.require_shared_vertices();
simplify_mesh(m.its, std::forward<Args>(a)...);
m = TriangleMesh{m.its};
m.require_shared_vertices();
m = TriangleMesh{ std::move(m.its) };
}
} // namespace Slic3r

View File

@ -41,8 +41,8 @@
//====================
#define ENABLE_2_4_0_ALPHA1 1
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
// Enable implementation of retract acceleration in gcode processor
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
// Enable rendering seams (and other options) in preview using models
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects

File diff suppressed because it is too large Load Diff

View File

@ -15,25 +15,79 @@ namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
struct TriangleMeshStats {
// Mesh metrics.
uint32_t number_of_facets = 0;
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float volume = -1.f;
int number_of_parts = 0;
// Mesh errors, remaining.
int open_edges = 0;
// Mesh errors, fixed.
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
int edges_fixed = 0;
// How many degenerate faces were removed?
int degenerate_facets = 0;
// How many faces were removed during fixing? Includes degenerate_faces and disconnected faces.
int facets_removed = 0;
// New faces could only be created with stl_fill_holes() and we ditched stl_fill_holes(), because mostly it does more harm than good.
//int facets_added = 0;
// How many facets were revesed? Faces are reversed by admesh while it connects patches of triangles togeter and a flipped triangle is encountered.
// Also the facets are reversed when a negative volume is corrected by flipping all facets.
int facets_reversed = 0;
// Edges shared by two triangles, oriented incorrectly.
int backwards_edges = 0;
void clear() { *this = TriangleMeshStats(); }
TriangleMeshStats merge(const TriangleMeshStats &rhs) const {
if (this->number_of_facets == 0)
return rhs;
else if (rhs.number_of_facets == 0)
return *this;
else {
TriangleMeshStats out;
out.number_of_facets = this->number_of_facets + rhs.number_of_facets;
out.min = this->min.cwiseMin(rhs.min);
out.max = this->max.cwiseMax(rhs.max);
out.size = out.max - out.min;
out.number_of_parts = this->number_of_parts + rhs.number_of_parts;
out.open_edges = this->open_edges + rhs.open_edges;
out.volume = this->volume + rhs.volume;
out.edges_fixed = this->edges_fixed + rhs.edges_fixed;
out.degenerate_facets = this->degenerate_facets + rhs.degenerate_facets;
out.facets_removed = this->facets_removed + rhs.facets_removed;
out.facets_reversed = this->facets_reversed + rhs.facets_reversed;
out.backwards_edges = this->backwards_edges + rhs.backwards_edges;
return out;
}
}
bool manifold() const { return open_edges == 0; }
bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; }
};
class TriangleMesh
{
public:
TriangleMesh() : repaired(false) {}
TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets);
TriangleMesh() = default;
TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces);
TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces);
explicit TriangleMesh(const indexed_triangle_set &M);
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
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_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
void repair(bool update_shared_vertices = true);
explicit TriangleMesh(indexed_triangle_set &&M);
void clear() { this->its.clear(); this->m_stats.clear(); }
bool ReadSTLFile(const char* input_file, bool repair = true);
bool write_ascii(const char* output_file);
bool write_binary(const char* output_file);
float volume();
void check_topology();
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
void WriteOBJFile(const char* output_file) const;
void scale(float factor);
void scale(const Vec3d &versor);
void scale(const Vec3f &versor);
void translate(float x, float y, float z);
void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis);
@ -41,15 +95,17 @@ public:
void rotate_x(float angle) { this->rotate(angle, X); }
void rotate_y(float angle) { this->rotate(angle, Y); }
void rotate_z(float angle) { this->rotate(angle, Z); }
void mirror(const Axis &axis);
void mirror(const Axis axis);
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& t, bool fix_left_handed = false);
// Flip triangles, negate volume.
void flip_triangles();
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;
std::vector<TriangleMesh> split() const;
void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const;
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
@ -58,37 +114,33 @@ public:
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
// Return the size of the mesh in coordinates.
Vec3d size() const { return stl.stats.size.cast<double>(); }
Vec3d size() const { return m_stats.size.cast<double>(); }
/// Return the center of the related bounding box.
Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
// Slice this mesh at the provided Z levels and return the vector
std::vector<ExPolygons> slice(const std::vector<double>& z) const;
void reset_repair_stats();
bool needed_repair() const;
void require_shared_vertices();
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 { assert(m_stats.number_of_facets == this->its.indices.size()); return m_stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; }
bool is_splittable() const;
bool repaired() const;
bool is_splittable() const;
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
size_t memsize() const;
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
size_t release_optional();
// Restore optional data possibly released by release_optional().
void restore_optional();
const stl_stats& stats() const { return this->stl.stats; }
// Used by the Undo / Redo stack, legacy interface. As of now there is nothing cached at TriangleMesh,
// but we may decide to cache some data in the future (for example normals), thus we keep the interface in place.
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
size_t release_optional() { return 0; }
// Restore optional data possibly released by release_optional().
void restore_optional() {}
const TriangleMeshStats& stats() const { return m_stats; }
indexed_triangle_set its;
bool repaired;
//private:
stl_file stl;
private:
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
TriangleMeshStats m_stats;
};
// Index of face indices incident with a vertex index.
@ -148,8 +200,18 @@ bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filenam
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
bool its_number_of_patches(const indexed_triangle_set &its);
bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
// Same as its_number_of_patches(its) > 1, but faster.
bool its_is_splittable(const indexed_triangle_set &its);
bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
// Calculate number of unconnected face edges. There should be no unconnected edge in a manifold mesh.
size_t its_num_open_edges(const indexed_triangle_set &its);
size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors);
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);
@ -217,13 +279,23 @@ inline Vec3f its_face_normal(const indexed_triangle_set &its, const int face_idx
{ return its_face_normal(its, its.indices[face_idx]); }
indexed_triangle_set its_make_cube(double x, double y, double z);
TriangleMesh make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
TriangleMesh make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; }
inline TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)) { return TriangleMesh(its_make_cone(r, h, fa)); }
inline TriangleMesh make_pyramid(float base, float height) { return TriangleMesh(its_make_pyramid(base, height)); }
inline TriangleMesh make_sphere(double rho, double fa=(2*PI/360)) { return TriangleMesh(its_make_sphere(rho, fa)); }
bool its_write_stl_ascii(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
inline bool its_write_stl_ascii(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_ascii(file, label, its.indices, its.vertices); }
bool its_write_stl_binary(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
inline bool its_write_stl_binary(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_binary(file, label, its.indices, its.vertices); }
inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); }
inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
@ -248,18 +320,12 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
namespace cereal {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
stl_allocate(&stl);
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
stl_get_size(&stl);
mesh.repair();
archive.loadBinary(reinterpret_cast<char*>(const_cast<Slic3r::TriangleMeshStats*>(&mesh.stats())), sizeof(Slic3r::TriangleMeshStats));
archive(mesh.its.indices, mesh.its.vertices);
}
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
const stl_file& stl = mesh.stl;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
archive.saveBinary(reinterpret_cast<const char*>(&mesh.stats()), sizeof(Slic3r::TriangleMeshStats));
archive(mesh.its.indices, mesh.its.vertices);
}
}

View File

@ -1967,7 +1967,8 @@ static void triangulate_slice(
int num_original_vertices,
// Z height of the slice.
float z,
bool triangulate)
bool triangulate,
bool normals_down)
{
sort_remove_duplicates(slice_vertices);
@ -2013,7 +2014,7 @@ static void triangulate_slice(
if (triangulate) {
size_t idx_vertex_new_first = its.vertices.size();
Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, true);
Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, normals_down);
for (size_t i = 0; i < triangles.size(); ) {
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
@ -2049,6 +2050,33 @@ static void triangulate_slice(
// its_remove_degenerate_faces(its);
}
void project_mesh(
const indexed_triangle_set &mesh,
const Transform3d &trafo,
Polygons *out_top,
Polygons *out_bottom,
std::function<void()> throw_on_cancel)
{
std::vector<Polygons> top, bottom;
std::vector<float> zs { -1e10, 1e10 };
slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel);
if (out_top)
*out_top = std::move(top.front());
if (out_bottom)
*out_bottom = std::move(bottom.back());
}
Polygons project_mesh(
const indexed_triangle_set &mesh,
const Transform3d &trafo,
std::function<void()> throw_on_cancel)
{
std::vector<Polygons> top, bottom;
std::vector<float> zs { -1e10, 1e10 };
slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel);
return union_(top.front(), bottom.back());
}
void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower, bool triangulate_caps)
{
assert(upper || lower);
@ -2196,10 +2224,10 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
}
if (upper != nullptr)
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
if (lower != nullptr)
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP);
}
}

View File

@ -98,7 +98,21 @@ void slice_mesh_slabs(
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel);
void cut_mesh(
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
void project_mesh(
const indexed_triangle_set &mesh,
const Transform3d &trafo,
Polygons *out_top,
Polygons *out_bottom,
std::function<void()> throw_on_cancel);
// Project mesh into 2D polygons.
Polygons project_mesh(
const indexed_triangle_set &mesh,
const Transform3d &trafo,
std::function<void()> throw_on_cancel);
void cut_mesh(
const indexed_triangle_set &mesh,
float z,
indexed_triangle_set *upper,

View File

@ -906,6 +906,7 @@ unsigned get_current_pid()
#endif
}
//FIXME this has potentially O(n^2) time complexity!
std::string xml_escape(std::string text, bool is_marked/* = false*/)
{
std::string::size_type pos = 0;

View File

@ -313,7 +313,6 @@ void GLVolume::SinkingContours::update()
m_shift = Vec3d::Zero();
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
assert(mesh.has_shared_vertices());
m_model.reset();
GUI::GLModel::InitializationData init_data;
@ -519,7 +518,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
{
return (m_convex_hull && m_convex_hull->facets_count() > 0) ?
return (m_convex_hull && ! m_convex_hull->empty()) ?
m_convex_hull->transformed_bounding_box(trafo) :
bounding_box().transformed(trafo);
}
@ -719,21 +718,20 @@ int GLVolumeCollection::load_wipe_tower_preview(
float min_width = 30.f;
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model
// are shifted so that the front edge has y=0 and centerline of the back edge has y=depth:
Pointf3s points;
std::vector<Vec3i> facets;
float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
static constexpr const int out_facets_idx[][3] = {
{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
{ 8, 10, 14 }, { 3, 12, 4 }, { 3, 13, 12 }, { 6, 13, 3 }, { 6, 14, 13 }, { 7, 14, 6 }, { 7, 15, 14 }, { 2, 15, 7 }, { 2, 8, 15 }, { 1, 8, 2 }, { 1, 9, 8 },
{ 0, 9, 1 }, { 0, 10, 9 }, { 5, 10, 0 }, { 5, 11, 10 }, { 4, 11, 5 }, { 4, 12, 11 } };
indexed_triangle_set its;
for (int i = 0; i < 16; ++i)
points.emplace_back(out_points_idx[i][0] / (100.f / min_width),
out_points_idx[i][1] + depth, out_points_idx[i][2]);
for (int i = 0; i < 28; ++i)
facets.emplace_back(out_facets_idx[i][0],
out_facets_idx[i][1],
out_facets_idx[i][2]);
TriangleMesh tooth_mesh(points, facets);
its.vertices.emplace_back(out_points_idx[i][0] / (100.f / min_width),
out_points_idx[i][1] + depth, out_points_idx[i][2]);
its.indices.reserve(28);
for (const int *face : out_facets_idx)
its.indices.emplace_back(face);
TriangleMesh tooth_mesh(std::move(its));
// We have the mesh ready. It has one tooth and width of min_width. We will now
// append several of these together until we are close to the required width
@ -744,7 +742,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
tooth_mesh.translate(min_width, 0.f, 0.f);
}
mesh.scale(Vec3d(width / (n * min_width), 1.f, height)); // Scaling to proper width
mesh.scale(Vec3f(width / (n * min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
@ -753,7 +751,6 @@ int GLVolumeCollection::load_wipe_tower_preview(
TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
brim_mesh.translate(-brim_width, -brim_width, 0.f);
mesh.merge(brim_mesh);
mesh.repair();
volumes.emplace_back(new GLVolume(color));
GLVolume& v = *volumes.back();

View File

@ -160,7 +160,6 @@ bool GLModel::init_from_file(const std::string& filename)
}
TriangleMesh mesh = model.mesh();
mesh.require_shared_vertices();
init_from(mesh.its, mesh.bounding_box());
m_filename = filename;

View File

@ -392,9 +392,9 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
// Create tooltip string, if there are errors
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n";
const stl_stats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
const TriangleMeshStats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
if (stats.degenerate_facets > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
@ -402,8 +402,6 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n";
if (stats.facets_removed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
if (stats.facets_added > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet added", "%1$d facets added", stats.facets_added), stats.facets_added) + "\n";
if (stats.facets_reversed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
if (stats.backwards_edges > 0)
@ -1535,7 +1533,6 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
}
TriangleMesh mesh = model.mesh();
mesh.repair();
// Mesh will be centered when loading.
ModelVolume* new_volume = model_object.add_volume(std::move(mesh), type);
new_volume->name = boost::filesystem::path(input_file).filename().string();
@ -1558,27 +1555,24 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf3& bb)
{
TriangleMesh mesh;
const double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
indexed_triangle_set mesh;
if (type_name == "Box")
// Sitting on the print bed, left front front corner at (0, 0).
mesh = make_cube(side, side, side);
mesh = its_make_cube(side, side, side);
else if (type_name == "Cylinder")
// Centered around 0, sitting on the print bed.
// The cylinder has the same volume as the box above.
mesh = make_cylinder(0.564 * side, side);
mesh = its_make_cylinder(0.564 * side, side);
else if (type_name == "Sphere")
// Centered around 0, half the sphere below the print bed, half above.
// The sphere has the same volume as the box above.
mesh = make_sphere(0.62 * side, PI / 18);
mesh = its_make_sphere(0.62 * side, PI / 18);
else if (type_name == "Slab")
// Sitting on the print bed, left front front corner at (0, 0).
mesh = make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
mesh.repair();
return mesh;
mesh = its_make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
return TriangleMesh(mesh);
}
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)

View File

@ -282,10 +282,8 @@ void GLGizmoCut::update_contours()
if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) {
m_cut_contours.cut_z = m_cut_z;
if (m_cut_contours.object_id != model_object->id()) {
if (m_cut_contours.object_id != model_object->id())
m_cut_contours.mesh = model_object->raw_mesh();
m_cut_contours.mesh.repair();
}
m_cut_contours.position = box.center();
m_cut_contours.shift = Vec3d::Zero();

View File

@ -313,9 +313,8 @@ void GLGizmoSimplify::process()
}
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
auto tm = std::make_unique<TriangleMesh>(its);
tm->repair();
m_volume->set_mesh(std::move(tm));
m_volume->set_mesh(its);
m_volume->calculate_convex_hull();
m_volume->set_new_unique_id();
m_volume->get_object()->invalidate_bounding_box();
m_need_reload = true;

View File

@ -270,11 +270,10 @@ void HollowedMesh::on_update()
m_drainholes = print_object->model_object()->sla_drain_holes;
m_old_hollowing_timestamp = timestamp;
const indexed_triangle_set &interior = print_object->hollowed_interior_mesh();
indexed_triangle_set interior = print_object->hollowed_interior_mesh();
if (!interior.empty()) {
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(interior);
m_hollowed_interior_transformed->repaired = false;
m_hollowed_interior_transformed->repair(true);
its_flip_triangles(interior);
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
m_hollowed_interior_transformed->transform(trafo_inv);
}
}

View File

@ -1740,7 +1740,6 @@ void MainFrame::repair_stl()
Slic3r::TriangleMesh tmesh;
tmesh.ReadSTLFile(input_file.ToUTF8().data());
tmesh.repair();
tmesh.WriteOBJFile(output_file.ToUTF8().data());
Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair"));
}

View File

@ -91,11 +91,9 @@ void MeshClipper::recalculate_triangles()
MeshSlicingParams slicing_params;
slicing_params.trafo.rotate(Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(up, Vec3d::UnitZ()));
assert(m_mesh->has_shared_vertices());
ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params));
if (m_negative_mesh && !m_negative_mesh->empty()) {
assert(m_negative_mesh->has_shared_vertices());
ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params));
expolys = diff_ex(expolys, neg_expolys);
}

View File

@ -1135,7 +1135,7 @@ void Sidebar::show_info_sizer()
static_cast<int>(model_object->facets_count()), stats.number_of_parts));
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
stats.facets_reversed + stats.backwards_edges;
if (errors > 0) {
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
p->object_info->info_manifold->SetLabel(tooltip);
@ -1147,8 +1147,6 @@ void Sidebar::show_info_sizer()
tooltip += format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + ", ";
if (stats.facets_removed > 0)
tooltip += format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + ", ";
if (stats.facets_added > 0)
tooltip += format_wxstr(_L_PLURAL("%1$d facet added", "%1$d facets added", stats.facets_added), stats.facets_added) + ", ";
if (stats.facets_reversed > 0)
tooltip += format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + ", ";
if (stats.backwards_edges > 0)
@ -2544,16 +2542,14 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
if (max_ratio > 10000) {
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
// so scale down the mesh
double inv = 1. / max_ratio;
object->scale_mesh_after_creation(inv * Vec3d::Ones());
object->scale_mesh_after_creation(1. / max_ratio);
object->origin_translation = Vec3d::Zero();
object->center_around_origin();
scaled_down = true;
break;
}
else if (max_ratio > 5) {
const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones();
instance->set_scaling_factor(inverse.cwiseProduct(instance->get_scaling_factor()));
instance->set_scaling_factor(instance->get_scaling_factor() / max_ratio);
scaled_down = true;
}
}
@ -5587,11 +5583,9 @@ void Plater::export_stl(bool extended, bool selection_only)
for (const ModelVolume *v : mo->volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.repair();
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
mesh.repair();
if (instances) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();
@ -5601,7 +5595,6 @@ void Plater::export_stl(bool extended, bool selection_only)
mesh.merge(m);
}
}
mesh.repair();
return mesh;
};

View File

@ -402,6 +402,7 @@ bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxPro
}
for (size_t i = 0; i < volumes.size(); ++ i) {
volumes[i]->set_mesh(std::move(meshes_repaired[i]));
volumes[i]->calculate_convex_hull();
volumes[i]->set_new_unique_id();
}
model_object.invalidate_bounding_box();

File diff suppressed because one or more lines are too long

View File

@ -17,12 +17,13 @@
using namespace Slic3r;
using namespace std;
static inline TriangleMesh make_cube() { return make_cube(20., 20, 20); }
SCENARIO( "TriangleMesh: Basic mesh statistics") {
GIVEN( "A 20mm cube, built from constexpr std::array" ) {
std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
std::vector<Vec3f> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
TriangleMesh cube(vertices, facets);
THEN( "Volume is appropriate for 20mm square cube.") {
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
@ -68,64 +69,11 @@ SCENARIO( "TriangleMesh: Basic mesh statistics") {
}
}
GIVEN( "A 20mm cube with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
THEN( "Volume is appropriate for 20mm square cube.") {
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
}
THEN( "Vertices array matches input.") {
for (size_t i = 0U; i < cube.its.vertices.size(); i++) {
REQUIRE(cube.its.vertices.at(i) == vertices.at(i).cast<float>());
}
for (size_t i = 0U; i < vertices.size(); i++) {
REQUIRE(vertices.at(i).cast<float>() == cube.its.vertices.at(i));
}
}
THEN( "Vertex count matches vertex array size.") {
REQUIRE(cube.facets_count() == facets.size());
}
THEN( "Facet array matches input.") {
for (size_t i = 0U; i < cube.its.indices.size(); i++) {
REQUIRE(cube.its.indices.at(i) == facets.at(i));
}
for (size_t i = 0U; i < facets.size(); i++) {
REQUIRE(facets.at(i) == cube.its.indices.at(i));
}
}
THEN( "Facet count matches facet array size.") {
REQUIRE(cube.facets_count() == facets.size());
}
#if 0
THEN( "Number of normals is equal to the number of facets.") {
REQUIRE(cube.normals().size() == facets.size());
}
#endif
THEN( "center() returns the center of the object.") {
REQUIRE(cube.center() == Vec3d(10.0,10.0,10.0));
}
THEN( "Size of cube is (20,20,20)") {
REQUIRE(cube.size() == Vec3d(20,20,20));
}
}
}
SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
GIVEN( "A 20mm cube with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
auto cube = make_cube();
WHEN( "The cube is scaled 200% uniformly") {
cube.scale(2.0);
@ -134,7 +82,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
}
}
WHEN( "The resulting cube is scaled 200% in the X direction") {
cube.scale(Vec3d(2.0, 1, 1));
cube.scale(Vec3f(2.0, 1, 1));
THEN( "The volume is doubled.") {
REQUIRE(abs(cube.volume() - 2*20.0*20.0*20.0) < 1e-2);
}
@ -144,7 +92,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
}
WHEN( "The cube is scaled 25% in the X direction") {
cube.scale(Vec3d(0.25, 1, 1));
cube.scale(Vec3f(0.25, 1, 1));
THEN( "The volume is 25% of the previous volume.") {
REQUIRE(abs(cube.volume() - 0.25*20.0*20.0*20.0) < 1e-2);
}
@ -177,7 +125,10 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
cube.translate(5.0, 10.0, 0.0);
cube.align_to_origin();
THEN( "The third vertex is located at 0,0,0") {
REQUIRE(cube.its.vertices.at(2) == Vec3f(0.0, 0.0, 0.0));
REQUIRE(cube.its.vertices.at(2) == Vec3f::Zero());
}
THEN( "Size is OK") {
REQUIRE(cube.stats().size == Vec3f(20.f, 20.f, 20.f));
}
}
}
@ -185,11 +136,8 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
SCENARIO( "TriangleMesh: slice behavior.") {
GIVEN( "A 20mm cube with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
auto cube = make_cube();
WHEN("Cube is sliced with z = [0+EPSILON,2,4,8,6,8,10,12,14,16,18,20]") {
std::vector<double> z { 0+EPSILON,2,4,8,6,8,10,12,14,16,18,20 };
std::vector<ExPolygons> result = cube.slice(z);
@ -206,12 +154,12 @@ SCENARIO( "TriangleMesh: slice behavior.") {
}
}
GIVEN( "A STL with an irregular shape.") {
const std::vector<Vec3d> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
const std::vector<Vec3f> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
const std::vector<Vec3i> facets {{0,1,2},{2,1,3},{1,0,4},{5,1,4},{0,2,4},{4,2,6},{7,6,8},{4,6,7},{9,4,7},{7,8,10},{2,3,6},{11,3,12},{7,12,9},{13,12,7},{6,3,11},{11,12,13},{3,1,5},{12,3,5},{5,4,9},{12,5,9},{13,7,10},{14,13,10},{8,15,10},{10,15,14},{6,11,8},{8,11,15},{15,11,13},{14,15,13}};
TriangleMesh cube(vertices, facets);
cube.repair();
auto cube = make_cube();
WHEN(" a top tangent plane is sliced") {
// At Z = 10 we have a top horizontal surface.
std::vector<ExPolygons> slices = cube.slice({5.0, 10.0});
THEN( "its area is included") {
REQUIRE(slices.at(0).at(0).area() > 0);
@ -240,9 +188,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
THEN("The mesh volume is 20*20*20") {
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
}
THEN("The resulting mesh is in the repaired state.") {
REQUIRE(cube.repaired == true);
}
THEN("There are 12 facets.") {
REQUIRE(cube.its.indices.size() == 12);
}
@ -266,9 +211,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
THEN("Resulting mesh has 2*PI/angle * 4 facets") {
REQUIRE(cyl.its.indices.size() == (2*PI/angle)*4);
}
THEN("The resulting mesh is in the repaired state.") {
REQUIRE(cyl.repaired == true);
}
THEN( "The mesh volume is approximately 10pi * 10^2") {
REQUIRE(abs(cyl.volume() - (10.0 * M_PI * std::pow(10,2))) < 1);
}
@ -283,9 +225,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, 10.f)); } ) == 1);
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, -10.f)); } ) == 1);
}
THEN("The resulting mesh is in the repaired state.") {
REQUIRE(sph.repaired == true);
}
THEN( "The mesh volume is approximately 4/3 * pi * 10^3") {
REQUIRE(abs(sph.volume() - (4.0/3.0 * M_PI * std::pow(10,3))) < 1); // 1% tolerance?
}
@ -295,32 +234,25 @@ SCENARIO( "make_xxx functions produce meshes.") {
SCENARIO( "TriangleMesh: split functionality.") {
GIVEN( "A 20mm cube with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
auto cube = make_cube();
WHEN( "The mesh is split into its component parts.") {
std::vector<TriangleMesh*> meshes = cube.split();
std::vector<TriangleMesh> meshes = cube.split();
THEN(" The bounding box statistics are propagated to the split copies") {
REQUIRE(meshes.size() == 1);
REQUIRE((meshes.at(0)->bounding_box() == cube.bounding_box()));
REQUIRE((meshes.front().bounding_box() == cube.bounding_box()));
}
}
}
GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
TriangleMesh cube2(vertices, facets);
cube2.repair();
auto cube = make_cube();
TriangleMesh cube2(cube);
cube.merge(cube2);
cube.repair();
WHEN( "The combined mesh is split") {
std::vector<TriangleMesh*> meshes = cube.split();
THEN( "Number of faces is 2x the source.") {
REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
}
std::vector<TriangleMesh> meshes = cube.split();
THEN( "Two meshes are in the output vector.") {
REQUIRE(meshes.size() == 2);
}
@ -330,17 +262,11 @@ SCENARIO( "TriangleMesh: split functionality.") {
SCENARIO( "TriangleMesh: Mesh merge functions") {
GIVEN( "Two 20mm cubes, each with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
TriangleMesh cube2(vertices, facets);
cube2.repair();
auto cube = make_cube();
TriangleMesh cube2(cube);
WHEN( "The two meshes are merged") {
cube.merge(cube2);
cube.repair();
THEN( "There are twice as many facets in the merged mesh as the original.") {
REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
}
@ -350,11 +276,7 @@ SCENARIO( "TriangleMesh: Mesh merge functions") {
SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
GIVEN( "A 20mm cube with one corner on the origin") {
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
TriangleMesh cube(vertices, facets);
cube.repair();
auto cube = make_cube();
WHEN( "Object is cut at the bottom") {
indexed_triangle_set upper {};
indexed_triangle_set lower {};
@ -384,7 +306,6 @@ TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
TriangleMesh mesh;
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/100_000.stl");
mesh.repair();
config.set("layer_height", 500);
config.set("first_layer_height", 250);
@ -412,7 +333,6 @@ TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
TriangleMesh mesh;
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/10_000.stl");
mesh.repair();
config.set("layer_height", 500);
config.set("first_layer_height", 250);

View File

@ -65,10 +65,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
// compare meshes
TriangleMesh src_mesh = src_model.mesh();
src_mesh.repair();
TriangleMesh dst_mesh = dst_model.mesh();
dst_mesh.repair();
bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size();
if (res) {

View File

@ -9,7 +9,6 @@ using namespace Slic3r;
TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]")
{
TriangleMesh tmesh = make_cube(1., 1., 1.);
tmesh.repair();
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(tmesh.its.vertices, tmesh.its.indices);
REQUIRE(! tree.empty());

View File

@ -13,7 +13,6 @@ TEST_CASE("Hollow two overlapping spheres") {
sphere2.translate( 5.f, 0.f, 0.f);
sphere1.merge(sphere2);
sphere1.require_shared_vertices();
sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles);

View File

@ -319,7 +319,6 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
mesh.translate(tr.x(), tr.y(), tr.z());
bb = mesh.bounding_box();
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
sla::RasterBase::Resolution res{2560, 1440};

View File

@ -57,7 +57,6 @@ TEST_CASE("Support point generator should be deterministic if seeded",
auto layer_h = 0.05f;
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, slicegrid, CLOSING_RADIUS);
point_gen.seed(0);

View File

@ -63,7 +63,6 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]")
sla::DrainHoles holes = { sla::DrainHole{p, normal, radius, hole_length} };
cube.merge(*cube_inside);
cube.require_shared_vertices();
sla::IndexedMesh emesh{cube};
emesh.load_holes(holes);

View File

@ -13,7 +13,6 @@ TEST_CASE("Overhanging point should be supported", "[SupGen]") {
// Pyramid with 45 deg slope
TriangleMesh mesh = make_pyramid(10.f, 10.f);
mesh.rotate_y(float(PI));
mesh.require_shared_vertices();
mesh.WriteOBJFile("Pyramid.obj");
sla::SupportPoints pts = calc_support_pts(mesh);
@ -56,7 +55,6 @@ TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") {
TriangleMesh mesh = make_cube(width, depth, height);
mesh.translate(0., 0., 5.); // lift up
mesh.require_shared_vertices();
mesh.WriteOBJFile("Cuboid.obj");
sla::SupportPointGenerator::Config cfg;
@ -83,7 +81,6 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") {
TriangleMesh mesh = make_prism(width, depth, height);
mesh.rotate_y(float(PI)); // rotate on its back
mesh.translate(0., 0., height);
mesh.require_shared_vertices();
mesh.WriteOBJFile("Prism.obj");
sla::SupportPointGenerator::Config cfg;
@ -115,7 +112,6 @@ TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowe
auto h = float(bb.max.z() - bb.min.z());
Vec3f mv = bb.center().cast<float>() - Vec3f{0.f, 0.f, 0.5f * h};
mesh.translate(-mv);
mesh.require_shared_vertices();
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
@ -132,7 +128,6 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]")
TriangleMesh mesh_high = center_around_bb(make_cube(width, depth, height));
mesh_high.translate(0., 0., 10.); // lift up
mesh.merge(mesh_high);
mesh.require_shared_vertices();
mesh.WriteOBJFile("parallel_plates.obj");

View File

@ -74,8 +74,6 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices, const Sup
byproducts.supporttree.retrieve_full_mesh(its);
TriangleMesh m{its};
m.merge(byproducts.input_mesh);
m.repair();
m.require_shared_vertices();
m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" +
byproducts.obj_fname).c_str());
}
@ -95,7 +93,6 @@ void test_supports(const std::string &obj_filename,
sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg);
REQUIRE(interior);
mesh.merge(TriangleMesh{sla::get_mesh(*interior)});
mesh.require_shared_vertices();
}
auto bb = mesh.bounding_box();
@ -105,7 +102,6 @@ void test_supports(const std::string &obj_filename,
auto layer_h = 0.05f;
out.slicegrid = grid(float(gnd), float(zmax), layer_h);
assert(mesh.has_shared_vertices());
out.model_slices = slice_mesh_ex(mesh.its, out.slicegrid, CLOSING_RADIUS);
sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{});
@ -283,8 +279,10 @@ void test_concave_hull(const ExPolygons &polys) {
_test_concave_hull(waffl, polys);
}
//FIXME this functionality is gone after TriangleMesh refactoring to get rid of admesh.
void check_validity(const TriangleMesh &input_mesh, int flags)
{
/*
TriangleMesh mesh{input_mesh};
if (flags & ASSUME_NO_EMPTY) {
@ -292,20 +290,18 @@ void check_validity(const TriangleMesh &input_mesh, int flags)
} else if (mesh.empty())
return; // If it can be empty and it is, there is nothing left to do.
REQUIRE(stl_validate(&mesh.stl));
bool do_update_shared_vertices = false;
mesh.repair(do_update_shared_vertices);
if (flags & ASSUME_NO_REPAIR) {
REQUIRE_FALSE(mesh.needed_repair());
REQUIRE_FALSE(mesh.repaired());
}
if (flags & ASSUME_MANIFOLD) {
mesh.require_shared_vertices();
if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj");
REQUIRE(mesh.is_manifold());
}
*/
}
void check_raster_transformations(sla::RasterBase::Orientation o, sla::RasterBase::TMirroring mirroring)
@ -420,53 +416,6 @@ double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd)
return error;
}
// Make a 3D pyramid
TriangleMesh make_pyramid(float base, float height)
{
float a = base / 2.f;
TriangleMesh mesh(
{
{-a, -a, 0}, {a, -a, 0}, {a, a, 0},
{-a, a, 0}, {0.f, 0.f, height}
},
{
{0, 1, 2},
{0, 2, 3},
{0, 1, 4},
{1, 2, 4},
{2, 3, 4},
{3, 0, 4}
});
mesh.repair();
return mesh;
}
TriangleMesh make_prism(double width, double length, double height)
{
// We need two upward facing triangles
double x = width / 2., y = length / 2.;
TriangleMesh mesh(
{
{-x, -y, 0.}, {x, -y, 0.}, {0., -y, height},
{-x, y, 0.}, {x, y, 0.}, {0., y, height},
},
{
{0, 1, 2}, // side 1
{4, 3, 5}, // side 2
{1, 4, 2}, {2, 4, 5}, // roof 1
{0, 2, 5}, {0, 5, 3}, // roof 2
{3, 4, 1}, {3, 1, 0} // bottom
});
return mesh;
}
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg)
@ -474,7 +423,6 @@ sla::SupportPoints calc_support_pts(
// Prepare the slice grid and the slices
auto bb = cast<float>(mesh.bounding_box());
std::vector<float> heights = grid(bb.min.z(), bb.max.z(), 0.1f);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, heights, CLOSING_RADIUS);
// Prepare the support point calculator

View File

@ -185,11 +185,6 @@ long raster_pxsum(const sla::RasterGrayscaleAA &raster);
double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd);
// Make a 3D pyramid
TriangleMesh make_pyramid(float base, float height);
TriangleMesh make_prism(double width, double length, double height);
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg = {});

View File

@ -4,10 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 49;
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
'hello world';
use Test::More tests => 5;
my $cube = {
vertices => [ [20,20,0], [20,0,0], [0,0,0], [0,20,0], [20,20,20], [0,20,20], [0,0,20], [20,0,20] ],
@ -17,12 +14,10 @@ my $cube = {
{
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m->repair;
my ($vertices, $facets) = ($m->vertices, $m->facets);
is_deeply $vertices, $cube->{vertices}, 'vertices arrayref roundtrip';
is_deeply $facets, $cube->{facets}, 'facets arrayref roundtrip';
is scalar(@{$m->normals}), scalar(@$facets), 'normals returns the right number of items';
{
my $m2 = $m->clone;
@ -34,109 +29,6 @@ my $cube = {
{
my $stats = $m->stats;
is $stats->{number_of_facets}, scalar(@{ $cube->{facets} }), 'stats.number_of_facets';
ok abs($stats->{volume} - 20*20*20) < 1E-2, 'stats.volume';
}
$m->scale(2);
ok abs($m->stats->{volume} - 40*40*40) < 1E-2, 'scale';
$m->scale_xyz(Slic3r::Pointf3->new(2,1,1));
ok abs($m->stats->{volume} - 2*40*40*40) < 1E-2, 'scale_xyz';
$m->translate(5,10,0);
is_deeply $m->vertices->[0], [85,50,0], 'translate';
$m->align_to_origin;
is_deeply $m->vertices->[2], [0,0,0], 'align_to_origin';
is_deeply $m->size, [80,40,40], 'size';
$m->scale_xyz(Slic3r::Pointf3->new(0.5,1,1));
$m->rotate(45, Slic3r::Point->new(20,20));
ok abs($m->size->[0] - sqrt(2)*40) < 1E-4, 'rotate';
{
my $meshes = $m->split;
is scalar(@$meshes), 1, 'split';
isa_ok $meshes->[0], 'Slic3r::TriangleMesh', 'split';
is_deeply $m->bb3, $meshes->[0]->bb3, 'split populates stats';
}
my $m2 = Slic3r::TriangleMesh->new;
$m2->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m2->repair;
$m->merge($m2);
$m->repair;
is $m->stats->{number_of_facets}, 2 * $m2->stats->{number_of_facets}, 'merge';
{
my $meshes = $m->split;
is scalar(@$meshes), 2, 'split';
}
}
{
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m->repair;
# The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
# open intervals at the bottom end, closed at the top end.
my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20);
my $result = $m->slice(\@z);
my $SCALING_FACTOR = 0.000001;
for my $i (0..$#z) {
is scalar(@{$result->[$i]}), 1, "number of returned polygons per layer (z = " . $z[$i] . ")";
is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon';
}
}
{
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl(
[ [0,0,0],[0,0,20],[0,5,0],[0,5,20],[50,0,0],[50,0,20],[15,5,0],[35,5,0],[15,20,0],[50,5,0],[35,20,0],[15,5,10],[50,5,20],[35,5,10],[35,20,10],[15,20,10] ],
[ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
);
$m->repair;
{
# at Z = 10 we have a top horizontal surface
my $slices = $m->slice([ 5, 10 ]);
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area';
}
$m->mirror_z;
{
# this second test also checks that performing a second slice on a mesh after
# a transformation works properly (shared_vertices is correctly invalidated);
# at Z = -10 we have a bottom horizontal surface
# (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
# open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice).
my $slices = $m->slice([ -5, -10+0.00001 ]);
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
}
}
{
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m->repair;
{
my $upper = Slic3r::TriangleMesh->new;
my $lower = Slic3r::TriangleMesh->new;
$m->cut(0, $upper, $lower);
$upper->repair; $lower->repair;
is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane';
is $lower->facets_count, 0, 'lower mesh has no facets';
}
{
my $upper = Slic3r::TriangleMesh->new;
my $lower = Slic3r::TriangleMesh->new;
$m->cut(10, $upper, $lower);
#$upper->repair; $lower->repair;
# we expect:
# 2 facets on external horizontal surfaces
# 3 facets on each side = 12 facets
# 6 facets on the triangulated side (8 vertices)
is $upper->facets_count, 2+12+6, 'upper mesh has the expected number of facets';
is $lower->facets_count, 2+12+6, 'lower mesh has the expected number of facets';
}
}

View File

@ -88,8 +88,6 @@
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
void print_info() const;
bool store_stl(char *path, bool binary)
%code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %};
@ -212,7 +210,6 @@ ModelMaterial::attributes()
%code%{ THIS->origin_translation = *point; %};
void ensure_on_bed();
bool needed_repair() const;
int materials_count() const;
int facets_count();
void center_around_origin();
@ -223,13 +220,6 @@ ModelMaterial::attributes()
%code{% THIS->rotate(angle, *axis); %};
void mirror(Axis axis);
ModelObjectPtrs* split_object()
%code%{
RETVAL = new ModelObjectPtrs(); // leak?
THIS->split(RETVAL);
%};
void print_info() const;
};

View File

@ -11,14 +11,11 @@
~TriangleMesh();
Clone<TriangleMesh> clone()
%code{% RETVAL = THIS; %};
void ReadSTLFile(char* input_file);
void write_ascii(char* output_file);
void write_binary(char* output_file);
void repair();
void WriteOBJFile(char* output_file);
void scale(float factor);
void scale_xyz(Vec3d* versor)
%code{% THIS->scale(*versor); %};
%code{% THIS->scale(versor->cast<float>()); %};
void translate(float x, float y, float z);
void rotate_x(float angle);
void rotate_y(float angle);
@ -28,16 +25,13 @@
void mirror_z();
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split();
void merge(TriangleMesh* mesh)
%code{% THIS->merge(*mesh); %};
ExPolygons horizontal_projection();
Clone<Polygon> convex_hull();
Clone<BoundingBoxf3> bounding_box();
Clone<Vec3d> center()
%code{% RETVAL = THIS->bounding_box().center(); %};
int facets_count();
void reset_repair_stats();
%{
@ -46,51 +40,40 @@ TriangleMesh::ReadFromPerl(vertices, facets)
SV* vertices
SV* facets
CODE:
stl_file &stl = THIS->stl;
stl.stats.type = inmemory;
// count facets and allocate memory
AV* facets_av = (AV*)SvRV(facets);
stl.stats.number_of_facets = av_len(facets_av)+1;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
// read geometry
AV* vertices_av = (AV*)SvRV(vertices);
for (int i = 0; i < stl.stats.number_of_facets; i++) {
AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0));
stl_facet facet;
facet.normal(0) = 0;
facet.normal(1) = 0;
facet.normal(2) = 0;
for (unsigned int v = 0; v <= 2; v++) {
AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0));
facet.vertex[v](0) = SvNV(*av_fetch(vertex_av, 0, 0));
facet.vertex[v](1) = SvNV(*av_fetch(vertex_av, 1, 0));
facet.vertex[v](2) = SvNV(*av_fetch(vertex_av, 2, 0));
std::vector<Slic3r::Vec3f> out_vertices;
{
AV* vertices_av = (AV*)SvRV(vertices);
int number_of_vertices = av_len(vertices_av) + 1;
out_vertices.reserve(number_of_vertices);
for (int i = 0; i < number_of_vertices; ++ i) {
AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, i, 0));
out_vertices.push_back(Slic3r::Vec3f(SvNV(*av_fetch(vertex_av, 0, 0)), SvNV(*av_fetch(vertex_av, 1, 0)), SvNV(*av_fetch(vertex_av, 2, 0))));
}
facet.extra[0] = 0;
facet.extra[1] = 0;
stl.facet_start[i] = facet;
}
stl_get_size(&stl);
std::vector<Slic3r::Vec3i> out_indices;
{
AV* facets_av = (AV*)SvRV(facets);
int number_of_facets = av_len(facets_av) + 1;
out_indices.reserve(number_of_facets);
for (int i = 0; i < number_of_facets; ++ i) {
AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0));
out_indices.push_back(Slic3r::Vec3i(SvIV(*av_fetch(facet_av, 0, 0)), SvIV(*av_fetch(facet_av, 1, 0)), SvIV(*av_fetch(facet_av, 2, 0))));
}
}
*THIS = TriangleMesh(std::move(out_vertices), std::move(out_indices));
SV*
TriangleMesh::stats()
CODE:
HV* hv = newHV();
(void)hv_stores( hv, "number_of_facets", newSViv(THIS->stl.stats.number_of_facets) );
(void)hv_stores( hv, "number_of_parts", newSViv(THIS->stl.stats.number_of_parts) );
(void)hv_stores( hv, "volume", newSVnv(THIS->stl.stats.volume) );
(void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stl.stats.degenerate_facets) );
(void)hv_stores( hv, "edges_fixed", newSViv(THIS->stl.stats.edges_fixed) );
(void)hv_stores( hv, "facets_removed", newSViv(THIS->stl.stats.facets_removed) );
(void)hv_stores( hv, "facets_added", newSViv(THIS->stl.stats.facets_added) );
(void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) );
(void)hv_stores( hv, "backwards_edges", newSViv(THIS->stl.stats.backwards_edges) );
(void)hv_stores( hv, "normals_fixed", newSViv(THIS->stl.stats.normals_fixed) );
(void)hv_stores( hv, "number_of_facets", newSViv(THIS->facets_count()) );
(void)hv_stores( hv, "number_of_parts", newSViv(THIS->stats().number_of_parts) );
(void)hv_stores( hv, "volume", newSVnv(THIS->stats().volume) );
(void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stats().degenerate_facets) );
(void)hv_stores( hv, "edges_fixed", newSViv(THIS->stats().edges_fixed) );
(void)hv_stores( hv, "facets_removed", newSViv(THIS->stats().facets_removed) );
(void)hv_stores( hv, "facets_reversed", newSViv(THIS->stats().facets_reversed) );
(void)hv_stores( hv, "backwards_edges", newSViv(THIS->stats().backwards_edges) );
RETVAL = (SV*)newRV_noinc((SV*)hv);
OUTPUT:
RETVAL
@ -98,9 +81,6 @@ TriangleMesh::stats()
SV*
TriangleMesh::vertices()
CODE:
if (!THIS->repaired) CONFESS("vertices() requires repair()");
THIS->require_shared_vertices();
// vertices
AV* vertices = newAV();
av_extend(vertices, THIS->its.vertices.size());
@ -120,13 +100,10 @@ TriangleMesh::vertices()
SV*
TriangleMesh::facets()
CODE:
if (!THIS->repaired) CONFESS("facets() requires repair()");
THIS->require_shared_vertices();
// facets
AV* facets = newAV();
av_extend(facets, THIS->stl.stats.number_of_facets);
for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) {
av_extend(facets, THIS->facets_count());
for (int i = 0; i < THIS->facets_count(); i++) {
AV* facet = newAV();
av_store(facets, i, newRV_noinc((SV*)facet));
av_extend(facet, 2);
@ -139,35 +116,14 @@ TriangleMesh::facets()
OUTPUT:
RETVAL
SV*
TriangleMesh::normals()
CODE:
if (!THIS->repaired) CONFESS("normals() requires repair()");
// normals
AV* normals = newAV();
av_extend(normals, THIS->stl.stats.number_of_facets);
for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) {
AV* facet = newAV();
av_store(normals, i, newRV_noinc((SV*)facet));
av_extend(facet, 2);
av_store(facet, 0, newSVnv(THIS->stl.facet_start[i].normal(0)));
av_store(facet, 1, newSVnv(THIS->stl.facet_start[i].normal(1)));
av_store(facet, 2, newSVnv(THIS->stl.facet_start[i].normal(2)));
}
RETVAL = newRV_noinc((SV*)normals);
OUTPUT:
RETVAL
SV*
TriangleMesh::size()
CODE:
AV* size = newAV();
av_extend(size, 2);
av_store(size, 0, newSVnv(THIS->stl.stats.size(0)));
av_store(size, 1, newSVnv(THIS->stl.stats.size(1)));
av_store(size, 2, newSVnv(THIS->stl.stats.size(2)));
av_store(size, 0, newSVnv(THIS->stats().size(0)));
av_store(size, 1, newSVnv(THIS->stats().size(1)));
av_store(size, 2, newSVnv(THIS->stats().size(2)));
RETVAL = newRV_noinc((SV*)size);
OUTPUT:
RETVAL
@ -176,8 +132,6 @@ SV*
TriangleMesh::slice(z)
std::vector<double> z
CODE:
THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
// convert doubles to floats
std::vector<float> z_f = cast<float>(z);
@ -206,7 +160,6 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh)
TriangleMesh* upper_mesh;
TriangleMesh* lower_mesh;
CODE:
THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
indexed_triangle_set upper, lower;
cut_mesh(THIS->its, z, upper_mesh ? &upper : nullptr, lower_mesh ? &lower : nullptr);
if (upper_mesh)
@ -217,12 +170,12 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh)
std::vector<double>
TriangleMesh::bb3()
CODE:
RETVAL.push_back(THIS->stl.stats.min(0));
RETVAL.push_back(THIS->stl.stats.min(1));
RETVAL.push_back(THIS->stl.stats.max(0));
RETVAL.push_back(THIS->stl.stats.max(1));
RETVAL.push_back(THIS->stl.stats.min(2));
RETVAL.push_back(THIS->stl.stats.max(2));
RETVAL.push_back(THIS->stats().min(0));
RETVAL.push_back(THIS->stats().min(1));
RETVAL.push_back(THIS->stats().max(0));
RETVAL.push_back(THIS->stats().max(1));
RETVAL.push_back(THIS->stats().min(2));
RETVAL.push_back(THIS->stats().max(2));
OUTPUT:
RETVAL
@ -250,16 +203,3 @@ sphere(double rho)
%}
};
%package{Slic3r::TriangleMesh};
%{
PROTOTYPES: DISABLE
std::string
hello_world()
CODE:
RETVAL = "Hello world!";
OUTPUT:
RETVAL
%}

View File

@ -239,7 +239,6 @@ SupportLayerPtrs* T_PTR_ARRAYREF_PTR
# we return these types whenever we want the items to be returned
# by reference and not marked ::Ref because they're newly allocated
# and not referenced by any Perl object
TriangleMeshPtrs T_PTR_ARRAYREF
INPUT

View File

@ -163,7 +163,6 @@
%typemap{Surfaces};
%typemap{Polygons*};
%typemap{TriangleMesh*};
%typemap{TriangleMeshPtrs};
%typemap{Model*};
%typemap{Ref<Model>}{simple};
%typemap{Clone<Model>}{simple};