mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	 65772958b7
			
		
	
	
		65772958b7
		
	
	
	
	
		
			
			delimited by the extrusion stepping and the sloping surface. This leads to a yet different metric from Cura or upstream Slic3r.
		
			
				
	
	
		
			217 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "libslic3r.h"
 | |
| #include "Model.hpp"
 | |
| #include "TriangleMesh.hpp"
 | |
| #include "SlicingAdaptive.hpp"
 | |
| 
 | |
| #include <boost/log/trivial.hpp>
 | |
| 
 | |
| // Based on the work of Florens Waserfall (@platch on github)
 | |
| // and his paper
 | |
| // Florens Wasserfall, Norman Hendrich, Jianwei Zhang:
 | |
| // Adaptive Slicing for the FDM Process Revisited
 | |
| // 13th IEEE Conference on Automation Science and Engineering (CASE-2017), August 20-23, Xi'an, China. DOI: 10.1109/COASE.2017.8256074
 | |
| // https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf
 | |
| 
 | |
| // Vojtech believes that there is a bug in @platch's derivation of the triangle area error metric.
 | |
| // Following Octave code paints graphs of recommended layer height versus surface slope angle.
 | |
| #if 0
 | |
| adeg=0:1:85;
 | |
| a=adeg*pi/180;
 | |
| t=tan(a);
 | |
| tsqr=sqrt(tan(a));
 | |
| lerr=1./cos(a);
 | |
| lerr2=1./(0.3+cos(a));
 | |
| plot(adeg, t, 'b', adeg, sqrt(t), 'g', adeg, 0.5 * lerr, 'm', adeg, 0.5 * lerr2, 'r')
 | |
| xlabel("angle(deg), 0 - horizontal wall, 90 - vertical wall");
 | |
| ylabel("layer height");
 | |
| legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as PrusaSlicer - error triangle area limit", "old slic3r - max distance metric", "new slic3r - Waserfall paper");
 | |
| #endif
 | |
| 
 | |
| #ifndef NDEBUG
 | |
| 	#define ADAPTIVE_LAYER_HEIGHT_DEBUG
 | |
| #endif /* NDEBUG */
 | |
| 
 | |
| namespace Slic3r
 | |
| {
 | |
| 
 | |
| static inline std::pair<float, float> face_z_span(const stl_facet &f)
 | |
| {
 | |
| 	return std::pair<float, float>(
 | |
| 		std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)),
 | |
| 		std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)));
 | |
| }
 | |
| 
 | |
| // By Florens Waserfall aka @platch:
 | |
| // 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.
 | |
| //
 | |
| // The definition of the roughness formula is in 
 | |
| // https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf
 | |
| // (page 51, formula (8))
 | |
| // Currenty @platch's error metric formula is not used.
 | |
| static constexpr double SURFACE_CONST = 0.18403;
 | |
| 
 | |
| // for a given facet, compute maximum height within the allowed surface roughness / stairstepping deviation
 | |
| static inline float layer_height_from_slope(const SlicingAdaptive::FaceZ &face, float max_surface_deviation)
 | |
