2022-07-15 15:37:19 +00:00
# include "FillBedJob.hpp"
# include "libslic3r/Model.hpp"
# include "libslic3r/ClipperUtils.hpp"
# include "libslic3r/ModelArrange.hpp"
# include "slic3r/GUI/Plater.hpp"
# include "slic3r/GUI/GLCanvas3D.hpp"
# include "slic3r/GUI/GUI_ObjectList.hpp"
2023-04-23 08:05:31 +00:00
# include "libnest2d/common.hpp"
2022-07-15 15:37:19 +00:00
# include <numeric>
namespace Slic3r {
namespace GUI {
//BBS: add partplate related logic
void FillBedJob : : prepare ( )
{
PartPlateList & plate_list = m_plater - > get_partplate_list ( ) ;
m_locked . clear ( ) ;
m_selected . clear ( ) ;
m_unselected . clear ( ) ;
m_bedpts . clear ( ) ;
2023-06-14 02:11:45 +00:00
params = init_arrange_params ( m_plater ) ;
2023-05-23 09:57:42 +00:00
2022-07-15 15:37:19 +00:00
m_object_idx = m_plater - > get_selected_object_idx ( ) ;
if ( m_object_idx = = - 1 )
return ;
//select current plate at first
int sel_id = m_plater - > get_selection ( ) . get_instance_idx ( ) ;
sel_id = std : : max ( sel_id , 0 ) ;
int sel_ret = plate_list . select_plate_by_obj ( m_object_idx , sel_id ) ;
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < boost : : format ( " :select plate obj_id %1%, ins_id %2%, ret %3%} " ) % m_object_idx % sel_id % sel_ret ;
PartPlate * plate = plate_list . get_curr_plate ( ) ;
Model & model = m_plater - > model ( ) ;
BoundingBox plate_bb = plate - > get_bounding_box_crd ( ) ;
int plate_cols = plate_list . get_plate_cols ( ) ;
int cur_plate_index = plate - > get_index ( ) ;
ModelObject * model_object = m_plater - > model ( ) . objects [ m_object_idx ] ;
if ( model_object - > instances . empty ( ) ) return ;
2023-06-19 10:25:24 +00:00
const Slic3r : : DynamicPrintConfig & global_config = wxGetApp ( ) . preset_bundle - > full_config ( ) ;
2022-07-15 15:37:19 +00:00
m_selected . reserve ( model_object - > instances . size ( ) ) ;
for ( size_t oidx = 0 ; oidx < model . objects . size ( ) ; + + oidx )
{
ModelObject * mo = model . objects [ oidx ] ;
for ( size_t inst_idx = 0 ; inst_idx < mo - > instances . size ( ) ; + + inst_idx )
{
bool selected = ( oidx = = m_object_idx ) ;
2023-06-19 10:25:24 +00:00
ArrangePolygon ap = get_instance_arrange_poly ( mo - > instances [ inst_idx ] , global_config ) ;
2022-07-15 15:37:19 +00:00
BoundingBox ap_bb = ap . transformed_poly ( ) . contour . bounding_box ( ) ;
ap . height = 1 ;
ap . name = mo - > name ;
if ( selected )
{
if ( mo - > instances [ inst_idx ] - > printable )
{
+ + ap . priority ;
ap . itemid = m_selected . size ( ) ;
m_selected . emplace_back ( ap ) ;
}
else
{
if ( plate_bb . contains ( ap_bb ) )
{
ap . bed_idx = 0 ;
ap . itemid = m_unselected . size ( ) ;
ap . row = cur_plate_index / plate_cols ;
ap . col = cur_plate_index % plate_cols ;
ap . translation ( X ) - = bed_stride_x ( m_plater ) * ap . col ;
ap . translation ( Y ) + = bed_stride_y ( m_plater ) * ap . row ;
m_unselected . emplace_back ( ap ) ;
}
else
{
ap . bed_idx = PartPlateList : : MAX_PLATES_COUNT ;
ap . itemid = m_locked . size ( ) ;
m_locked . emplace_back ( ap ) ;
}
}
}
else
{
if ( plate_bb . contains ( ap_bb ) )
{
ap . bed_idx = 0 ;
ap . itemid = m_unselected . size ( ) ;
ap . row = cur_plate_index / plate_cols ;
ap . col = cur_plate_index % plate_cols ;
ap . translation ( X ) - = bed_stride_x ( m_plater ) * ap . col ;
ap . translation ( Y ) + = bed_stride_y ( m_plater ) * ap . row ;
m_unselected . emplace_back ( ap ) ;
}
else
{
ap . bed_idx = PartPlateList : : MAX_PLATES_COUNT ;
ap . itemid = m_locked . size ( ) ;
m_locked . emplace_back ( ap ) ;
}
}
}
}
/*
for ( ModelInstance * inst : model_object - > instances )
if ( inst - > printable ) {
ArrangePolygon ap = get_arrange_poly ( inst ) ;
// Existing objects need to be included in the result. Only
// the needed amount of object will be added, no more.
+ + ap . priority ;
m_selected . emplace_back ( ap ) ;
} */
if ( m_selected . empty ( ) ) return ;
//add the virtual object into unselect list if has
2023-05-23 09:57:42 +00:00
double scaled_exclusion_gap = scale_ ( 1 ) ;
plate_list . preprocess_exclude_areas ( params . excluded_regions , 1 , scaled_exclusion_gap ) ;
2022-07-15 15:37:19 +00:00
plate_list . preprocess_exclude_areas ( m_unselected ) ;
2024-07-22 06:19:54 +00:00
m_bedpts = get_bed_shape ( global_config ) ;
2022-07-15 15:37:19 +00:00
auto & objects = m_plater - > model ( ) . objects ;
/*BoundingBox bedbb = get_extents(m_bedpts);
for ( size_t idx = 0 ; idx < objects . size ( ) ; + + idx )
if ( int ( idx ) ! = m_object_idx )
for ( ModelInstance * mi : objects [ idx ] - > instances ) {
ArrangePolygon ap = get_arrange_poly ( mi ) ;
auto ap_bb = ap . transformed_poly ( ) . contour . bounding_box ( ) ;
if ( ap . bed_idx = = 0 & & ! bedbb . contains ( ap_bb ) )
ap . bed_idx = arrangement : : UNARRANGED ;
m_unselected . emplace_back ( ap ) ;
2023-04-23 08:05:31 +00:00
} */
2022-07-15 15:37:19 +00:00
if ( auto wt = get_wipe_tower_arrangepoly ( * m_plater ) )
2023-04-23 08:05:31 +00:00
m_unselected . emplace_back ( std : : move ( * wt ) ) ;
2022-07-15 15:37:19 +00:00
double sc = scaled < double > ( 1. ) * scaled ( 1. ) ;
2023-12-26 09:52:51 +00:00
auto polys = offset_ex ( m_selected . front ( ) . poly , params . min_obj_distance / 2 ) ;
2023-04-23 08:05:31 +00:00
ExPolygon poly = polys . empty ( ) ? m_selected . front ( ) . poly : polys . front ( ) ;
2022-07-15 15:37:19 +00:00
double poly_area = poly . area ( ) / sc ;
double unsel_area = std : : accumulate ( m_unselected . begin ( ) ,
m_unselected . end ( ) , 0. ,
[ cur_plate_index ] ( double s , const auto & ap ) {
//BBS: m_unselected instance is in the same partplate
return s + ( ap . bed_idx = = cur_plate_index ) * ap . poly . area ( ) ;
//return s + (ap.bed_idx == 0) * ap.poly.area();
} ) / sc ;
double fixed_area = unsel_area + m_selected . size ( ) * poly_area ;
double bed_area = Polygon { m_bedpts } . area ( ) / sc ;
// This is the maximum number of items, the real number will always be close but less.
int needed_items = ( bed_area - fixed_area ) / poly_area ;
//int sel_id = m_plater->get_selection().get_instance_idx();
// if the selection is not a single instance, choose the first as template
//sel_id = std::max(sel_id, 0);
ModelInstance * mi = model_object - > instances [ sel_id ] ;
2023-06-19 10:25:24 +00:00
ArrangePolygon template_ap = get_instance_arrange_poly ( mi , global_config ) ;
2022-07-15 15:37:19 +00:00
for ( int i = 0 ; i < needed_items ; + + i ) {
ArrangePolygon ap = template_ap ;
ap . poly = m_selected . front ( ) . poly ;
ap . bed_idx = PartPlateList : : MAX_PLATES_COUNT ;
ap . height = 1 ;
ap . itemid = - 1 ;
ap . setter = [ this , mi ] ( const ArrangePolygon & p ) {
ModelObject * mo = m_plater - > model ( ) . objects [ m_object_idx ] ;
2023-04-23 08:05:31 +00:00
ModelObject * newObj = m_plater - > model ( ) . add_object ( * mo ) ;
newObj - > name = mo - > name + " " + std : : to_string ( p . itemid ) ;
for ( ModelInstance * newInst : newObj - > instances ) { newInst - > apply_arrange_result ( p . translation . cast < double > ( ) , p . rotation ) ; }
//m_plater->sidebar().obj_list()->paste_objects_into_list({m_plater->model().objects.size()-1});
2022-07-15 15:37:19 +00:00
} ;
m_selected . emplace_back ( ap ) ;
}
m_status_range = m_selected . size ( ) ;
// The strides have to be removed from the fixed items. For the
// arrangeable (selected) items bed_idx is ignored and the
// translation is irrelevant.
//BBS: remove logic for unselected object
/*double stride = bed_stride(m_plater);
for ( auto & p : m_unselected )
if ( p . bed_idx > 0 )
p . translation ( X ) - = p . bed_idx * stride ; */
}
void FillBedJob : : process ( )
{
if ( m_object_idx = = - 1 | | m_selected . empty ( ) ) return ;
2024-07-22 06:19:54 +00:00
const Slic3r : : DynamicPrintConfig & global_config = wxGetApp ( ) . preset_bundle - > full_config ( ) ;
2022-07-15 15:37:19 +00:00
2024-07-22 06:19:54 +00:00
update_arrange_params ( params , global_config , m_selected ) ;
m_bedpts = get_shrink_bedpts ( global_config , params ) ;
2023-05-23 09:57:42 +00:00
2023-04-23 08:05:31 +00:00
auto & partplate_list = m_plater - > get_partplate_list ( ) ;
2023-06-19 10:25:24 +00:00
if ( params . avoid_extrusion_cali_region & & global_config . opt_bool ( " scan_first_layer " ) )
2023-04-23 08:05:31 +00:00
partplate_list . preprocess_nonprefered_areas ( m_unselected , MAX_NUM_PLATES ) ;
2023-05-23 09:57:42 +00:00
2024-07-22 06:19:54 +00:00
update_selected_items_inflation ( m_selected , global_config , params ) ;
update_unselected_items_inflation ( m_unselected , global_config , params ) ;
2022-07-15 15:37:19 +00:00
bool do_stop = false ;
params . stopcondition = [ this , & do_stop ] ( ) {
return was_canceled ( ) | | do_stop ;
} ;
params . progressind = [ this ] ( unsigned st , std : : string str = " " ) {
2023-04-23 08:05:31 +00:00
if ( st > 0 )
2023-11-27 01:04:02 +00:00
update_status ( st , _L ( " Filling " ) + " " + wxString : : FromUTF8 ( str ) ) ;
2022-07-15 15:37:19 +00:00
} ;
params . on_packed = [ & do_stop ] ( const ArrangePolygon & ap ) {
do_stop = ap . bed_idx > 0 & & ap . priority = = 0 ;
} ;
2023-06-19 08:37:12 +00:00
// final align用的是凸包, 在有fixed item的情况下可能找到的参考点位置是错的, 这里就不做了。见STUDIO-3265
params . do_final_align = false ;
2022-07-15 15:37:19 +00:00
2024-02-18 03:48:32 +00:00
if ( m_selected . size ( ) > 100 ) {
// too many items, just find grid empty cells to put them
Vec2f step = unscaled < float > ( get_extents ( m_selected . front ( ) . poly ) . size ( ) ) + Vec2f ( m_selected . front ( ) . brim_width , m_selected . front ( ) . brim_width ) ;
std : : vector < Vec2f > empty_cells = Plater : : get_empty_cells ( step ) ;
size_t n = std : : min ( m_selected . size ( ) , empty_cells . size ( ) ) ;
for ( size_t i = 0 ; i < n ; i + + ) {
m_selected [ i ] . translation = scaled < coord_t > ( empty_cells [ i ] ) ;
m_selected [ i ] . bed_idx = 0 ;
}
for ( size_t i = n ; i < m_selected . size ( ) ; i + + ) {
m_selected [ i ] . bed_idx = - 1 ;
}
}
else
arrangement : : arrange ( m_selected , m_unselected , m_bedpts , params ) ;
2022-07-15 15:37:19 +00:00
// finalize just here.
2023-04-23 08:05:31 +00:00
update_status ( m_status_range , was_canceled ( ) ?
2023-11-27 01:04:02 +00:00
_L ( " Bed filling canceled. " ) :
_L ( " Bed filling done. " ) ) ;
2022-07-15 15:37:19 +00:00
}
void FillBedJob : : finalize ( )
{
// Ignore the arrange result if aborted.
if ( was_canceled ( ) ) return ;
if ( m_object_idx = = - 1 ) return ;
ModelObject * model_object = m_plater - > model ( ) . objects [ m_object_idx ] ;
if ( model_object - > instances . empty ( ) ) return ;
//BBS: partplate
PartPlateList & plate_list = m_plater - > get_partplate_list ( ) ;
int plate_cols = plate_list . get_plate_cols ( ) ;
int cur_plate = plate_list . get_curr_plate_index ( ) ;
size_t inst_cnt = model_object - > instances . size ( ) ;
int added_cnt = std : : accumulate ( m_selected . begin ( ) , m_selected . end ( ) , 0 , [ ] ( int s , auto & ap ) {
return s + int ( ap . priority = = 0 & & ap . bed_idx = = 0 ) ;
} ) ;
2023-04-23 08:05:31 +00:00
int oldSize = m_plater - > model ( ) . objects . size ( ) ;
2022-07-15 15:37:19 +00:00
if ( added_cnt > 0 ) {
//BBS: adjust the selected instances
for ( ArrangePolygon & ap : m_selected ) {
if ( ap . bed_idx ! = 0 ) {
2023-04-23 08:05:31 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < boost : : format ( " :skipped: bed_id %1%, trans {%2%,%3%} " ) % ap . bed_idx % unscale < double > ( ap . translation ( X ) ) % unscale < double > ( ap . translation ( Y ) ) ;
/*if (ap.itemid == -1)*/
2022-07-15 15:37:19 +00:00
continue ;
ap . bed_idx = plate_list . get_plate_count ( ) ;
}
else
ap . bed_idx = cur_plate ;
2024-03-22 12:20:40 +00:00
if ( m_selected . size ( ) < = 100 ) {
ap . row = ap . bed_idx / plate_cols ;
ap . col = ap . bed_idx % plate_cols ;
ap . translation ( X ) + = bed_stride_x ( m_plater ) * ap . col ;
ap . translation ( Y ) - = bed_stride_y ( m_plater ) * ap . row ;
}
2022-07-15 15:37:19 +00:00
ap . apply ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < boost : : format ( " :selected: bed_id %1%, trans {%2%,%3%} " ) % ap . bed_idx % unscale < double > ( ap . translation ( X ) ) % unscale < double > ( ap . translation ( Y ) ) ;
}
2023-04-23 08:05:31 +00:00
int newSize = m_plater - > model ( ) . objects . size ( ) ;
auto obj_list = m_plater - > sidebar ( ) . obj_list ( ) ;
for ( size_t i = oldSize ; i < newSize ; i + + ) {
obj_list - > add_object_to_list ( i , true , true , false ) ;
2023-11-13 08:47:31 +00:00
obj_list - > update_printable_state ( i , 0 ) ;
2022-07-15 15:37:19 +00:00
}
2023-04-23 08:05:31 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < " : paste_objects_into_list " ;
2022-07-15 15:37:19 +00:00
/*for (ArrangePolygon& ap : m_selected) {
if ( ap . bed_idx ! = arrangement : : UNARRANGED & & ( ap . priority ! = 0 | | ap . bed_idx = = 0 ) )
ap . apply ( ) ;
} */
2023-04-23 08:05:31 +00:00
//model_object->ensure_on_bed();
//BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": model_object->ensure_on_bed()";
2022-07-15 15:37:19 +00:00
m_plater - > update ( ) ;
}
Job : : finalize ( ) ;
}
} } // namespace Slic3r::GUI