#include "libslic3r.h" #include "TriangleMesh.hpp" #include "SlicingAdaptive.hpp" #ifdef SLIC3R_DEBUG #undef NDEBUG #define DEBUG #define _DEBUG #endif /* This constant essentially describes the volumetric error at the surface which is induced * by stacking "elliptic" extrusion threads. * It is empirically determined by * 1. measuring the surface profile of printed parts to find * the ratio between layer height and profile height and then * 2. computing the geometric difference between the model-surface and the elliptic profile. * [Link to detailed description follows] */ #define SURFACE_CONST 0.18403 namespace Slic3r { void SlicingAdaptive::clear() { m_meshes.clear(); m_faces.clear(); m_face_normal_z.clear(); } std::pair face_z_span(const stl_facet *f) { return std::pair( std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z), std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z)); } void SlicingAdaptive::prepare(coordf_t object_size) { this->object_size = object_size; // 1) Collect faces of all meshes. int nfaces_total = 0; for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) nfaces_total += (*it_mesh)->stl.stats.number_of_facets; m_faces.reserve(nfaces_total); for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) m_faces.push_back((*it_mesh)->stl.facet_start + i); // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { std::pair span1 = face_z_span(f1); std::pair span2 = face_z_span(f2); return span1 < span2; }); // 3) Generate Z components of the facet normals. m_face_normal_z.assign(m_faces.size(), 0.f); for (size_t iface = 0; iface < m_faces.size(); ++ iface) m_face_normal_z[iface] = m_faces[iface]->normal.z; // 4) Reset current facet pointer this->current_facet = 0; } float SlicingAdaptive::next_layer_height(coordf_t z, coordf_t quality_factor, coordf_t min_layer_height, coordf_t max_layer_height) { float height = max_layer_height; // factor must be between 0-1, 0 is highest quality, 1 highest print speed. // factor must be between 0-1, 0 is highest quality, 1 highest print speed. // Invert the slider scale (100% should represent a very high quality for the user) quality_factor = std::max(0.f, std::min(1.f, 1 - quality_factor/100.f)); float delta_min = SURFACE_CONST * min_layer_height; float delta_max = SURFACE_CONST * max_layer_height + 0.5 * max_layer_height; float scaled_quality_factor = quality_factor * (delta_max - delta_min) + delta_min; bool first_hit = false; // find all facets intersecting the slice-layer int ordered_id = current_facet; for (; ordered_id < int(m_faces.size()); ++ ordered_id) { std::pair zspan = face_z_span(m_faces[ordered_id]); // facet's minimum is higher than slice_z -> end loop if (zspan.first >= z) break; // facet's maximum is higher than slice_z -> store the first event for next layer_height call to begin at this point if (zspan.second > z) { // first event? if (! first_hit) { first_hit = true; current_facet = ordered_id; } // skip touching facets which could otherwise cause small height values if (zspan.second <= z + EPSILON) continue; // compute height for this facet and store minimum of all heights height = std::min(height, this->_layer_height_from_facet(ordered_id, scaled_quality_factor)); } } // lower height limit due to printer capabilities height = std::max(height, min_layer_height); // check for sloped facets inside the determined layer and correct height if necessary if (height > min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { std::pair zspan = face_z_span(m_faces[ordered_id]); // facet's minimum is higher than slice_z + height -> end loop if (zspan.first >= z + height) break; // skip touching facets which could otherwise cause small cusp values if (zspan.second <= z + EPSILON) continue; // Compute new height for this facet and check against height. float reduced_height = this->_layer_height_from_facet(ordered_id, scaled_quality_factor); float z_diff = zspan.first - z; if (reduced_height > z_diff) { if (reduced_height < height) { #ifdef DEBUG std::cout << "adaptive layer computation: height is reduced from " << height; #endif height = reduced_height; #ifdef DEBUG std::cout << "to " << height << " due to higher facet" << std::endl; #endif } } else { #ifdef DEBUG std::cout << "cusp computation, height is reduced from " << height; #endif height = z_diff; #ifdef DEBUG std::cout << "to " << height << " due to z-diff" << std::endl; #endif } } // lower height limit due to printer capabilities again height = std::max(height, float(min_layer_height)); } #ifdef DEBUG std::cout << "adaptive layer computation, layer-bottom at z:" << z << ", quality_factor:" << quality_factor << ", resulting layer height:" << height << std::endl; #endif return height; } // Returns the distance to the next horizontal facet in Z-dir // to consider horizontal object features in slice thickness float SlicingAdaptive::horizontal_facet_distance(coordf_t z, coordf_t max_layer_height) { for (size_t i = 0; i < m_faces.size(); ++ i) { std::pair zspan = face_z_span(m_faces[i]); // facet's minimum is higher than max forward distance -> end loop if (zspan.first > z + max_layer_height) break; // min_z == max_z -> horizontal facet if (zspan.first > z && zspan.first == zspan.second) return zspan.first - z; } // objects maximum? return (z + max_layer_height > this->object_size) ? std::max(this->object_size - z, 0.f) : max_layer_height; } // for a given facet, compute maximum height within the allowed surface roughness / stairstepping deviation float SlicingAdaptive::_layer_height_from_facet(int ordered_id, float scaled_quality_factor) { float normal_z = std::abs(m_face_normal_z[ordered_id]); float height = scaled_quality_factor/(SURFACE_CONST + normal_z/2); return (normal_z == 0.f) ? 9999.f : height; } }; // namespace Slic3r