2016-12-08 18:02:16 +00:00
# include "libslic3r.h"
2019-11-13 12:53:02 +00:00
# include "Model.hpp"
2016-12-08 18:02:16 +00:00
# include "TriangleMesh.hpp"
# include "SlicingAdaptive.hpp"
2019-12-13 12:43:16 +00:00
# include <boost/log/trivial.hpp>
2021-06-01 17:54:53 +00:00
# include <cfloat>
2019-12-13 12:43:16 +00:00
// 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 */
2016-12-08 18:02:16 +00:00
namespace Slic3r
{
2019-12-13 12:43:16 +00:00
// 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.
2021-02-09 19:04:11 +00:00
//static constexpr const double SURFACE_CONST = 0.18403;
2019-12-13 12:43:16 +00:00
// 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 )
2016-12-08 18:02:16 +00:00
{
2019-12-13 12:43:16 +00:00
// @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);
2016-12-08 18:02:16 +00:00
}
2019-12-13 12:43:16 +00:00
void SlicingAdaptive : : clear ( )
2016-12-08 18:02:16 +00:00
{
2019-12-13 12:43:16 +00:00
m_faces . clear ( ) ;
}
2019-11-13 12:53:02 +00:00
2019-12-13 12:43:16 +00:00
void SlicingAdaptive : : prepare ( const ModelObject & object )
{
this - > clear ( ) ;
2019-11-13 12:53:02 +00:00
2019-12-13 12:43:16 +00:00
TriangleMesh mesh = object . raw_mesh ( ) ;
const ModelInstance & first_instance = * object . instances . front ( ) ;
mesh . transform ( first_instance . get_matrix ( ) , first_instance . is_left_handed ( ) ) ;
2019-11-29 13:24:24 +00:00
2019-11-13 12:53:02 +00:00
// 1) Collect faces from mesh.
2021-09-14 09:58:07 +00:00
m_faces . reserve ( mesh . facets_count ( ) ) ;
for ( stl_triangle_vertex_indices face : mesh . its . indices ) {
stl_vertex vertex [ 3 ] = { mesh . its . vertices [ face [ 0 ] ] , mesh . its . vertices [ face [ 1 ] ] , mesh . its . vertices [ face [ 2 ] ] } ;
2021-09-14 12:51:28 +00:00
stl_vertex n = face_normal_normalized ( vertex ) ;
2021-09-14 09:58:07 +00:00
std : : pair < float , float > face_z_span {
std : : min ( std : : min ( vertex [ 0 ] . z ( ) , vertex [ 1 ] . z ( ) ) , vertex [ 2 ] . z ( ) ) ,
std : : max ( std : : max ( vertex [ 0 ] . z ( ) , vertex [ 1 ] . z ( ) ) , vertex [ 2 ] . z ( ) )
} ;
m_faces . emplace_back ( FaceZ ( { face_z_span , std : : abs ( n . z ( ) ) , std : : sqrt ( n . x ( ) * n . x ( ) + n . y ( ) * n . y ( ) ) } ) ) ;
2019-11-29 13:24:24 +00:00
}
2016-12-08 18:02:16 +00:00
// 2) Sort faces lexicographically by their Z span.
2019-12-13 12:43:16 +00:00
std : : sort ( m_faces . begin ( ) , m_faces . end ( ) , [ ] ( const FaceZ & f1 , const FaceZ & f2 ) { return f1 . z_span < f2 . z_span ; } ) ;
2016-12-08 18:02:16 +00:00
}
2019-12-13 12:43:16 +00:00
// 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 & current_facet )
2016-12-08 18:02:16 +00:00
{
2019-12-13 12:43:16 +00:00
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 ) ) ;
}
2016-12-08 18:02:16 +00:00
// find all facets intersecting the slice-layer
2019-12-13 12:43:16 +00:00
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 ) ) ;
}
}
2016-12-08 18:02:16 +00:00
}
// lower height limit due to printer capabilities
2016-12-12 16:53:38 +00:00
height = std : : max ( height , float ( m_slicing_params . min_layer_height ) ) ;
2016-12-08 18:02:16 +00:00
// check for sloped facets inside the determined layer and correct height if necessary
2019-12-13 12:43:16 +00:00
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 ;
2019-11-13 12:53:02 +00:00
// facet's minimum is higher than slice_z + height -> end loop
2019-12-13 12:43:16 +00:00
if ( zspan . first > = print_z + height )
2016-12-08 18:02:16 +00:00
break ;
// skip touching facets which could otherwise cause small cusp values
2019-12-13 12:43:16 +00:00
if ( zspan . second < print_z + EPSILON )
2016-12-08 18:02:16 +00:00
continue ;
// Compute cusp-height for this facet and check against height.
2019-12-13 12:43:16 +00:00
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 */
2016-12-08 18:02:16 +00:00
height = z_diff ;
2019-12-13 12:43:16 +00:00
} 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 ;
2016-12-08 18:02:16 +00:00
}
}
// lower height limit due to printer capabilities again
2016-12-12 16:53:38 +00:00
height = std : : max ( height , float ( m_slicing_params . min_layer_height ) ) ;
2016-12-08 18:02:16 +00:00
}
2019-11-26 11:24:07 +00:00
2019-12-13 12:43:16 +00:00
# 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 */
2016-12-08 18:02:16 +00:00
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 ) {
2019-12-13 12:43:16 +00:00
std : : pair < float , float > zspan = m_faces [ i ] . z_span ;
2019-11-13 12:53:02 +00:00
// facet's minimum is higher than max forward distance -> end loop
2016-12-12 16:53:38 +00:00
if ( zspan . first > z + m_slicing_params . max_layer_height )
2016-12-08 18:02:16 +00:00
break ;
// min_z == max_z -> horizontal facet
2019-12-13 12:43:16 +00:00
if ( zspan . first > z & & zspan . first = = zspan . second )
2016-12-08 18:02:16 +00:00
return zspan . first - z ;
}
// objects maximum?
2019-11-13 12:53:02 +00:00
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 ;
2016-12-08 18:02:16 +00:00
}
} ; // namespace Slic3r