2023-06-10 10:14:12 +08:00
# include "SupportSpotsGenerator.hpp"
# include "BoundingBox.hpp"
# include "ExPolygon.hpp"
# include "ExtrusionEntity.hpp"
# include "ExtrusionEntityCollection.hpp"
# include "GCode/ExtrusionProcessor.hpp"
# include "Line.hpp"
# include "Point.hpp"
# include "Polygon.hpp"
# include "PrincipalComponents2D.hpp"
# include "Print.hpp"
# include "PrintBase.hpp"
# include "PrintConfig.hpp"
# include "Tesselate.hpp"
# include "Utils.hpp"
# include "libslic3r.h"
# include "tbb/parallel_for.h"
# include "tbb/blocked_range.h"
# include "tbb/blocked_range2d.h"
# include "tbb/parallel_reduce.h"
# include <algorithm>
# include <boost/log/trivial.hpp>
# include <cmath>
# include <cstddef>
# include <cstdio>
# include <functional>
# include <limits>
# include <math.h>
# include <oneapi/tbb/concurrent_vector.h>
# include <oneapi/tbb/parallel_for.h>
# include <optional>
# include <unordered_map>
# include <unordered_set>
# include <stack>
# include <utility>
# include <vector>
# include "AABBTreeLines.hpp"
# include "KDTreeIndirect.hpp"
# include "libslic3r/Layer.hpp"
# include "libslic3r/ClipperUtils.hpp"
# include "Geometry/ConvexHull.hpp"
// #define DETAILED_DEBUG_LOGS
// #define DEBUG_FILES
# ifdef DEBUG_FILES
# include <boost/nowide/cstdio.hpp>
# include "libslic3r/Color.hpp"
2023-12-27 18:02:35 +08:00
constexpr bool debug_files = true ;
# else
constexpr bool debug_files = false ;
2023-06-10 10:14:12 +08:00
# endif
2023-12-27 18:02:35 +08:00
namespace Slic3r : : SupportSpotsGenerator {
ExtrusionLine : : ExtrusionLine ( ) : a ( Vec2f : : Zero ( ) ) , b ( Vec2f : : Zero ( ) ) , len ( 0.0 ) , origin_entity ( nullptr ) { }
ExtrusionLine : : ExtrusionLine ( const Vec2f & a , const Vec2f & b , float len , const ExtrusionEntity * origin_entity )
2023-06-10 10:14:12 +08:00
: a ( a ) , b ( b ) , len ( len ) , origin_entity ( origin_entity )
{ }
2023-12-27 18:02:35 +08:00
ExtrusionLine : : ExtrusionLine ( const Vec2f & a , const Vec2f & b )
2023-06-10 10:14:12 +08:00
: a ( a ) , b ( b ) , len ( ( a - b ) . norm ( ) ) , origin_entity ( nullptr )
{ }
2023-12-27 18:02:35 +08:00
bool ExtrusionLine : : is_external_perimeter ( ) const
2023-06-10 10:14:12 +08:00
{
assert ( origin_entity ! = nullptr ) ;
return origin_entity - > role ( ) . is_external_perimeter ( ) ;
}
using LD = AABBTreeLines : : LinesDistancer < ExtrusionLine > ;
struct SupportGridFilter
{
private :
Vec3f cell_size ;
Vec3f origin ;
Vec3f size ;
Vec3i cell_count ;
std : : unordered_set < size_t > taken_cells { } ;
public :
SupportGridFilter ( const PrintObject * po , float voxel_size )
{
cell_size = Vec3f ( voxel_size , voxel_size , voxel_size ) ;
Vec2crd size_half = po - > size ( ) . head < 2 > ( ) . cwiseQuotient ( Vec2crd ( 2 , 2 ) ) + Vec2crd : : Ones ( ) ;
Vec3f min = unscale ( Vec3crd ( - size_half . x ( ) , - size_half . y ( ) , 0 ) ) . cast < float > ( ) - cell_size ;
Vec3f max = unscale ( Vec3crd ( size_half . x ( ) , size_half . y ( ) , po - > height ( ) ) ) . cast < float > ( ) + cell_size ;
origin = min ;
size = max - min ;
cell_count = size . cwiseQuotient ( cell_size ) . cast < int > ( ) + Vec3i : : Ones ( ) ;
}
Vec3i to_cell_coords ( const Vec3f & position ) const
{
Vec3i cell_coords = ( position - this - > origin ) . cwiseQuotient ( this - > cell_size ) . cast < int > ( ) ;
return cell_coords ;
}
size_t to_cell_index ( const Vec3i & cell_coords ) const
{
# ifdef DETAILED_DEBUG_LOGS
assert ( cell_coords . x ( ) > = 0 ) ;
assert ( cell_coords . x ( ) < cell_count . x ( ) ) ;
assert ( cell_coords . y ( ) > = 0 ) ;
assert ( cell_coords . y ( ) < cell_count . y ( ) ) ;
assert ( cell_coords . z ( ) > = 0 ) ;
assert ( cell_coords . z ( ) < cell_count . z ( ) ) ;
# endif
return cell_coords . z ( ) * cell_count . x ( ) * cell_count . y ( ) + cell_coords . y ( ) * cell_count . x ( ) + cell_coords . x ( ) ;
}
Vec3f get_cell_center ( const Vec3i & cell_coords ) const
{
return origin + cell_coords . cast < float > ( ) . cwiseProduct ( this - > cell_size ) + this - > cell_size . cwiseQuotient ( Vec3f ( 2.0f , 2.0f , 2.0f ) ) ;
}
void take_position ( const Vec3f & position ) { taken_cells . insert ( to_cell_index ( to_cell_coords ( position ) ) ) ; }
bool position_taken ( const Vec3f & position ) const
{
return taken_cells . find ( to_cell_index ( to_cell_coords ( position ) ) ) ! = taken_cells . end ( ) ;
}
} ;
2023-12-27 18:02:35 +08:00
void SliceConnection : : add ( const SliceConnection & other )
2023-06-10 10:14:12 +08:00
{
this - > area + = other . area ;
this - > centroid_accumulator + = other . centroid_accumulator ;
this - > second_moment_of_area_accumulator + = other . second_moment_of_area_accumulator ;
this - > second_moment_of_area_covariance_accumulator + = other . second_moment_of_area_covariance_accumulator ;
}
2023-12-27 18:02:35 +08:00
void SliceConnection : : print_info ( const std : : string & tag ) const
2023-06-10 10:14:12 +08:00
{
Vec3f centroid = centroid_accumulator / area ;
Vec2f variance = ( second_moment_of_area_accumulator / area - centroid . head < 2 > ( ) . cwiseProduct ( centroid . head < 2 > ( ) ) ) ;
float covariance = second_moment_of_area_covariance_accumulator / area - centroid . x ( ) * centroid . y ( ) ;
std : : cout < < tag < < std : : endl ;
std : : cout < < " area: " < < area < < std : : endl ;
std : : cout < < " centroid: " < < centroid . x ( ) < < " " < < centroid . y ( ) < < " " < < centroid . z ( ) < < std : : endl ;
std : : cout < < " variance: " < < variance . x ( ) < < " " < < variance . y ( ) < < std : : endl ;
std : : cout < < " covariance: " < < covariance < < std : : endl ;
}
2024-03-27 14:38:03 +08:00
Integrals : : Integrals ( const Polygon & polygon )
{
if ( polygon . points . size ( ) < 3 ) {
assert ( false & & " Polygon is expected to have non-zero area! " ) ;
* this = Integrals { } ;
return ;
}
2023-12-27 18:02:35 +08:00
Vec2f p0 = unscaled ( polygon . first_point ( ) ) . cast < float > ( ) ;
for ( size_t i = 2 ; i < polygon . points . size ( ) ; i + + ) {
Vec2f p1 = unscaled ( polygon . points [ i - 1 ] ) . cast < float > ( ) ;
Vec2f p2 = unscaled ( polygon . points [ i ] ) . cast < float > ( ) ;
float sign = cross2 ( p1 - p0 , p2 - p1 ) > 0 ? 1.0f : - 1.0f ;
auto [ area , first_moment_of_area , second_moment_area ,
second_moment_of_area_covariance ] = compute_moments_of_area_of_triangle ( p0 , p1 , p2 ) ;
this - > area + = sign * area ;
this - > x_i + = sign * first_moment_of_area ;
this - > x_i_squared + = sign * second_moment_area ;
this - > xy + = sign * second_moment_of_area_covariance ;
}
}
2024-03-27 14:38:03 +08:00
Integrals : : Integrals ( const Polygons & polygons )
{
for ( const Polygon & polygon : polygons ) {
* this = * this + Integrals { polygon } ;
}
}
Integrals : : Integrals ( const Polylines & polylines , const std : : vector < float > & widths ) {
assert ( polylines . size ( ) = = widths . size ( ) ) ;
for ( size_t i = 0 ; i < polylines . size ( ) ; + + i ) {
Lines polyline { polylines [ i ] . lines ( ) } ;
float width { widths [ i ] } ;
for ( const Line & line : polyline ) {
Vec2f line_direction = unscaled ( line . vector ( ) ) . cast < float > ( ) ;
Vec2f normal { line_direction . y ( ) , - line_direction . x ( ) } ;
normal . normalize ( ) ;
Vec2f line_a = unscaled ( line . a ) . cast < float > ( ) ;
Vec2f line_b = unscaled ( line . b ) . cast < float > ( ) ;
Vec2crd a = scaled ( Vec2f { line_a + normal * width / 2 } ) ;
Vec2crd b = scaled ( Vec2f { line_b + normal * width / 2 } ) ;
Vec2crd c = scaled ( Vec2f { line_b - normal * width / 2 } ) ;
Vec2crd d = scaled ( Vec2f { line_a - normal * width / 2 } ) ;
const Polygon ractangle ( { a , b , c , d } ) ;
Integrals integrals { ractangle } ;
* this = * this + integrals ;
}
}
}
Integrals : : Integrals ( float area , Vec2f x_i , Vec2f x_i_squared , float xy )
: area ( area ) , x_i ( std : : move ( x_i ) ) , x_i_squared ( std : : move ( x_i_squared ) ) , xy ( xy )
{ }
Integrals operator + ( const Integrals & a , const Integrals & b )
{
return Integrals { a . area + b . area , a . x_i + b . x_i , a . x_i_squared + b . x_i_squared , a . xy + b . xy } ;
2023-12-27 18:02:35 +08:00
}
2023-06-10 10:14:12 +08:00
SliceConnection estimate_slice_connection ( size_t slice_idx , const Layer * layer )
{
SliceConnection connection ;
const LayerSlice & slice = layer - > lslices_ex [ slice_idx ] ;
Polygons slice_polys = to_polygons ( layer - > lslices [ slice_idx ] ) ;
BoundingBox slice_bb = get_extents ( slice_polys ) ;
const Layer * lower_layer = layer - > lower_layer ;
2023-09-16 16:26:29 +08:00
std : : unordered_set < size_t > linked_slices_below ;
for ( const auto & link : slice . overlaps_below ) { linked_slices_below . insert ( link . slice_idx ) ; }
2023-06-10 10:14:12 +08:00
ExPolygons below { } ;
2023-09-16 16:26:29 +08:00
for ( const auto & linked_slice_idx_below : linked_slices_below ) { below . push_back ( lower_layer - > lslices [ linked_slice_idx_below ] ) ; }
2023-06-10 10:14:12 +08:00
Polygons below_polys = to_polygons ( below ) ;
BoundingBox below_bb = get_extents ( below_polys ) ;
Polygons overlap = intersection ( ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( slice_polys , below_bb ) ,
ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( below_polys , slice_bb ) ) ;
2023-12-27 18:02:35 +08:00
const Integrals integrals { overlap } ;
connection . area + = integrals . area ;
connection . centroid_accumulator + = Vec3f ( integrals . x_i . x ( ) , integrals . x_i . y ( ) , layer - > print_z * integrals . area ) ;
connection . second_moment_of_area_accumulator + = integrals . x_i_squared ;
connection . second_moment_of_area_covariance_accumulator + = integrals . xy ;
2023-06-10 10:14:12 +08:00
return connection ;
} ;
using PrecomputedSliceConnections = std : : vector < std : : vector < SliceConnection > > ;
PrecomputedSliceConnections precompute_slices_connections ( const PrintObject * po )
{
PrecomputedSliceConnections result { } ;
for ( size_t lidx = 0 ; lidx < po - > layer_count ( ) ; lidx + + ) {
result . emplace_back ( std : : vector < SliceConnection > { } ) ;
for ( size_t slice_idx = 0 ; slice_idx < po - > get_layer ( lidx ) - > lslices_ex . size ( ) ; slice_idx + + ) {
result [ lidx ] . push_back ( SliceConnection { } ) ;
}
}
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , po - > layers ( ) . size ( ) ) , [ po , & result ] ( tbb : : blocked_range < size_t > r ) {
for ( size_t lidx = r . begin ( ) ; lidx < r . end ( ) ; lidx + + ) {
const Layer * l = po - > get_layer ( lidx ) ;
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , l - > lslices_ex . size ( ) ) , [ lidx , l , & result ] ( tbb : : blocked_range < size_t > r2 ) {
for ( size_t slice_idx = r2 . begin ( ) ; slice_idx < r2 . end ( ) ; slice_idx + + ) {
result [ lidx ] [ slice_idx ] = estimate_slice_connection ( slice_idx , l ) ;
}
} ) ;
}
} ) ;
return result ;
} ;
float get_flow_width ( const LayerRegion * region , ExtrusionRole role )
{
if ( role = = ExtrusionRole : : BridgeInfill ) return region - > flow ( FlowRole : : frExternalPerimeter ) . width ( ) ;
if ( role = = ExtrusionRole : : ExternalPerimeter ) return region - > flow ( FlowRole : : frExternalPerimeter ) . width ( ) ;
if ( role = = ExtrusionRole : : GapFill ) return region - > flow ( FlowRole : : frInfill ) . width ( ) ;
if ( role = = ExtrusionRole : : Perimeter ) return region - > flow ( FlowRole : : frPerimeter ) . width ( ) ;
if ( role = = ExtrusionRole : : SolidInfill ) return region - > flow ( FlowRole : : frSolidInfill ) . width ( ) ;
if ( role = = ExtrusionRole : : InternalInfill ) return region - > flow ( FlowRole : : frInfill ) . width ( ) ;
if ( role = = ExtrusionRole : : TopSolidInfill ) return region - > flow ( FlowRole : : frTopSolidInfill ) . width ( ) ;
// default
return region - > flow ( FlowRole : : frPerimeter ) . width ( ) ;
}
float estimate_curled_up_height (
float distance , float curvature , float layer_height , float flow_width , float prev_line_curled_height , Params params )
{
float curled_up_height = 0 ;
if ( fabs ( distance ) < 3.0 * flow_width ) {
curled_up_height = std : : max ( prev_line_curled_height - layer_height * 0.75f , 0.0f ) ;
}
if ( distance > params . malformation_distance_factors . first * flow_width & &
distance < params . malformation_distance_factors . second * flow_width ) {
// imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section
// and the rest will be called curling section
// float anchored_section = flow_width - point.distance;
float curling_section = distance ;
// after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle
// The anchored part not, because the melted material holds to the previous layer well.
// We can assume for simplicity perfect equalization of layer height and raising part width, from which:
float swelling_radius = ( layer_height + curling_section ) / 2.0f ;
curled_up_height + = std : : max ( 0.f , ( swelling_radius - layer_height ) / 2.0f ) ;
// On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section.
// The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and
// thus shrinking force is greater. This tension will cause the curling section to curle up
if ( curvature > 0.01 ) {
float radius = ( 1.0 / curvature ) ;
float curling_t = sqrt ( radius / 100 ) ;
float b = curling_t * flow_width ;
float a = curling_section ;
float c = sqrt ( std : : max ( 0.0f , a * a - b * b ) ) ;
curled_up_height + = c ;
}
curled_up_height = std : : min ( curled_up_height , params . max_curled_height_factor * layer_height ) ;
}
return curled_up_height ;
}
std : : vector < ExtrusionLine > check_extrusion_entity_stability ( const ExtrusionEntity * entity ,
const LayerRegion * layer_region ,
const LD & prev_layer_lines ,
const AABBTreeLines : : LinesDistancer < Linef > & prev_layer_boundary ,
const Params & params )
{
assert ( ! entity - > is_collection ( ) ) ;
if ( entity - > role ( ) . is_bridge ( ) & & ! entity - > role ( ) . is_perimeter ( ) ) {
// pure bridges are handled separately, beacuse we need to align the forward and backward direction support points
if ( entity - > length ( ) < scale_ ( params . min_distance_to_allow_local_supports ) ) {
return { } ;
}
const float flow_width = get_flow_width ( layer_region , entity - > role ( ) ) ;
2023-12-27 18:02:35 +08:00
std : : vector < ExtrusionProcessor : : ExtendedPoint > annotated_points =
ExtrusionProcessor : : estimate_points_properties < true , true , true , true > ( entity - > as_polyline ( ) . points , prev_layer_boundary ,
flow_width , params . bridge_distance ) ;
2023-06-10 10:14:12 +08:00
std : : vector < ExtrusionLine > lines_out ;
lines_out . reserve ( annotated_points . size ( ) ) ;
float bridged_distance = 0.0f ;
std : : optional < Vec2d > bridging_dir { } ;
for ( size_t i = 0 ; i < annotated_points . size ( ) ; + + i ) {
2023-12-27 18:02:35 +08:00
ExtrusionProcessor : : ExtendedPoint & curr_point = annotated_points [ i ] ;
const ExtrusionProcessor : : ExtendedPoint & prev_point = i > 0 ? annotated_points [ i - 1 ] : annotated_points [ i ] ;
2023-06-10 10:14:12 +08:00
SupportPointCause potential_cause = std : : abs ( curr_point . curvature ) > 0.1 ? SupportPointCause : : FloatingBridgeAnchor :
SupportPointCause : : LongBridge ;
float line_len = ( prev_point . position - curr_point . position ) . norm ( ) ;
Vec2d line_dir = line_len > EPSILON ? Vec2d ( ( curr_point . position - prev_point . position ) / double ( line_len ) ) : Vec2d : : Zero ( ) ;
ExtrusionLine line_out { prev_point . position . cast < float > ( ) , curr_point . position . cast < float > ( ) , line_len , entity } ;
float max_bridge_len = std : : max ( params . support_points_interface_radius * 2.0f ,
params . bridge_distance /
( ( 1.0f + std : : abs ( curr_point . curvature ) ) * ( 1.0f + std : : abs ( curr_point . curvature ) ) *
( 1.0f + std : : abs ( curr_point . curvature ) ) ) ) ;
if ( ! bridging_dir . has_value ( ) & & curr_point . distance > flow_width & & line_len > params . bridge_distance * 0.6 ) {
bridging_dir = line_dir ;
}
if ( curr_point . distance > flow_width & & potential_cause = = SupportPointCause : : LongBridge & & bridging_dir . has_value ( ) & &
bridging_dir - > dot ( line_dir ) < 0.8 ) { // skip backward direction of bridge - supported by forward points enough
bridged_distance + = line_len ;
} else if ( curr_point . distance > flow_width ) {
bridged_distance + = line_len ;
if ( bridged_distance > max_bridge_len ) {
bridged_distance = 0.0f ;
line_out . support_point_generated = potential_cause ;
}
} else {
bridged_distance = 0.0f ;
}
lines_out . push_back ( line_out ) ;
}
return lines_out ;
} else { // single extrusion path, with possible varying parameters
if ( entity - > length ( ) < scale_ ( params . min_distance_to_allow_local_supports ) ) {
return { } ;
}
const float flow_width = get_flow_width ( layer_region , entity - > role ( ) ) ;
// Compute only unsigned distance - prev_layer_lines can contain unconnected paths, thus the sign of the distance is unreliable
2023-12-27 18:02:35 +08:00
std : : vector < ExtrusionProcessor : : ExtendedPoint > annotated_points =
ExtrusionProcessor : : estimate_points_properties < true , true , false , false > ( entity - > as_polyline ( ) . points , prev_layer_lines ,
flow_width , params . bridge_distance ) ;
2023-06-10 10:14:12 +08:00
std : : vector < ExtrusionLine > lines_out ;
lines_out . reserve ( annotated_points . size ( ) ) ;
float bridged_distance = annotated_points . front ( ) . position ! = annotated_points . back ( ) . position ? ( params . bridge_distance + 1.0f ) :
0.0f ;
for ( size_t i = 0 ; i < annotated_points . size ( ) ; + + i ) {
2023-12-27 18:02:35 +08:00
ExtrusionProcessor : : ExtendedPoint & curr_point = annotated_points [ i ] ;
const ExtrusionProcessor : : ExtendedPoint & prev_point = i > 0 ? annotated_points [ i - 1 ] : annotated_points [ i ] ;
2023-06-10 10:14:12 +08:00
float line_len = ( prev_point . position - curr_point . position ) . norm ( ) ;
ExtrusionLine line_out { prev_point . position . cast < float > ( ) , curr_point . position . cast < float > ( ) , line_len , entity } ;
Vec2f middle = 0.5 * ( line_out . a + line_out . b ) ;
auto [ middle_distance , bottom_line_idx , x ] = prev_layer_lines . distance_from_lines_extra < false > ( middle ) ;
ExtrusionLine bottom_line = prev_layer_lines . get_lines ( ) . empty ( ) ? ExtrusionLine { } : prev_layer_lines . get_line ( bottom_line_idx ) ;
// correctify the distance sign using slice polygons
float sign = ( prev_layer_boundary . distance_from_lines < true > ( curr_point . position ) + 0.5f * flow_width ) < 0.0f ? - 1.0f : 1.0f ;
curr_point . distance * = sign ;
SupportPointCause potential_cause = SupportPointCause : : FloatingExtrusion ;
// Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
// if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
// potential_cause = SupportPointCause::FloatingExtrusion;
// }
float max_bridge_len = std : : max ( params . support_points_interface_radius * 2.0f ,
params . bridge_distance /
( ( 1.0f + std : : abs ( curr_point . curvature ) ) * ( 1.0f + std : : abs ( curr_point . curvature ) ) *
( 1.0f + std : : abs ( curr_point . curvature ) ) ) ) ;
if ( curr_point . distance > 1.2f * flow_width ) {
line_out . form_quality = 0.8f ;
bridged_distance + = line_len ;
if ( bridged_distance > max_bridge_len ) {
line_out . support_point_generated = potential_cause ;
bridged_distance = 0.0f ;
}
} else if ( curr_point . distance > flow_width * 0.8f ) {
bridged_distance + = line_len ;
line_out . form_quality = bottom_line . form_quality - 0.3f ;
if ( line_out . form_quality < 0 & & bridged_distance > max_bridge_len ) {
line_out . support_point_generated = potential_cause ;
line_out . form_quality = 0.5f ;
bridged_distance = 0.0f ;
}
} else {
bridged_distance = 0.0f ;
}
line_out . curled_up_height = estimate_curled_up_height ( middle_distance , 0.5 * ( prev_point . curvature + curr_point . curvature ) ,
layer_region - > layer ( ) - > height , flow_width , bottom_line . curled_up_height ,
params ) ;
lines_out . push_back ( line_out ) ;
}
return lines_out ;
}
}
2023-12-27 18:02:35 +08:00
/**
* Calculates the second moment of area over an arbitrary polygon .
*
* Important note : The calculated moment is for an axis with origin at
* the polygon centroid !
*
* @ param integrals Integrals over the polygon area .
* @ param axis_direction Direction of the rotation axis going through centroid .
*/
float compute_second_moment (
const Integrals & integrals ,
const Vec2f & axis_direction
) {
// Second moment of area for any axis intersecting coordinate system origin
// can be evaluated using the second moments of area calculated for the coordinate
// system axis and the moment product (int xy).
// The equation is derived appling known formulas for the moment of inertia
// to a plannar problem. One can reason about second moment
// of area by by setting density to 1 in the moment of inertia formulas.
const auto area = integrals . area ;
const auto I_xx = integrals . x_i_squared . y ( ) ;
const auto I_yy = integrals . x_i_squared . x ( ) ;
const auto I_xy = - integrals . xy ;
const Vec2f centroid = integrals . x_i / area ;
Matrix2f moment_tensor { } ;
moment_tensor < <
I_xx , I_xy ,
I_xy , I_yy ;
const float moment_at_0_0 = axis_direction . transpose ( ) * moment_tensor * axis_direction ;
// Apply parallel axis theorem to move the moment to centroid
using line_alg : : distance_to_infinite_squared ;
const Linef axis_at_0_0 = { { 0 , 0 } , axis_direction . cast < double > ( ) } ;
const double distance = distance_to_infinite_squared ( axis_at_0_0 , centroid . cast < double > ( ) ) ;
return moment_at_0_0 - area * distance ;
}
ObjectPart : : ObjectPart (
const std : : vector < const ExtrusionEntityCollection * > & extrusion_collections ,
const bool connected_to_bed ,
const coordf_t print_head_z ,
const coordf_t layer_height ,
const std : : optional < Polygons > & brim
) {
if ( connected_to_bed ) {
this - > connected_to_bed = true ;
}
const auto bottom_z = print_head_z - layer_height ;
const auto center_z = print_head_z - layer_height / 2 ;
for ( const ExtrusionEntityCollection * collection : extrusion_collections ) {
if ( collection - > empty ( ) ) {
continue ;
}
2024-03-27 14:38:03 +08:00
for ( const ExtrusionEntity * entity : collection - > flatten ( ) ) {
Polylines polylines ;
std : : vector < float > widths ;
if (
const auto * path = dynamic_cast < const ExtrusionPath * > ( entity ) ;
path ! = nullptr
) {
polylines . push_back ( path - > as_polyline ( ) ) ;
widths . push_back ( path - > width ( ) ) ;
} else if (
const auto * loop = dynamic_cast < const ExtrusionLoop * > ( entity ) ;
loop ! = nullptr
) {
for ( const ExtrusionPath & path : loop - > paths ) {
polylines . push_back ( path . as_polyline ( ) ) ;
widths . push_back ( path . width ( ) ) ;
}
} else if (
const auto * multi_path = dynamic_cast < const ExtrusionMultiPath * > ( entity ) ;
multi_path ! = nullptr
) {
for ( const ExtrusionPath & path : multi_path - > paths ) {
polylines . push_back ( path . as_polyline ( ) ) ;
widths . push_back ( path . width ( ) ) ;
}
} else {
throw std : : runtime_error (
" Failed to construct object part from extrusions! "
" Unknown extrusion type. "
) ;
}
2023-12-27 18:02:35 +08:00
2024-03-27 14:38:03 +08:00
const Integrals integrals { polylines , widths } ;
2023-12-27 18:02:35 +08:00
const float volume = integrals . area * layer_height ;
this - > volume + = volume ;
this - > volume_centroid_accumulator + = to_3d ( integrals . x_i , center_z * integrals . area ) / integrals . area * volume ;
if ( this - > connected_to_bed ) {
this - > sticking_area + = integrals . area ;
this - > sticking_centroid_accumulator + = to_3d ( integrals . x_i , bottom_z * integrals . area ) ;
this - > sticking_second_moment_of_area_accumulator + = integrals . x_i_squared ;
this - > sticking_second_moment_of_area_covariance_accumulator + = integrals . xy ;
}
}
2024-03-27 14:38:03 +08:00
}
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
if ( brim ) {
Integrals integrals { * brim } ;
this - > sticking_area + = integrals . area ;
this - > sticking_centroid_accumulator + = to_3d ( integrals . x_i , bottom_z * integrals . area ) ;
this - > sticking_second_moment_of_area_accumulator + = integrals . x_i_squared ;
this - > sticking_second_moment_of_area_covariance_accumulator + = integrals . xy ;
}
}
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
void ObjectPart : : add ( const ObjectPart & other )
2023-06-10 10:14:12 +08:00
{
this - > connected_to_bed = this - > connected_to_bed | | other . connected_to_bed ;
this - > volume_centroid_accumulator + = other . volume_centroid_accumulator ;
this - > volume + = other . volume ;
this - > sticking_area + = other . sticking_area ;
this - > sticking_centroid_accumulator + = other . sticking_centroid_accumulator ;
this - > sticking_second_moment_of_area_accumulator + = other . sticking_second_moment_of_area_accumulator ;
this - > sticking_second_moment_of_area_covariance_accumulator + = other . sticking_second_moment_of_area_covariance_accumulator ;
}
2023-12-27 18:02:35 +08:00
void ObjectPart : : add_support_point ( const Vec3f & position , float sticking_area )
2023-06-10 10:14:12 +08:00
{
this - > sticking_area + = sticking_area ;
this - > sticking_centroid_accumulator + = sticking_area * position ;
this - > sticking_second_moment_of_area_accumulator + = sticking_area * position . head < 2 > ( ) . cwiseProduct ( position . head < 2 > ( ) ) ;
this - > sticking_second_moment_of_area_covariance_accumulator + = sticking_area * position . x ( ) * position . y ( ) ;
}
2023-12-27 18:02:35 +08:00
float ObjectPart : : compute_elastic_section_modulus (
const Vec2f & line_dir ,
2023-06-10 10:14:12 +08:00
const Vec3f & extreme_point ,
2023-12-27 18:02:35 +08:00
const Integrals & integrals
) const {
float second_moment_of_area = compute_second_moment ( integrals , Vec2f { - line_dir . y ( ) , line_dir . x ( ) } ) ;
if ( second_moment_of_area < EPSILON ) { return 0.0f ; }
Vec2f centroid = integrals . x_i / integrals . area ;
2023-06-10 10:14:12 +08:00
float extreme_fiber_dist = line_alg : : distance_to ( Linef ( centroid . head < 2 > ( ) . cast < double > ( ) ,
( centroid . head < 2 > ( ) + Vec2f ( line_dir . y ( ) , - line_dir . x ( ) ) ) . cast < double > ( ) ) ,
extreme_point . head < 2 > ( ) . cast < double > ( ) ) ;
2023-12-27 18:02:35 +08:00
float elastic_section_modulus = second_moment_of_area / extreme_fiber_dist ;
2023-06-10 10:14:12 +08:00
# ifdef DETAILED_DEBUG_LOGS
BOOST_LOG_TRIVIAL ( debug ) < < " extreme_fiber_dist: " < < extreme_fiber_dist ;
BOOST_LOG_TRIVIAL ( debug ) < < " elastic_section_modulus: " < < elastic_section_modulus ;
# endif
return elastic_section_modulus ;
}
2023-12-27 18:02:35 +08:00
std : : tuple < float , SupportPointCause > ObjectPart : : is_stable_while_extruding ( const SliceConnection & connection ,
2023-06-10 10:14:12 +08:00
const ExtrusionLine & extruded_line ,
const Vec3f & extreme_point ,
float layer_z ,
const Params & params ) const
{
2023-12-27 18:02:35 +08:00
// Note that exteme point is calculated for the current layer, while it should
// be computed for the first layer. The shape of the first layer however changes a lot,
// during support points additions (for organic supports it is not even clear how)
// and during merging. Using the current layer is heuristics and also small optimization,
// as the AABB tree for it is calculated anyways. This heuristic should usually be
// on the safe side.
2023-06-10 10:14:12 +08:00
Vec2f line_dir = ( extruded_line . b - extruded_line . a ) . normalized ( ) ;
const Vec3f & mass_centroid = this - > volume_centroid_accumulator / this - > volume ;
float mass = this - > volume * params . filament_density ;
float weight = mass * params . gravity_constant ;
float movement_force = params . max_acceleration * mass ;
float extruder_conflict_force = params . standard_extruder_conflict_force +
std : : min ( extruded_line . curled_up_height , 1.0f ) * params . malformations_additive_conflict_extruder_force ;
// section for bed calculations
{
if ( this - > sticking_area < EPSILON ) return { 1.0f , SupportPointCause : : UnstableFloatingPart } ;
2023-12-27 18:02:35 +08:00
Integrals integrals ;
integrals . area = this - > sticking_area ;
integrals . x_i = this - > sticking_centroid_accumulator . head < 2 > ( ) ;
integrals . x_i_squared = this - > sticking_second_moment_of_area_accumulator ;
integrals . xy = this - > sticking_second_moment_of_area_covariance_accumulator ;
2023-06-10 10:14:12 +08:00
Vec3f bed_centroid = this - > sticking_centroid_accumulator / this - > sticking_area ;
2023-12-27 18:02:35 +08:00
float bed_yield_torque = - compute_elastic_section_modulus ( line_dir , extreme_point , integrals ) * params . get_bed_adhesion_yield_strength ( ) ;
2023-06-10 10:14:12 +08:00
Vec2f bed_weight_arm = ( mass_centroid . head < 2 > ( ) - bed_centroid . head < 2 > ( ) ) ;
float bed_weight_arm_len = bed_weight_arm . norm ( ) ;
2023-12-27 18:02:35 +08:00
float bed_weight_dir_xy_variance = compute_second_moment ( integrals , { - bed_weight_arm . y ( ) , bed_weight_arm . x ( ) } ) / this - > sticking_area ;
2023-06-10 10:14:12 +08:00
float bed_weight_sign = bed_weight_arm_len < 2.0f * sqrt ( bed_weight_dir_xy_variance ) ? - 1.0f : 1.0f ;
float bed_weight_torque = bed_weight_sign * bed_weight_arm_len * weight ;
float bed_movement_arm = std : : max ( 0.0f , mass_centroid . z ( ) - bed_centroid . z ( ) ) ;
float bed_movement_torque = movement_force * bed_movement_arm ;
float bed_conflict_torque_arm = layer_z - bed_centroid . z ( ) ;
float bed_extruder_conflict_torque = extruder_conflict_force * bed_conflict_torque_arm ;
float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque + bed_yield_torque ;
# ifdef DETAILED_DEBUG_LOGS
BOOST_LOG_TRIVIAL ( debug ) < < " bed_centroid: " < < bed_centroid . x ( ) < < " " < < bed_centroid . y ( ) < < " " < < bed_centroid . z ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_yield_torque: " < < bed_yield_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_weight_arm: " < < bed_weight_arm_len ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_weight_torque: " < < bed_weight_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_movement_arm: " < < bed_movement_arm ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_movement_torque: " < < bed_movement_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_conflict_torque_arm: " < < bed_conflict_torque_arm ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: extruded_line.curled_up_height: " < < extruded_line . curled_up_height ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: extruded_line.form_quality: " < < extruded_line . form_quality ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: extruder_conflict_force: " < < extruder_conflict_force ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: bed_extruder_conflict_torque: " < < bed_extruder_conflict_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: total_torque: " < < bed_total_torque < < " layer_z: " < < layer_z ;
# endif
if ( bed_total_torque > 0 ) {
return { bed_total_torque / bed_conflict_torque_arm ,
( this - > connected_to_bed ? SupportPointCause : : SeparationFromBed : SupportPointCause : : UnstableFloatingPart ) } ;
}
}
// section for weak connection calculations
{
if ( connection . area < EPSILON ) return { 1.0f , SupportPointCause : : UnstableFloatingPart } ;
Vec3f conn_centroid = connection . centroid_accumulator / connection . area ;
if ( layer_z - conn_centroid . z ( ) < 3.0f ) { return { - 1.0f , SupportPointCause : : WeakObjectPart } ; }
2023-12-27 18:02:35 +08:00
Integrals integrals ;
integrals . area = connection . area ;
integrals . x_i = connection . centroid_accumulator . head < 2 > ( ) ;
integrals . x_i_squared = connection . second_moment_of_area_accumulator ;
integrals . xy = connection . second_moment_of_area_covariance_accumulator ;
float conn_yield_torque = compute_elastic_section_modulus ( line_dir , extreme_point , integrals ) * params . material_yield_strength ;
2023-06-10 10:14:12 +08:00
float conn_weight_arm = ( conn_centroid . head < 2 > ( ) - mass_centroid . head < 2 > ( ) ) . norm ( ) ;
if ( layer_z - conn_centroid . z ( ) < 30.0 ) {
conn_weight_arm = 0.0f ; // Given that we do not have very good info about the weight distribution between the connection and current layer,
// do not consider the weight until quite far away from the weak connection segment
}
float conn_weight_torque = conn_weight_arm * weight * ( 1.0f - conn_centroid . z ( ) / layer_z ) * ( 1.0f - conn_centroid . z ( ) / layer_z ) ;
float conn_movement_arm = std : : max ( 0.0f , mass_centroid . z ( ) - conn_centroid . z ( ) ) ;
float conn_movement_torque = movement_force * conn_movement_arm ;
float conn_conflict_torque_arm = layer_z - conn_centroid . z ( ) ;
float conn_extruder_conflict_torque = extruder_conflict_force * conn_conflict_torque_arm ;
float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque - conn_yield_torque ;
# ifdef DETAILED_DEBUG_LOGS
BOOST_LOG_TRIVIAL ( debug ) < < " conn_centroid: " < < conn_centroid . x ( ) < < " " < < conn_centroid . y ( ) < < " " < < conn_centroid . z ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_yield_torque: " < < conn_yield_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_weight_arm: " < < conn_weight_arm ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_weight_torque: " < < conn_weight_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_movement_arm: " < < conn_movement_arm ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_movement_torque: " < < conn_movement_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_conflict_torque_arm: " < < conn_conflict_torque_arm ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: conn_extruder_conflict_torque: " < < conn_extruder_conflict_torque ;
BOOST_LOG_TRIVIAL ( debug ) < < " SSG: total_torque: " < < conn_total_torque < < " layer_z: " < < layer_z ;
# endif
return { conn_total_torque / conn_conflict_torque_arm , SupportPointCause : : WeakObjectPart } ;
}
}
2023-12-27 18:02:35 +08:00
std : : vector < const ExtrusionEntityCollection * > gather_extrusions ( const LayerSlice & slice , const Layer * layer ) {
// TODO reserve might be good, benchmark
std : : vector < const ExtrusionEntityCollection * > result ;
2023-06-10 10:14:12 +08:00
for ( const auto & island : slice . islands ) {
const LayerRegion * perimeter_region = layer - > get_region ( island . perimeters . region ( ) ) ;
2023-06-27 11:07:34 +08:00
for ( size_t perimeter_idx : island . perimeters ) {
2023-12-27 18:02:35 +08:00
auto collection = static_cast < const ExtrusionEntityCollection * > (
perimeter_region - > perimeters ( ) . entities [ perimeter_idx ]
) ;
result . push_back ( collection ) ;
2023-06-10 10:14:12 +08:00
}
for ( const LayerExtrusionRange & fill_range : island . fills ) {
const LayerRegion * fill_region = layer - > get_region ( fill_range . region ( ) ) ;
2023-06-27 11:07:34 +08:00
for ( size_t fill_idx : fill_range ) {
2023-12-27 18:02:35 +08:00
auto collection = static_cast < const ExtrusionEntityCollection * > (
fill_region - > fills ( ) . entities [ fill_idx ]
) ;
result . push_back ( collection ) ;
2023-06-10 10:14:12 +08:00
}
}
2023-12-27 18:02:35 +08:00
const ExtrusionEntityCollection & collection = perimeter_region - > thin_fills ( ) ;
result . push_back ( & collection ) ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
return result ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
bool has_brim ( const Layer * layer , const Params & params ) {
return
int ( layer - > id ( ) ) = = params . raft_layers_count
& & params . raft_layers_count = = 0
& & params . brim_type ! = BrimType : : btNoBrim
& & params . brim_width > 0.0 ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
Polygons get_brim ( const ExPolygon & slice_polygon , const BrimType brim_type , const float brim_width ) {
// TODO: The algorithm here should take into account that multiple slices may
// have coliding Brim areas and the final brim area is smaller,
2023-06-10 10:14:12 +08:00
// thus has lower adhesion. For now this effect will be neglected.
ExPolygons brim ;
2023-12-27 18:02:35 +08:00
if ( brim_type = = BrimType : : btOuterAndInner | | brim_type = = BrimType : : btOuterOnly ) {
Polygon brim_hole = slice_polygon . contour ;
2023-06-10 10:14:12 +08:00
brim_hole . reverse ( ) ;
2023-12-27 18:02:35 +08:00
Polygons c = expand ( slice_polygon . contour , scale_ ( brim_width ) ) ; // For very small polygons, the expand may result in empty vector, even thought the input is correct.
2023-06-10 10:14:12 +08:00
if ( ! c . empty ( ) ) {
brim . push_back ( ExPolygon { c . front ( ) , brim_hole } ) ;
}
}
2023-12-27 18:02:35 +08:00
if ( brim_type = = BrimType : : btOuterAndInner | | brim_type = = BrimType : : btInnerOnly ) {
Polygons brim_contours = slice_polygon . holes ;
2023-06-10 10:14:12 +08:00
polygons_reverse ( brim_contours ) ;
for ( const Polygon & brim_contour : brim_contours ) {
2023-12-27 18:02:35 +08:00
Polygons brim_holes = shrink ( { brim_contour } , scale_ ( brim_width ) ) ;
2023-06-10 10:14:12 +08:00
polygons_reverse ( brim_holes ) ;
ExPolygon inner_brim { brim_contour } ;
inner_brim . holes = brim_holes ;
brim . push_back ( inner_brim ) ;
}
}
2023-12-27 18:02:35 +08:00
return to_polygons ( brim ) ;
2023-06-10 10:14:12 +08:00
}
class ActiveObjectParts
{
size_t next_part_idx = 0 ;
std : : unordered_map < size_t , ObjectPart > active_object_parts ;
std : : unordered_map < size_t , size_t > active_object_parts_id_mapping ;
public :
size_t get_flat_id ( size_t id )
{
size_t index = active_object_parts_id_mapping . at ( id ) ;
while ( index ! = active_object_parts_id_mapping . at ( index ) ) { index = active_object_parts_id_mapping . at ( index ) ; }
size_t i = id ;
while ( index ! = active_object_parts_id_mapping . at ( i ) ) {
size_t next = active_object_parts_id_mapping [ i ] ;
active_object_parts_id_mapping [ i ] = index ;
i = next ;
}
return index ;
}
ObjectPart & access ( size_t id ) { return this - > active_object_parts . at ( this - > get_flat_id ( id ) ) ; }
size_t insert ( const ObjectPart & new_part )
{
this - > active_object_parts . emplace ( next_part_idx , new_part ) ;
this - > active_object_parts_id_mapping . emplace ( next_part_idx , next_part_idx ) ;
return next_part_idx + + ;
}
void merge ( size_t from , size_t to )
{
size_t to_flat = this - > get_flat_id ( to ) ;
size_t from_flat = this - > get_flat_id ( from ) ;
active_object_parts . at ( to_flat ) . add ( active_object_parts . at ( from_flat ) ) ;
active_object_parts . erase ( from_flat ) ;
active_object_parts_id_mapping [ from ] = to_flat ;
}
} ;
2023-12-27 18:02:35 +08:00
// Function that is used when new support point is generated. It will update the ObjectPart stability, weakest conneciton info,
// and the support presence grid and add the point to the issues.
void reckon_new_support_point ( ObjectPart & part ,
SliceConnection & weakest_conn ,
SupportPoints & supp_points ,
SupportGridFilter & supports_presence_grid ,
const SupportPoint & support_point ,
bool is_global = false )
2023-06-10 10:14:12 +08:00
{
2023-12-27 18:02:35 +08:00
// if position is taken and point is for global stability (force > 0) or we are too close to the bed, do not add
// This allows local support points (e.g. bridging) to be generated densely
if ( ( supports_presence_grid . position_taken ( support_point . position ) & & is_global ) ) {
return ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
float area = support_point . spot_radius * support_point . spot_radius * float ( PI ) ;
// add the stability effect of the point only if the spot is not taken, so that the densely created local support points do
// not add unrealistic amount of stability to the object (due to overlaping of local support points)
if ( ! ( supports_presence_grid . position_taken ( support_point . position ) ) ) {
part . add_support_point ( support_point . position , area ) ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
supp_points . push_back ( support_point ) ;
supports_presence_grid . take_position ( support_point . position ) ;
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
// The support point also increases the stability of the weakest connection of the object, which should be reflected
if ( weakest_conn . area > EPSILON ) { // Do not add it to the weakest connection if it is not valid - does not exist
weakest_conn . area + = area ;
weakest_conn . centroid_accumulator + = support_point . position * area ;
weakest_conn . second_moment_of_area_accumulator + = area *
support_point . position . head < 2 > ( ) . cwiseProduct ( support_point . position . head < 2 > ( ) ) ;
weakest_conn . second_moment_of_area_covariance_accumulator + = area * support_point . position . x ( ) * support_point . position . y ( ) ;
2023-06-10 10:14:12 +08:00
}
}
2023-12-27 18:02:35 +08:00
struct LocalSupports {
std : : vector < tbb : : concurrent_vector < ExtrusionLine > > unstable_lines_per_slice ;
std : : vector < tbb : : concurrent_vector < ExtrusionLine > > ext_perim_lines_per_slice ;
2023-06-10 10:14:12 +08:00
} ;
2023-12-27 18:02:35 +08:00
struct EnitityToCheck
{
const ExtrusionEntity * e ;
const LayerRegion * region ;
size_t slice_idx ;
} ;
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
// TODO DRY: Very similar to gather extrusions.
std : : vector < EnitityToCheck > gather_entities_to_check ( const Layer * layer ) {
2023-06-10 10:14:12 +08:00
auto get_flat_entities = [ ] ( const ExtrusionEntity * e ) {
std : : vector < const ExtrusionEntity * > entities ;
std : : vector < const ExtrusionEntity * > queue { e } ;
while ( ! queue . empty ( ) ) {
const ExtrusionEntity * next = queue . back ( ) ;
queue . pop_back ( ) ;
if ( next - > is_collection ( ) ) {
for ( const ExtrusionEntity * e : static_cast < const ExtrusionEntityCollection * > ( next ) - > entities ) {
queue . push_back ( e ) ;
}
} else {
entities . push_back ( next ) ;
}
}
return entities ;
} ;
std : : vector < EnitityToCheck > entities_to_check ;
for ( size_t slice_idx = 0 ; slice_idx < layer - > lslices_ex . size ( ) ; + + slice_idx ) {
const LayerSlice & slice = layer - > lslices_ex . at ( slice_idx ) ;
for ( const auto & island : slice . islands ) {
for ( const LayerExtrusionRange & fill_range : island . fills ) {
const LayerRegion * fill_region = layer - > get_region ( fill_range . region ( ) ) ;
2023-06-27 11:07:34 +08:00
for ( size_t fill_idx : fill_range ) {
2023-06-10 10:14:12 +08:00
for ( const ExtrusionEntity * e : get_flat_entities ( fill_region - > fills ( ) . entities [ fill_idx ] ) ) {
if ( e - > role ( ) = = ExtrusionRole : : BridgeInfill ) {
entities_to_check . push_back ( { e , fill_region , slice_idx } ) ;
}
}
}
}
const LayerRegion * perimeter_region = layer - > get_region ( island . perimeters . region ( ) ) ;
2023-06-27 11:07:34 +08:00
for ( size_t perimeter_idx : island . perimeters ) {
2023-06-10 10:14:12 +08:00
for ( const ExtrusionEntity * e : get_flat_entities ( perimeter_region - > perimeters ( ) . entities [ perimeter_idx ] ) ) {
entities_to_check . push_back ( { e , perimeter_region , slice_idx } ) ;
}
}
}
}
2023-12-27 18:02:35 +08:00
return entities_to_check ;
}
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
LocalSupports compute_local_supports (
const std : : vector < EnitityToCheck > & entities_to_check ,
const std : : optional < Linesf > & previous_layer_boundary ,
const LD & prev_layer_ext_perim_lines ,
size_t slices_count ,
const Params & params
) {
std : : vector < tbb : : concurrent_vector < ExtrusionLine > > unstable_lines_per_slice ( slices_count ) ;
std : : vector < tbb : : concurrent_vector < ExtrusionLine > > ext_perim_lines_per_slice ( slices_count ) ;
AABBTreeLines : : LinesDistancer < Linef > prev_layer_boundary_distancer =
( previous_layer_boundary ? AABBTreeLines : : LinesDistancer < Linef > { * previous_layer_boundary } : AABBTreeLines : : LinesDistancer < Linef > { } ) ;
if constexpr ( debug_files ) {
for ( const auto & e_to_check : entities_to_check ) {
for ( const auto & line : check_extrusion_entity_stability ( e_to_check . e , e_to_check . region , prev_layer_ext_perim_lines ,
prev_layer_boundary_distancer , params ) ) {
if ( line . support_point_generated . has_value ( ) ) {
unstable_lines_per_slice [ e_to_check . slice_idx ] . push_back ( line ) ;
}
if ( line . is_external_perimeter ( ) ) {
ext_perim_lines_per_slice [ e_to_check . slice_idx ] . push_back ( line ) ;
}
}
}
} else {
2023-06-10 10:14:12 +08:00
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , entities_to_check . size ( ) ) ,
2023-12-27 18:02:35 +08:00
[ & entities_to_check , & prev_layer_ext_perim_lines , & prev_layer_boundary_distancer , & unstable_lines_per_slice ,
2023-06-10 10:14:12 +08:00
& ext_perim_lines_per_slice , & params ] ( tbb : : blocked_range < size_t > r ) {
for ( size_t entity_idx = r . begin ( ) ; entity_idx < r . end ( ) ; + + entity_idx ) {
const auto & e_to_check = entities_to_check [ entity_idx ] ;
for ( const auto & line :
check_extrusion_entity_stability ( e_to_check . e , e_to_check . region , prev_layer_ext_perim_lines ,
2023-12-27 18:02:35 +08:00
prev_layer_boundary_distancer , params ) ) {
2023-06-10 10:14:12 +08:00
if ( line . support_point_generated . has_value ( ) ) {
unstable_lines_per_slice [ e_to_check . slice_idx ] . push_back ( line ) ;
}
if ( line . is_external_perimeter ( ) ) {
ext_perim_lines_per_slice [ e_to_check . slice_idx ] . push_back ( line ) ;
}
}
}
} ) ;
2023-12-27 18:02:35 +08:00
}
return { unstable_lines_per_slice , ext_perim_lines_per_slice } ;
}
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
struct SliceMappings
{
std : : unordered_map < size_t , size_t > index_to_object_part_mapping ;
std : : unordered_map < size_t , SliceConnection > index_to_weakest_connection ;
} ;
std : : optional < PartialObject > to_partial_object ( const ObjectPart & part ) {
if ( part . volume > EPSILON ) {
return PartialObject { part . volume_centroid_accumulator / part . volume , part . volume ,
part . connected_to_bed } ;
}
return { } ;
}
SliceMappings update_active_object_parts ( const Layer * layer ,
const Params & params ,
const std : : vector < SliceConnection > & precomputed_slice_connections ,
const SliceMappings & previous_slice_mappings ,
ActiveObjectParts & active_object_parts ,
PartialObjects & partial_objects )
{
SliceMappings new_slice_mappings ;
2023-06-10 10:14:12 +08:00
for ( size_t slice_idx = 0 ; slice_idx < layer - > lslices_ex . size ( ) ; + + slice_idx ) {
2023-12-27 18:02:35 +08:00
const LayerSlice & slice = layer - > lslices_ex . at ( slice_idx ) ;
const std : : vector < const ExtrusionEntityCollection * > extrusion_collections { gather_extrusions ( slice , layer ) } ;
const bool connected_to_bed = int ( layer - > id ( ) ) = = params . raft_layers_count ;
const std : : optional < Polygons > brim {
has_brim ( layer , params ) ?
std : : optional { get_brim ( layer - > lslices [ slice_idx ] , params . brim_type , params . brim_width ) } :
std : : nullopt
} ;
ObjectPart new_part {
extrusion_collections ,
connected_to_bed ,
layer - > print_z ,
layer - > height ,
brim
} ;
const SliceConnection & connection_to_below = precomputed_slice_connections [ slice_idx ] ;
2023-06-10 10:14:12 +08:00
# ifdef DETAILED_DEBUG_LOGS
2023-12-27 18:02:35 +08:00
std : : cout < < " SLICE IDX: " < < slice_idx < < std : : endl ;
for ( const auto & link : slice . overlaps_below ) {
std : : cout < < " connected to slice below: " < < link . slice_idx < < " by area : " < < link . area < < std : : endl ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
connection_to_below . print_info ( " CONNECTION TO BELOW " ) ;
# endif
2023-06-10 10:14:12 +08:00
2023-12-27 18:02:35 +08:00
if ( connection_to_below . area < EPSILON ) { // new object part emerging
size_t part_id = active_object_parts . insert ( new_part ) ;
new_slice_mappings . index_to_object_part_mapping . emplace ( slice_idx , part_id ) ;
new_slice_mappings . index_to_weakest_connection . emplace ( slice_idx , connection_to_below ) ;
} else {
size_t final_part_id { } ;
SliceConnection transfered_weakest_connection { } ;
// MERGE parts
{
std : : unordered_set < size_t > parts_ids ;
for ( const auto & link : slice . overlaps_below ) {
size_t part_id = active_object_parts . get_flat_id ( previous_slice_mappings . index_to_object_part_mapping . at ( link . slice_idx ) ) ;
parts_ids . insert ( part_id ) ;
transfered_weakest_connection . add ( previous_slice_mappings . index_to_weakest_connection . at ( link . slice_idx ) ) ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
final_part_id = * parts_ids . begin ( ) ;
for ( size_t part_id : parts_ids ) {
if ( final_part_id ! = part_id ) {
auto object_part = active_object_parts . access ( part_id ) ;
if ( auto object = to_partial_object ( object_part ) ) {
partial_objects . push_back ( std : : move ( * object ) ) ;
}
active_object_parts . merge ( part_id , final_part_id ) ;
}
}
}
const float bottom_z = layer - > bottom_z ( ) ;
auto estimate_conn_strength = [ bottom_z ] ( const SliceConnection & conn ) {
if ( conn . area < EPSILON ) { // connection is empty, does not exists. Return max strength so that it is not picked as the
// weakest connection.
return INFINITY ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
Vec3f centroid = conn . centroid_accumulator / conn . area ;
Vec2f variance = ( conn . second_moment_of_area_accumulator / conn . area -
centroid . head < 2 > ( ) . cwiseProduct ( centroid . head < 2 > ( ) ) ) ;
float xy_variance = variance . x ( ) + variance . y ( ) ;
float arm_len_estimate = std : : max ( 1.0f , bottom_z - ( conn . centroid_accumulator . z ( ) / conn . area ) ) ;
return conn . area * sqrt ( xy_variance ) / arm_len_estimate ;
2023-06-10 10:14:12 +08:00
} ;
2023-12-27 18:02:35 +08:00
# ifdef DETAILED_DEBUG_LOGS
connection_to_below . print_info ( " new_weakest_connection " ) ;
transfered_weakest_connection . print_info ( " transfered_weakest_connection " ) ;
# endif
if ( estimate_conn_strength ( transfered_weakest_connection ) > estimate_conn_strength ( connection_to_below ) ) {
transfered_weakest_connection = connection_to_below ;
}
new_slice_mappings . index_to_weakest_connection . emplace ( slice_idx , transfered_weakest_connection ) ;
new_slice_mappings . index_to_object_part_mapping . emplace ( slice_idx , final_part_id ) ;
ObjectPart & part = active_object_parts . access ( final_part_id ) ;
part . add ( new_part ) ;
}
}
return new_slice_mappings ;
2023-06-10 10:14:12 +08:00
}
2023-12-27 18:02:35 +08:00
void reckon_global_supports ( const tbb : : concurrent_vector < ExtrusionLine > & external_perimeter_lines ,
const coordf_t layer_bottom_z ,
const Params & params ,
ObjectPart & part ,
SliceConnection & weakest_connection ,
SupportPoints & supp_points ,
SupportGridFilter & supports_presence_grid )
{
LD current_slice_lines_distancer ( { external_perimeter_lines . begin ( ) , external_perimeter_lines . end ( ) } ) ;
2023-06-10 10:14:12 +08:00
float unchecked_dist = params . min_distance_between_support_points + 1.0f ;
2023-12-27 18:02:35 +08:00
for ( const ExtrusionLine & line : external_perimeter_lines ) {
if ( ( unchecked_dist + line . len < params . min_distance_between_support_points & &
line . curled_up_height < params . curling_tolerance_limit ) | |
2023-06-10 10:14:12 +08:00
line . len < EPSILON ) {
unchecked_dist + = line . len ;
} else {
unchecked_dist = line . len ;
Vec2f pivot_site_search_point = Vec2f ( line . b + ( line . b - line . a ) . normalized ( ) * 300.0f ) ;
2023-12-27 18:02:35 +08:00
auto [ dist , nidx , nearest_point ] = current_slice_lines_distancer . distance_from_lines_extra < false > ( pivot_site_search_point ) ;
Vec3f position = to_3d ( nearest_point , layer_bottom_z ) ;
auto [ force , cause ] = part . is_stable_while_extruding ( weakest_connection , line , position , layer_bottom_z , params ) ;
2023-06-10 10:14:12 +08:00
if ( force > 0 ) {
2023-12-27 18:02:35 +08:00
SupportPoint support_point { cause , position , params . support_points_interface_radius } ;
reckon_new_support_point ( part , weakest_connection , supp_points , supports_presence_grid , support_point , true ) ;
}
2023-06-10 10:14:12 +08:00
}
}
}
2023-12-27 18:02:35 +08:00
std : : tuple < SupportPoints , PartialObjects > check_stability ( const PrintObject * po ,
const PrecomputedSliceConnections & precomputed_slices_connections ,
const PrintTryCancel & cancel_func ,
const Params & params )
{
SupportPoints supp_points { } ;
SupportGridFilter supports_presence_grid ( po , params . min_distance_between_support_points ) ;
ActiveObjectParts active_object_parts { } ;
PartialObjects partial_objects { } ;
LD prev_layer_ext_perim_lines ;
SliceMappings slice_mappings ;
for ( size_t layer_idx = 0 ; layer_idx < po - > layer_count ( ) ; + + layer_idx ) {
cancel_func ( ) ;
const Layer * layer = po - > get_layer ( layer_idx ) ;
float bottom_z = layer - > bottom_z ( ) ;
slice_mappings = update_active_object_parts ( layer , params , precomputed_slices_connections [ layer_idx ] , slice_mappings , active_object_parts , partial_objects ) ;
std : : optional < Linesf > prev_layer_boundary = layer - > lower_layer ! = nullptr ?
std : : optional { to_unscaled_linesf ( layer - > lower_layer - > lslices ) } :
std : : nullopt ;
LocalSupports local_supports {
compute_local_supports ( gather_entities_to_check ( layer ) , prev_layer_boundary , prev_layer_ext_perim_lines , layer - > lslices_ex . size ( ) , params ) } ;
std : : vector < ExtrusionLine > current_layer_ext_perims_lines { } ;
current_layer_ext_perims_lines . reserve ( prev_layer_ext_perim_lines . get_lines ( ) . size ( ) ) ;
// All object parts updated, and for each slice we have coresponding weakest connection.
// We can now check each slice and its corresponding weakest connection and object part for stability.
for ( size_t slice_idx = 0 ; slice_idx < layer - > lslices_ex . size ( ) ; + + slice_idx ) {
ObjectPart & part = active_object_parts . access ( slice_mappings . index_to_object_part_mapping [ slice_idx ] ) ;
SliceConnection & weakest_conn = slice_mappings . index_to_weakest_connection [ slice_idx ] ;
if ( layer_idx > 1 ) {
for ( const auto & l : local_supports . unstable_lines_per_slice [ slice_idx ] ) {
assert ( l . support_point_generated . has_value ( ) ) ;
SupportPoint support_point { * l . support_point_generated , to_3d ( l . b , bottom_z ) ,
params . support_points_interface_radius } ;
reckon_new_support_point ( part , weakest_conn , supp_points , supports_presence_grid , support_point ) ;
}
}
const tbb : : concurrent_vector < ExtrusionLine > & external_perimeter_lines = local_supports . ext_perim_lines_per_slice [ slice_idx ] ;
if ( layer_idx > 1 ) {
reckon_global_supports ( external_perimeter_lines , bottom_z , params , part , weakest_conn , supp_points , supports_presence_grid ) ;
}
current_layer_ext_perims_lines . insert ( current_layer_ext_perims_lines . end ( ) , external_perimeter_lines . begin ( ) , external_perimeter_lines . end ( ) ) ;
2023-06-10 10:14:12 +08:00
} // slice iterations
prev_layer_ext_perim_lines = LD ( current_layer_ext_perims_lines ) ;
} // layer iterations
2023-12-27 18:02:35 +08:00
for ( const auto & active_obj_pair : slice_mappings . index_to_object_part_mapping ) {
auto object_part = active_object_parts . access ( active_obj_pair . second ) ;
if ( auto object = to_partial_object ( object_part ) ) {
partial_objects . push_back ( std : : move ( * object ) ) ;
}
2023-06-10 10:14:12 +08:00
}
return { supp_points , partial_objects } ;
}
# ifdef DEBUG_FILES
void debug_export ( const SupportPoints & support_points , const PartialObjects & objects , std : : string file_name )
{
Slic3r : : CNumericLocalesSetter locales_setter ;
{
FILE * fp = boost : : nowide : : fopen ( debug_out_path ( ( file_name + " _supports.obj " ) . c_str ( ) ) . c_str ( ) , " w " ) ;
if ( fp = = nullptr ) {
BOOST_LOG_TRIVIAL ( error ) < < " Debug files: Couldn't open " < < file_name < < " for writing " ;
return ;
}
for ( size_t i = 0 ; i < support_points . size ( ) ; + + i ) {
Vec3f color { 1.0f , 1.0f , 1.0f } ;
switch ( support_points [ i ] . cause ) {
case SupportPointCause : : FloatingBridgeAnchor : color = { 0.863281f , 0.109375f , 0.113281f } ; break ; //RED
case SupportPointCause : : LongBridge : color = { 0.960938f , 0.90625f , 0.0625f } ; break ; // YELLOW
case SupportPointCause : : FloatingExtrusion : color = { 0.921875f , 0.515625f , 0.101563f } ; break ; // ORANGE
case SupportPointCause : : SeparationFromBed : color = { 0.0f , 1.0f , 0.0 } ; break ; // GREEN
case SupportPointCause : : UnstableFloatingPart : color = { 0.105469f , 0.699219f , 0.84375f } ; break ; // BLUE
case SupportPointCause : : WeakObjectPart : color = { 0.609375f , 0.210938f , 0.621094f } ; break ; // PURPLE
}
fprintf ( fp , " v %f %f %f %f %f %f \n " , support_points [ i ] . position ( 0 ) , support_points [ i ] . position ( 1 ) ,
support_points [ i ] . position ( 2 ) , color [ 0 ] , color [ 1 ] , color [ 2 ] ) ;
}
for ( size_t i = 0 ; i < objects . size ( ) ; + + i ) {
Vec3f color { 1.0f , 0.0f , 1.0f } ;
if ( objects [ i ] . connected_to_bed ) {
color = { 1.0f , 0.0f , 0.0f } ;
}
fprintf ( fp , " v %f %f %f %f %f %f \n " , objects [ i ] . centroid ( 0 ) , objects [ i ] . centroid ( 1 ) , objects [ i ] . centroid ( 2 ) , color [ 0 ] ,
color [ 1 ] , color [ 2 ] ) ;
}
fclose ( fp ) ;
}
}
# endif
std : : tuple < SupportPoints , PartialObjects > full_search ( const PrintObject * po , const PrintTryCancel & cancel_func , const Params & params )
{
auto precomputed_slices_connections = precompute_slices_connections ( po ) ;
auto results = check_stability ( po , precomputed_slices_connections , cancel_func , params ) ;
# ifdef DEBUG_FILES
auto [ supp_points , objects ] = results ;
debug_export ( supp_points , objects , " issues " ) ;
# endif
return results ;
}
void estimate_supports_malformations ( SupportLayerPtrs & layers , float flow_width , const Params & params )
{
# ifdef DEBUG_FILES
FILE * debug_file = boost : : nowide : : fopen ( debug_out_path ( " supports_malformations.obj " ) . c_str ( ) , " w " ) ;
FILE * full_file = boost : : nowide : : fopen ( debug_out_path ( " supports_full.obj " ) . c_str ( ) , " w " ) ;
# endif
AABBTreeLines : : LinesDistancer < ExtrusionLine > prev_layer_lines { } ;
for ( SupportLayer * l : layers ) {
l - > curled_lines . clear ( ) ;
std : : vector < ExtrusionLine > current_layer_lines ;
for ( const ExtrusionEntity * extrusion : l - > support_fills . flatten ( ) . entities ) {
Polyline pl = extrusion - > as_polyline ( ) ;
Polygon pol ( pl . points ) ;
pol . make_counter_clockwise ( ) ;
2023-12-27 18:02:35 +08:00
auto annotated_points = ExtrusionProcessor : : estimate_points_properties < true , true , false , false > ( pol . points , prev_layer_lines ,
flow_width ) ;
2023-06-10 10:14:12 +08:00
for ( size_t i = 0 ; i < annotated_points . size ( ) ; + + i ) {
2023-12-27 18:02:35 +08:00
const ExtrusionProcessor : : ExtendedPoint & a = i > 0 ? annotated_points [ i - 1 ] : annotated_points [ i ] ;
const ExtrusionProcessor : : ExtendedPoint & b = annotated_points [ i ] ;
2023-06-10 10:14:12 +08:00
ExtrusionLine line_out { a . position . cast < float > ( ) , b . position . cast < float > ( ) , float ( ( a . position - b . position ) . norm ( ) ) ,
extrusion } ;
Vec2f middle = 0.5 * ( line_out . a + line_out . b ) ;
auto [ middle_distance , bottom_line_idx , x ] = prev_layer_lines . distance_from_lines_extra < false > ( middle ) ;
ExtrusionLine bottom_line = prev_layer_lines . get_lines ( ) . empty ( ) ? ExtrusionLine { } :
prev_layer_lines . get_line ( bottom_line_idx ) ;
Vec2f v1 = ( bottom_line . b - bottom_line . a ) ;
Vec2f v2 = ( a . position . cast < float > ( ) - bottom_line . a ) ;
auto d = ( v1 . x ( ) * v2 . y ( ) ) - ( v1 . y ( ) * v2 . x ( ) ) ;
float sign = ( d > 0 ) ? - 1.0f : 1.0f ;
line_out . curled_up_height = estimate_curled_up_height ( middle_distance * sign , 0.5 * ( a . curvature + b . curvature ) , l - > height ,
flow_width , bottom_line . curled_up_height , params ) ;
current_layer_lines . push_back ( line_out ) ;
}
}
for ( const ExtrusionLine & line : current_layer_lines ) {
if ( line . curled_up_height > params . curling_tolerance_limit ) {
l - > curled_lines . push_back ( CurledLine { Point : : new_scale ( line . a ) , Point : : new_scale ( line . b ) , line . curled_up_height } ) ;
}
}
# ifdef DEBUG_FILES
for ( const ExtrusionLine & line : current_layer_lines ) {
if ( line . curled_up_height > params . curling_tolerance_limit ) {
Vec3f color = value_to_rgbf ( - EPSILON , l - > height * params . max_curled_height_factor , line . curled_up_height ) ;
fprintf ( debug_file , " v %f %f %f %f %f %f \n " , line . b [ 0 ] , line . b [ 1 ] , l - > print_z , color [ 0 ] , color [ 1 ] , color [ 2 ] ) ;
}
}
for ( const ExtrusionLine & line : current_layer_lines ) {
Vec3f color = value_to_rgbf ( - EPSILON , l - > height * params . max_curled_height_factor , line . curled_up_height ) ;
fprintf ( full_file , " v %f %f %f %f %f %f \n " , line . b [ 0 ] , line . b [ 1 ] , l - > print_z , color [ 0 ] , color [ 1 ] , color [ 2 ] ) ;
}
# endif
prev_layer_lines = LD { current_layer_lines } ;
}
# ifdef DEBUG_FILES
fclose ( debug_file ) ;
fclose ( full_file ) ;
# endif
}
void estimate_malformations ( LayerPtrs & layers , const Params & params )
{
# ifdef DEBUG_FILES
FILE * debug_file = boost : : nowide : : fopen ( debug_out_path ( " object_malformations.obj " ) . c_str ( ) , " w " ) ;
FILE * full_file = boost : : nowide : : fopen ( debug_out_path ( " object_full.obj " ) . c_str ( ) , " w " ) ;
# endif
LD prev_layer_lines { } ;
for ( Layer * l : layers ) {
l - > curled_lines . clear ( ) ;
std : : vector < Linef > boundary_lines = l - > lower_layer ! = nullptr ? to_unscaled_linesf ( l - > lower_layer - > lslices ) : std : : vector < Linef > ( ) ;
AABBTreeLines : : LinesDistancer < Linef > prev_layer_boundary { std : : move ( boundary_lines ) } ;
std : : vector < ExtrusionLine > current_layer_lines ;
for ( const LayerRegion * layer_region : l - > regions ( ) ) {
for ( const ExtrusionEntity * extrusion : layer_region - > perimeters ( ) . flatten ( ) . entities ) {
if ( ! extrusion - > role ( ) . is_external_perimeter ( ) )
continue ;
Points extrusion_pts ;
extrusion - > collect_points ( extrusion_pts ) ;
float flow_width = get_flow_width ( layer_region , extrusion - > role ( ) ) ;
2023-12-27 18:02:35 +08:00
auto annotated_points = ExtrusionProcessor : : estimate_points_properties < true , true , false , false > ( extrusion_pts ,
prev_layer_lines ,
flow_width ,
2023-06-10 10:14:12 +08:00
params . bridge_distance ) ;
for ( size_t i = 0 ; i < annotated_points . size ( ) ; + + i ) {
2023-12-27 18:02:35 +08:00
const ExtrusionProcessor : : ExtendedPoint & a = i > 0 ? annotated_points [ i - 1 ] : annotated_points [ i ] ;
const ExtrusionProcessor : : ExtendedPoint & b = annotated_points [ i ] ;
2023-06-10 10:14:12 +08:00
ExtrusionLine line_out { a . position . cast < float > ( ) , b . position . cast < float > ( ) , float ( ( a . position - b . position ) . norm ( ) ) ,
extrusion } ;
Vec2f middle = 0.5 * ( line_out . a + line_out . b ) ;
auto [ middle_distance , bottom_line_idx , x ] = prev_layer_lines . distance_from_lines_extra < false > ( middle ) ;
ExtrusionLine bottom_line = prev_layer_lines . get_lines ( ) . empty ( ) ? ExtrusionLine { } :
prev_layer_lines . get_line ( bottom_line_idx ) ;
// correctify the distance sign using slice polygons
2023-12-27 18:02:35 +08:00
float sign = ( prev_layer_boundary . distance_from_lines < true > ( middle . cast < double > ( ) ) + 0.5f * flow_width ) < 0.0f ? - 1.0f :
1.0f ;
2023-06-10 10:14:12 +08:00
line_out . curled_up_height = estimate_curled_up_height ( middle_distance * sign , 0.5 * ( a . curvature + b . curvature ) ,
l - > height , flow_width , bottom_line . curled_up_height , params ) ;
current_layer_lines . push_back ( line_out ) ;
}
}
}
for ( const ExtrusionLine & line : current_layer_lines ) {
if ( line . curled_up_height > params . curling_tolerance_limit ) {
l - > curled_lines . push_back ( CurledLine { Point : : new_scale ( line . a ) , Point : : new_scale ( line . b ) , line . curled_up_height } ) ;
}
}
# ifdef DEBUG_FILES
for ( const ExtrusionLine & line : current_layer_lines ) {
if ( line . curled_up_height > params . curling_tolerance_limit ) {
Vec3f color = value_to_rgbf ( - EPSILON , l - > height * params . max_curled_height_factor , line . curled_up_height ) ;
fprintf ( debug_file , " v %f %f %f %f %f %f \n " , line . b [ 0 ] , line . b [ 1 ] , l - > print_z , color [ 0 ] , color [ 1 ] , color [ 2 ] ) ;
}
}
for ( const ExtrusionLine & line : current_layer_lines ) {
Vec3f color = value_to_rgbf ( - EPSILON , l - > height * params . max_curled_height_factor , line . curled_up_height ) ;
fprintf ( full_file , " v %f %f %f %f %f %f \n " , line . b [ 0 ] , line . b [ 1 ] , l - > print_z , color [ 0 ] , color [ 1 ] , color [ 2 ] ) ;
}
# endif
prev_layer_lines = LD { current_layer_lines } ;
}
# ifdef DEBUG_FILES
fclose ( debug_file ) ;
fclose ( full_file ) ;
# endif
}
std : : vector < std : : pair < SupportPointCause , bool > > gather_issues ( const SupportPoints & support_points , PartialObjects & partial_objects )
{
std : : vector < std : : pair < SupportPointCause , bool > > result ;
// The partial object are most likely sorted from smaller to larger as the print continues, so this should save some sorting time
std : : reverse ( partial_objects . begin ( ) , partial_objects . end ( ) ) ;
std : : sort ( partial_objects . begin ( ) , partial_objects . end ( ) ,
[ ] ( const PartialObject & left , const PartialObject & right ) { return left . volume > right . volume ; } ) ;
// Object may have zero extrusions and thus no partial objects. (e.g. very tiny object)
float max_volume_part = partial_objects . empty ( ) ? 0.0f : partial_objects . front ( ) . volume ;
for ( const PartialObject & p : partial_objects ) {
if ( p . volume > max_volume_part / 200.0f & & ! p . connected_to_bed ) {
result . emplace_back ( SupportPointCause : : UnstableFloatingPart , true ) ;
break ;
}
}
// should be detected in previous step
// if (!unstable_floating_part_added) {
// for (const SupportPoint &sp : support_points) {
// if (sp.cause == SupportPointCause::UnstableFloatingPart) {
// result.emplace_back(SupportPointCause::UnstableFloatingPart, true);
// break;
// }
// }
// }
std : : vector < SupportPoint > ext_supp_points { } ;
ext_supp_points . reserve ( support_points . size ( ) ) ;
for ( const SupportPoint & sp : support_points ) {
switch ( sp . cause ) {
case SupportPointCause : : FloatingBridgeAnchor :
case SupportPointCause : : FloatingExtrusion : ext_supp_points . push_back ( sp ) ; break ;
default : break ;
}
}
auto coord_fn = [ & ext_supp_points ] ( size_t idx , size_t dim ) { return ext_supp_points [ idx ] . position [ dim ] ; } ;
KDTreeIndirect < 3 , float , decltype ( coord_fn ) > ext_points_tree { coord_fn , ext_supp_points . size ( ) } ;
for ( const SupportPoint & sp : ext_supp_points ) {
auto cluster = find_nearby_points ( ext_points_tree , sp . position , 3.0 ) ;
int score = 0 ;
bool floating_bridge = false ;
for ( size_t idx : cluster ) {
score + = ext_supp_points [ idx ] . cause = = SupportPointCause : : FloatingBridgeAnchor ? 3 : 1 ;
floating_bridge = floating_bridge | | ext_supp_points [ idx ] . cause = = SupportPointCause : : FloatingBridgeAnchor ;
}
if ( score > 5 ) {
if ( floating_bridge ) {
result . emplace_back ( SupportPointCause : : FloatingBridgeAnchor , true ) ;
} else {
result . emplace_back ( SupportPointCause : : FloatingExtrusion , true ) ;
}
break ;
}
}
for ( const SupportPoint & sp : support_points ) {
if ( sp . cause = = SupportPointCause : : SeparationFromBed ) {
result . emplace_back ( SupportPointCause : : SeparationFromBed , true ) ;
break ;
}
}
for ( const SupportPoint & sp : support_points ) {
if ( sp . cause = = SupportPointCause : : WeakObjectPart ) {
result . emplace_back ( SupportPointCause : : WeakObjectPart , true ) ;
break ;
}
}
if ( ext_supp_points . size ( ) > max_volume_part / 200.0f ) {
result . emplace_back ( SupportPointCause : : FloatingExtrusion , false ) ;
}
for ( const SupportPoint & sp : support_points ) {
if ( sp . cause = = SupportPointCause : : LongBridge ) {
result . emplace_back ( SupportPointCause : : LongBridge , false ) ;
break ;
}
}
return result ;
}
} // namespace SupportSpotsGenerator