| {
 | |
| // @platch's formula, see his paper "Adaptive Slicing for the FDM Process Revisited".
 | |
| //    return float(max_surface_deviation / (SURFACE_CONST + 0.5 * std::abs(normal_z)));
 | |
| 	
 | |
| // Constant stepping in horizontal direction, as used by Cura.
 | |
| //    return (face.n_cos > 1e-5) ? float(max_surface_deviation * face.n_sin / face.n_cos) : FLT_MAX;
 | |
| 
 | |
| // Constant error measured as an area of the surface error triangle, Vojtech's formula.
 | |
| //    return (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX;
 | |
| 
 | |
| // Constant error measured as an area of the surface error triangle, Vojtech's formula with clamping to roughness at 90 degrees.
 | |
|     return std::min(max_surface_deviation / 0.184f, (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX);
 | |
| 
 | |
| // Constant stepping along the surface, equivalent to the "surface roughness" metric by Perez and later Pandey et all, see @platch's paper for references.
 | |
| //    return float(max_surface_deviation * face.n_sin);
 | |
| }
 | |
| 
 | |
| void SlicingAdaptive::clear()
 | |
| {
 | |
| 	m_faces.clear();
 | |
| }
 | |
| 
 | |
| void SlicingAdaptive::prepare(const ModelObject &object)
 | |
| {
 | |
|     this->clear();
 | |
| 
 | |
|     TriangleMesh		 mesh			= object.raw_mesh();
 | |
|     const ModelInstance &first_instance = *object.instances.front();
 | |
|     mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
 | |
| 
 | |
|     // 1) Collect faces from mesh.
 | |
|     m_faces.reserve(mesh.stl.stats.number_of_facets);
 | |
|     for (const stl_facet &face : mesh.stl.facet_start) {
 | |
|     	Vec3f n = face.normal.normalized();
 | |
| 		m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
 | |
|     }
 | |
| 
 | |
| 	// 2) Sort faces lexicographically by their Z span.
 | |
| 	std::sort(m_faces.begin(), m_faces.end(), [](const FaceZ &f1, const FaceZ &f2) { return f1.z_span < f2.z_span; });
 | |
| }
 | |
| 
 | |
| // current_facet is in/out parameter, rememebers the index of the last face of m_faces visited, 
 | |
| // where this function will start from.
 | |
| // print_z - the top print surface of the previous layer.
 | |
| // returns height of the next layer.
 | |
| float SlicingAdaptive::next_layer_height(const float print_z, float quality_factor, size_t ¤t_facet)
 | |
| {
 | |
| 	float  height = (float)m_slicing_params.max_layer_height;
 | |
| 
 | |
| 	float  max_surface_deviation;
 | |
| 
 | |
| 	{
 | |
| #if 0
 | |
| // @platch's formula for quality:
 | |
| 	    double delta_min = SURFACE_CONST * m_slicing_params.min_layer_height;
 | |
| 	    double delta_mid = (SURFACE_CONST + 0.5) * m_slicing_params.layer_height;
 | |
| 	    double delta_max = (SURFACE_CONST + 0.5) * m_slicing_params.max_layer_height;
 | |
| #else
 | |
| // Vojtech's formula for triangle area error metric.
 | |
| 	    double delta_min = m_slicing_params.min_layer_height;
 | |
| 	    double delta_mid = m_slicing_params.layer_height;
 | |
| 	    double delta_max = m_slicing_params.max_layer_height;
 | |
| #endif
 | |
| 	    max_surface_deviation = (quality_factor < 0.5f) ?
 | |
| 	    	lerp(delta_min, delta_mid, 2. * quality_factor) :
 | |
| 	    	lerp(delta_max, delta_mid, 2. * (1. - quality_factor));
 | |
| 	}
 | |
| 	
 | |
| 	// find all facets intersecting the slice-layer
 | |
| 	size_t ordered_id = current_facet;
 | |
| 	{
 | |
| 		bool first_hit = false;
 | |
| 		for (; ordered_id < m_faces.size(); ++ ordered_id) {
 | |
| 	        const std::pair<float, float> &zspan = m_faces[ordered_id].z_span;
 | |
| 	        // facet's minimum is higher than slice_z -> end loop
 | |
| 			if (zspan.first >= print_z)
 | |
| 				break;
 | |
| 			// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
 | |
| 			if (zspan.second > print_z) {
 | |
| 				// first event?
 | |
| 				if (! first_hit) {
 | |
| 					first_hit = true;
 | |
| 					current_facet = ordered_id;
 | |
| 	            }
 | |
| 				// skip touching facets which could otherwise cause small cusp values
 | |
| 				if (zspan.second < print_z + EPSILON)
 | |
| 					continue;
 | |
| 				// compute cusp-height for this facet and store minimum of all heights
 | |
| 				height = std::min(height, layer_height_from_slope(m_faces[ordered_id], max_surface_deviation));
 | |
| 	        }
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// lower height limit due to printer capabilities
 | |
| 	height = std::max(height, float(m_slicing_params.min_layer_height));
 | |
| 
 | |
| 	// check for sloped facets inside the determined layer and correct height if necessary
 | |
| 	if (height > float(m_slicing_params.min_layer_height)) {
 | |
| 		for (; ordered_id < m_faces.size(); ++ ordered_id) {
 | |
|             const std::pair<float, float> &zspan = m_faces[ordered_id].z_span;
 | |
|             // facet's minimum is higher than slice_z + height -> end loop
 | |
| 			if (zspan.first >= print_z + height)
 | |
| 				break;
 | |
| 
 | |
| 			// skip touching facets which could otherwise cause small cusp values
 | |
| 			if (zspan.second < print_z + EPSILON)
 | |
| 				continue;
 | |
| 
 | |
| 			// Compute cusp-height for this facet and check against height.
 | |
|             float reduced_height = layer_height_from_slope(m_faces[ordered_id], max_surface_deviation);
 | |
| 
 | |
| 			float z_diff = zspan.first - print_z;
 | |
| 			if (reduced_height < z_diff) {
 | |
| 				assert(z_diff < height + EPSILON);
 | |
| 				// The currently visited triangle's slope limits the next layer height so much, that
 | |
| 				// the lowest point of the currently visible triangle is already above the newly proposed layer height.
 | |
| 				// This means, that we need to limit the layer height so that the offending newly visited triangle
 | |
| 				// is just above of the new layer.
 | |
| #ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
 | |
|                 BOOST_LOG_TRIVIAL(trace) << "cusp computation, height is reduced from " << height << "to " << z_diff << " due to z-diff";
 | |
| #endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
 | |
| 				height = z_diff;
 | |
| 			} else if (reduced_height < height) {
 | |
| #ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
 | |
| 				BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation: height is reduced from " << height << "to " << reduced_height << " due to higher facet";
 | |
| #endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
 | |
| 				height = reduced_height;
 | |
| 			}
 | |
| 		}
 | |
| 		// lower height limit due to printer capabilities again
 | |
| 		height = std::max(height, float(m_slicing_params.min_layer_height));
 | |
| 	}
 | |
| 
 | |
| #ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
 | |
|     BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation, layer-bottom at z:" << print_z << ", quality_factor:" << quality_factor << ", resulting layer height:" << height;
 | |
| #endif  /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
 | |
| 	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(float z)
 | |
| {
 | |
| 	for (size_t i = 0; i < m_faces.size(); ++ i) {
 | |
|         std::pair<float, float> zspan = m_faces[i].z_span;
 | |
|         // facet's minimum is higher than max forward distance -> end loop
 | |
| 		if (zspan.first > z + m_slicing_params.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 + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? 
 | |
| 		std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height;
 | |
| }
 | |
| 
 | |
| }; // namespace Slic3r
 |