2016-12-08 19:02:16 +01:00
#include "libslic3r.h"
2019-11-13 13:53:02 +01:00
#include "Model.hpp"
2016-12-08 19:02:16 +01:00
#include "TriangleMesh.hpp"
#include "SlicingAdaptive.hpp"
2019-12-13 13:43:16 +01:00
#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 */
2016-12-08 19:02:16 +01:00
namespace Slic3r
{
2019-12-13 13:43:16 +01:00
static inline std :: pair < float , float > face_z_span ( const stl_facet & f )
2016-12-08 19:02:16 +01:00
{
return std :: pair < float , float > (
2019-12-13 13:43:16 +01:00
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 )));
2016-12-08 19:02:16 +01:00
}
2019-12-13 13:43:16 +01: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 20:04:11 +01:00
//static constexpr const double SURFACE_CONST = 0.18403;
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
{
2019-12-13 13:43:16 +01: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;
2019-11-13 13:53:02 +01:00
2019-12-13 13:43:16 +01:00
// 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;
2019-11-13 13:53:02 +01:00
2019-12-13 13:43:16 +01:00
// 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 ());
2019-11-29 14:24:24 +01:00
2019-11-13 13:53:02 +01:00
// 1) Collect faces from mesh.
2019-12-13 13:43:16 +01:00
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 ()) }));
2019-11-29 14:24:24 +01:00
}
2016-12-08 19:02:16 +01:00
// 2) Sort faces lexicographically by their Z span.
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
}
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
{
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
// find all facets intersecting the slice-layer
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
}
// lower height limit due to printer capabilities
2016-12-12 17:53:38 +01:00
height = std :: max ( height , float ( m_slicing_params . min_layer_height ));
2016-12-08 19:02:16 +01:00
// check for sloped facets inside the determined layer and correct height if necessary
2019-12-13 13:43:16 +01: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 13:53:02 +01:00
// facet's minimum is higher than slice_z + height -> end loop
2019-12-13 13:43:16 +01:00
if ( zspan . first >= print_z + height )
2016-12-08 19:02:16 +01:00
break ;
// skip touching facets which could otherwise cause small cusp values
2019-12-13 13:43:16 +01:00
if ( zspan . second < print_z + EPSILON )
2016-12-08 19:02:16 +01:00
continue ;
// Compute cusp-height for this facet and check against height.
2019-12-13 13:43:16 +01:00
float reduced_height = layer_height_from_slope ( m_faces [ ordered_id ], max_surface_deviation );
2019-11-26 12:24:07 +01:00
2019-12-13 13:43:16 +01:00
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 19:02:16 +01:00
height = z_diff ;
2019-12-13 13:43:16 +01: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 19:02:16 +01:00
}
}
// lower height limit due to printer capabilities again
2016-12-12 17:53:38 +01:00
height = std :: max ( height , float ( m_slicing_params . min_layer_height ));
2016-12-08 19:02:16 +01:00
}
2019-11-26 12:24:07 +01:00
2019-12-13 13:43:16 +01: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 19:02:16 +01: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 13:43:16 +01:00
std :: pair < float , float > zspan = m_faces [ i ]. z_span ;
2019-11-13 13:53:02 +01:00
// facet's minimum is higher than max forward distance -> end loop
2016-12-12 17:53:38 +01:00
if ( zspan . first > z + m_slicing_params . max_layer_height )
2016-12-08 19:02:16 +01:00
break ;
// min_z == max_z -> horizontal facet
2019-12-13 13:43:16 +01:00
if ( zspan . first > z && zspan . first == zspan . second )
2016-12-08 19:02:16 +01:00
return zspan . first - z ;
}
// objects maximum?
2019-11-13 13:53:02 +01: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 19:02:16 +01:00
}
}; // namespace Slic3r