Merge some BS1.7 changes:

bring back fill_bed feature, original implemention from PrusaSlicer. BS added exclusion logic.
This commit is contained in:
SoftFever 2023-08-07 23:42:27 +08:00
parent b8db25ac0e
commit 3acd89e877
20 changed files with 242 additions and 180 deletions

View file

@ -86,18 +86,21 @@ template<class PConf>
void fill_config(PConf& pcfg, const ArrangeParams &params) {
if (params.is_seq_print) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
// Start placing the items from the center of the print bed
pcfg.starting_point = PConf::Alignment::BOTTOM_LEFT;
}
else {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
// Start placing the items from the center of the print bed
pcfg.starting_point = PConf::Alignment::TOP_RIGHT;
}
if (params.do_final_align) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
}else
pcfg.alignment = PConf::Alignment::DONT_ALIGN;
// Try 4 angles (45 degree step) and find the one with min cost
if (params.allow_rotations)
pcfg.rotations = {0., PI / 4., PI/2, 3. * PI / 4. };
@ -452,27 +455,25 @@ protected:
}
std::set<int> extruder_ids;
int non_virt_cnt = 0;
std::set<int> first_object_extruder_ids;
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) continue;
extruder_ids.insert(p.extrude_ids.begin(),p.extrude_ids.end());
non_virt_cnt++;
if (non_virt_cnt == 1) { first_object_extruder_ids.insert(p.extrude_ids.begin(), p.extrude_ids.end()); }
}
extruder_ids.insert(item.extrude_ids.begin(),item.extrude_ids.end());
// add a large cost if not multi materials on same plate is not allowed
if (!params.allow_multi_materials_on_same_plate) {
bool first_object = non_virt_cnt == 0;
bool same_color_with_first_object = std::all_of(item.extrude_ids.begin(), item.extrude_ids.end(),
[&](int color) { return first_object_extruder_ids.find(color) != first_object_extruder_ids.end(); });
// non_virt_cnt==0 means it's the first object, which can be multi-color
if (!(first_object || same_color_with_first_object)) score += LARGE_COST_TO_REJECT * 1.3;
// it's the first object, which can be multi-color
bool first_object = extruder_ids.empty();
// the two objects (previously packed items and the current item) are considered having same color if either one's colors are a subset of the other
std::set<int> item_extruder_ids(item.extrude_ids.begin(), item.extrude_ids.end());
bool same_color_with_previous_items = std::includes(item_extruder_ids.begin(), item_extruder_ids.end(), extruder_ids.begin(), extruder_ids.end())
|| std::includes(extruder_ids.begin(), extruder_ids.end(), item_extruder_ids.begin(), item_extruder_ids.end());
if (!(first_object || same_color_with_previous_items)) score += LARGE_COST_TO_REJECT * 1.3;
}
// for layered printing, we want extruder change as few as possible
// this has very weak effect, CAN NOT use a large weight
extruder_ids.insert(item.extrude_ids.begin(), item.extrude_ids.end());
if (!params.is_seq_print) {
score += 1 * std::max(0, ((int) extruder_ids.size() - 1));
}
@ -544,12 +545,6 @@ public:
if (items.empty()) return;
auto binbb = sl::boundingBox(m_bin);
// BBS: excluded region (virtual object but not wipe tower) should not affect final alignment
//bool all_is_excluded_region = std::all_of(items.begin(), items.end(), [](Item &itm) { return itm.is_virt_object && !itm.is_wipe_tower; });
//if (!all_is_excluded_region)
// cfg.alignment = PConfig::Alignment::DONT_ALIGN;
//else
// cfg.alignment = PConfig::Alignment::CENTER;
auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? binbb.minCorner() : binbb.center();
// if we have wipe tower, items should be arranged around wipe tower
@ -561,15 +556,7 @@ public:
}
cfg.object_function = [this, binbb, starting_point](const Item &item, const ItemGroup &packed_items) {
// 在我们的摆盘中,没有天然的固定对象。固定对象只有:屏蔽区域、挤出补偿区域、料塔。
// 对于屏蔽区域,摆入的对象仍然是可以向右上滑动的;
// 对挤出料塔,摆入的对象不能滑动(必须围绕料塔)
bool pack_around_wipe_tower = std::any_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_wipe_tower; });
//if(pack_around_wipe_tower)
return fixed_overfit(objfunc(item, starting_point), binbb);
//else {
// return fixed_overfit_topright_sliding(objfunc(item, starting_point), binbb, m_excluded_and_extruCali_regions);
//}
return fixed_overfit(objfunc(item, starting_point), binbb);
};
};

View file

@ -57,7 +57,7 @@ struct ArrangePolygon {
//BBS: add row/col for sudoku-style layout
int row{0};
int col{0};
std::vector<int> extrude_ids{1}; ///extruder_id for least extruder switch
std::vector<int> extrude_ids{}; /// extruder_id for least extruder switch
int bed_temp{0}; ///bed temperature for different material judge
int print_temp{0}; ///print temperature for different material judge
int first_bed_temp{ 0 }; ///first layer bed temperature for different material judge
@ -114,6 +114,8 @@ struct ArrangeParams {
bool allow_rotations = false;
bool do_final_align = true;
//BBS: add specific arrange params
bool allow_multi_materials_on_same_plate = true;
bool avoid_extrusion_cali_region = true;

View file

@ -807,7 +807,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
offset_top_surface = 0;
// don't takes into account too thin areas
// skip if the exposed area is smaller than 2x perimeter width
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 2.0 * (double(perimeter_width)));
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 2.5 * (double(perimeter_width)));
Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface);

View file

@ -685,9 +685,9 @@ bool Preset::is_custom_defined()
return false;
}
bool Preset::is_bbl_vendor_preset(PresetBundle *preset_bundle)
bool Preset::has_lidar(PresetBundle *preset_bundle)
{
bool is_bbl_vendor_preset = false;
bool has_lidar = false;
if (preset_bundle) {
auto config = &preset_bundle->printers.get_edited_preset().config;
std::string vendor_name;
@ -700,9 +700,9 @@ bool Preset::is_bbl_vendor_preset(PresetBundle *preset_bundle)
}
}
if (!vendor_name.empty())
is_bbl_vendor_preset = vendor_name.compare("BBL") == 0 ? true : false;
has_lidar = vendor_name.compare("BBL") == 0 ? true : false;
}
return is_bbl_vendor_preset;
return has_lidar;
}
static std::vector<std::string> s_Preset_print_options {

View file

@ -302,7 +302,7 @@ public:
std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type
bool is_custom_defined();
bool is_bbl_vendor_preset(PresetBundle *preset_bundle);
bool has_lidar(PresetBundle *preset_bundle);

View file

@ -187,7 +187,7 @@ void BackgroundSlicingProcess::process_fff()
{
assert(m_print == m_fff_print);
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
m_fff_print->is_BBL_printer() = preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle);
m_fff_print->is_BBL_printer() = preset_bundle.printers.get_edited_preset().has_lidar(&preset_bundle);
//BBS: add the logic to process from an existed gcode file
if (m_print->finished()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: skip slicing, to process previous gcode file")%__LINE__;

View file

@ -5399,11 +5399,13 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
// only show this option if the printer has micro Lidar and can do first layer scan
DynamicPrintConfig &current_config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const bool has_lidar = preset_bundle->printers.get_edited_preset().has_lidar(preset_bundle);
auto op = current_config.option("scan_first_layer");
if (op && op->getBool()) {
if (has_lidar && op && op->getBool()) {
if (imgui->bbl_checkbox(_L("Avoid extrusion calibration region"), settings.avoid_extrusion_cali_region)) {
settings_out.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region;
appcfg->set("arrange", avoid_extrusion_key.c_str(), settings_out.avoid_extrusion_cali_region);
appcfg->set("arrange", avoid_extrusion_key.c_str(), settings_out.avoid_extrusion_cali_region ? "1" : "0");
settings_changed = true;
}
} else {

View file

@ -656,6 +656,13 @@ wxMenuItem* MenuFactory::append_menu_item_instance_to_object(wxMenu* menu)
return menu_item;
}
void MenuFactory::append_menu_item_fill_bed(wxMenu *menu)
{
append_menu_item(
menu, wxID_ANY, _L("Fill bed with copies"), _L("Fill the remaining area of bed with copies of the selected object"),
[](wxCommandEvent &) { plater()->fill_bed_with_instances(); }, "", nullptr, []() { return plater()->can_increase_instances(); }, m_parent);
}
wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
{
// BBS: to be checked
@ -1040,8 +1047,9 @@ void MenuFactory::create_object_menu()
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
}
void MenuFactory::create_bbl_object_menu()
void MenuFactory::create_extra_object_menu()
{
append_menu_item_fill_bed(&m_object_menu);
// Object Clone
append_menu_item_clone(&m_object_menu);
// Object Repair
@ -1262,7 +1270,7 @@ void MenuFactory::init(wxWindow* parent)
create_sla_object_menu();
//create_part_menu();
create_bbl_object_menu();
create_extra_object_menu();
create_bbl_part_menu();
create_bbl_assemble_object_menu();
create_bbl_assemble_part_menu();

View file

@ -108,7 +108,7 @@ private:
//BBS: add part plate related logic
void create_plate_menu();
//BBS: add bbl object menu
void create_bbl_object_menu();
void create_extra_object_menu();
void create_bbl_part_menu();
void create_bbl_assemble_object_menu();
void create_bbl_assemble_part_menu();
@ -150,6 +150,7 @@ private:
void append_menu_item_change_filament(wxMenu* menu);
void append_menu_item_set_printable(wxMenu* menu);
void append_menu_item_locked(wxMenu* menu);
void append_menu_item_fill_bed(wxMenu *menu);
};

View file

@ -3547,7 +3547,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, bool notify_partplate)
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, bool notify_partplate, bool do_info_update)
{
auto model_object = (*m_objects)[obj_idx];
//BBS start add obj_idx for debug
@ -3564,6 +3564,9 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed,
const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut());
Expand(m_objects_model->GetParent(item));
if (!do_info_update)
return;
update_info_items(obj_idx, nullptr, call_selection_changed);
add_volumes_to_object_in_list(obj_idx);

View file

@ -325,7 +325,7 @@ public:
void part_selection_changed();
// Add object to the list
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true);
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true, bool do_info_update = true);
// Add object's volumes to the list
// Return selected items, if add_to_selection is defined
wxDataViewItemArray add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume *)> add_to_selection = nullptr);

View file

@ -353,7 +353,7 @@ void ArrangeJob::prepare_partplate() {
ModelObject* mo = model.objects[oidx];
for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
{
bool in_plate = plate->contain_instance(oidx, inst_idx);
bool in_plate = plate->contain_instance(oidx, inst_idx) || plate->intersect_instance(oidx, inst_idx);
ArrangePolygon&& ap = prepare_arrange_polygon(mo->instances[inst_idx]);
ArrangePolygons& cont = mo->instances[inst_idx]->printable ?
@ -392,20 +392,7 @@ void ArrangeJob::prepare()
NotificationManager::NotificationLevel::RegularNotificationLevel, _u8L("Arranging..."));
m_plater->get_notification_manager()->bbl_close_plateinfo_notification();
{
const GLCanvas3D::ArrangeSettings &settings = static_cast<const GLCanvas3D *>(m_plater->canvas3D())->get_arrange_settings();
auto & print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
params.clearance_height_to_rod = print.config().extruder_clearance_height_to_rod.value;
params.clearance_height_to_lid = print.config().extruder_clearance_height_to_lid.value;
params.cleareance_radius = print.config().extruder_clearance_radius.value;
params.printable_height = print.config().printable_height.value;
params.allow_rotations = settings.enable_rotation;
params.allow_multi_materials_on_same_plate = settings.allow_multi_materials_on_same_plate;
params.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region;
params.is_seq_print = settings.is_seq_print;
params.min_obj_distance = scaled(settings.distance);
}
params = init_arrange_params(m_plater);
//BBS update extruder params and speed table before arranging
Plater::setExtruderParams(Model::extruderParamsMap);
@ -510,85 +497,27 @@ void ArrangeJob::process()
auto & partplate_list = m_plater->get_partplate_list();
auto& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
if (params.is_seq_print)
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
if (params.avoid_extrusion_cali_region && print.full_print_config().opt_bool("scan_first_layer"))
const Slic3r::DynamicPrintConfig& global_config = wxGetApp().preset_bundle->full_config();
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const bool has_lidar = preset_bundle->printers.get_edited_preset().has_lidar(preset_bundle);
if (has_lidar && params.avoid_extrusion_cali_region && global_config.opt_bool("scan_first_layer"))
partplate_list.preprocess_nonprefered_areas(m_unselected, MAX_NUM_PLATES);
double skirt_distance = print.has_skirt() ? print.config().skirt_distance.value : 0;
double brim_max = 0;
std::for_each(m_selected.begin(), m_selected.end(), [&](ArrangePolygon ap) { brim_max = std::max(brim_max, ap.brim_width); });
update_arrange_params(params, *m_plater, m_selected);
update_selected_items_inflation(m_selected, *m_plater, params);
update_unselected_items_inflation(m_unselected, *m_plater, params);
// Note: skirt_distance is now defined between outermost brim and skirt, not the object and skirt.
// So we can't do max but do adding instead.
params.brim_skirt_distance = skirt_distance + brim_max;
params.bed_shrink_x = settings.bed_shrink_x + params.brim_skirt_distance;
params.bed_shrink_y = settings.bed_shrink_y + params.brim_skirt_distance;
// for sequential print, we need to inflate the bed because cleareance_radius is so large
if (params.is_seq_print) {
float shift_dist = params.cleareance_radius / 2 - 5;
params.bed_shrink_x -= shift_dist;
params.bed_shrink_y -= shift_dist;
// dont forget to move the excluded region
for (auto& region : m_unselected) {
if (region.is_virt_object)
region.poly.translate(-scaled(shift_dist), -scaled(shift_dist));
}
}
if (print.full_print_config().opt_bool("enable_support")) {
params.bed_shrink_x = std::max(5.f, params.bed_shrink_x);
params.bed_shrink_y = std::max(5.f, params.bed_shrink_y);
params.min_obj_distance = std::max(scaled(10.0), params.min_obj_distance);
}
// do not inflate brim_width. Objects are allowed to have overlapped brim.
Points bedpts = get_bed_shape(*m_plater->config());
BoundingBox bedbb = Polygon(bedpts).bounding_box();
std::for_each(m_selected.begin(), m_selected.end(), [&](ArrangePolygon &ap) {
ap.inflation = params.min_obj_distance / 2;
BoundingBox apbb = ap.poly.contour.bounding_box();
auto diffx = bedbb.size().x() - apbb.size().x() - 5;
auto diffy = bedbb.size().y() - apbb.size().y() - 5;
if (diffx > 0 && diffy > 0) {
auto min_diff = std::min(diffx, diffy);
ap.inflation = std::min(min_diff / 2, ap.inflation);
}
});
// For occulusion regions, inflation should be larger to prevent genrating brim on them.
// However, extrusion cali regions are exceptional, since we can allow brim overlaps them.
// 屏蔽区域只需要膨胀brim宽度防止brim长过去挤出标定区域不需要膨胀brim可以长过去。
// 以前我们认为还需要膨胀clearance_radius/2这其实是不需要的因为这些区域并不会真的摆放物体
// 其他物体的膨胀轮廓是可以跟它们重叠的。
Points bedpts = get_shrink_bedpts(*m_plater,params);
double scaled_exclusion_gap = scale_(1);
std::for_each(m_unselected.begin(), m_unselected.end(), [&](auto &ap) {
ap.inflation = !ap.is_virt_object ?
params.min_obj_distance / 2 :
(ap.is_extrusion_cali_object ? 0 : scaled_exclusion_gap);
});
partplate_list.preprocess_exclude_areas(params.excluded_regions, 1, scaled_exclusion_gap);
// shrink bed by moving to center by dist
auto shrinkFun = [](Points& bedpts, double dist, int direction) {
#define SGN(x) ((x)>=0?1:-1)
Point center = Polygon(bedpts).bounding_box().center();
for (auto& pt : bedpts)
pt[direction] += dist * SGN(center[direction] - pt[direction]);
};
shrinkFun(bedpts, scaled(params.bed_shrink_x), 0);
shrinkFun(bedpts, scaled(params.bed_shrink_y), 1);
BOOST_LOG_TRIVIAL(debug) << "arrange bed_shrink_x=" << params.bed_shrink_x
<< ", brim_max= "<<brim_max<<", "
<< "; bedpts:" << bedpts[0].transpose() << ", " << bedpts[1].transpose() << ", " << bedpts[2].transpose() << ", " << bedpts[3].transpose();
params.stopcondition = [this]() { return was_canceled(); };
params.progressind = [this](unsigned num_finished, std::string str="") {
update_status(num_finished, _L("Arranging") + " " + str);
params.progressind = [this](unsigned num_finished, std::string str = "") {
update_status(num_finished, _L("Arranging") + " "+ wxString::FromUTF8(str));
};
{
@ -765,8 +694,8 @@ void ArrangeJob::finalize() {
std::optional<arrangement::ArrangePolygon>
get_wipe_tower_arrangepoly(const Plater &plater)
{
// BBS FIXME: use actual plate_idx
if (auto wti = get_wipe_tower(plater, 0))
int id = plater.canvas3D()->fff_print()->get_plate_index();
if (auto wti = get_wipe_tower(plater, id))
return get_wipetower_arrange_poly(&wti);
return {};
@ -775,12 +704,12 @@ get_wipe_tower_arrangepoly(const Plater &plater)
//BBS: add sudoku-style stride
double bed_stride_x(const Plater* plater) {
double bedwidth = plater->build_volume().bounding_box().size().x();
return scaled<double>((1. + LOGICAL_BED_GAP) * bedwidth);
return (1. + LOGICAL_BED_GAP) * bedwidth;
}
double bed_stride_y(const Plater* plater) {
double beddepth = plater->build_volume().bounding_box().size().y();
return scaled<double>((1. + LOGICAL_BED_GAP) * beddepth);
return (1. + LOGICAL_BED_GAP) * beddepth;
}
@ -800,4 +729,107 @@ arrangement::ArrangeParams get_arrange_params(Plater *p)
return params;
}
// call before get selected and unselected
arrangement::ArrangeParams init_arrange_params(Plater *p)
{
arrangement::ArrangeParams params;
const GLCanvas3D::ArrangeSettings &settings = static_cast<const GLCanvas3D *>(p->canvas3D())->get_arrange_settings();
auto & print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
params.clearance_height_to_rod = print.config().extruder_clearance_height_to_rod.value;
params.clearance_height_to_lid = print.config().extruder_clearance_height_to_lid.value;
params.cleareance_radius = print.config().extruder_clearance_radius.value;
params.printable_height = print.config().printable_height.value;
params.allow_rotations = settings.enable_rotation;
params.allow_multi_materials_on_same_plate = settings.allow_multi_materials_on_same_plate;
params.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region;
params.is_seq_print = settings.is_seq_print;
params.min_obj_distance = scaled(settings.distance);
params.bed_shrink_x = settings.bed_shrink_x;
params.bed_shrink_y = settings.bed_shrink_y;
int state = p->get_prepare_state();
if (state == Job::JobPrepareState::PREPARE_STATE_MENU) {
PartPlateList &plate_list = p->get_partplate_list();
PartPlate * plate = plate_list.get_curr_plate();
params.is_seq_print = plate->get_real_print_seq() == PrintSequence::ByObject;
}
if (params.is_seq_print)
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
return params;
}
//after get selected.call this to update bed_shrink
void update_arrange_params(arrangement::ArrangeParams &params, const Plater &p, const arrangement::ArrangePolygons &selected)
{
const GLCanvas3D::ArrangeSettings &settings = static_cast<const GLCanvas3D *>(p.canvas3D())->get_arrange_settings();
auto & print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
double skirt_distance = print.has_skirt() ? print.config().skirt_distance.value : 0;
double brim_max = 0;
std::for_each(selected.begin(), selected.end(), [&](const ArrangePolygon &ap) { brim_max = std::max(brim_max, ap.brim_width); });
// Note: skirt_distance is now defined between outermost brim and skirt, not the object and skirt.
// So we can't do max but do adding instead.
params.brim_skirt_distance = skirt_distance + brim_max;
params.bed_shrink_x = settings.bed_shrink_x + params.brim_skirt_distance;
params.bed_shrink_y = settings.bed_shrink_y + params.brim_skirt_distance;
// for sequential print, we need to inflate the bed because cleareance_radius is so large
if (params.is_seq_print) {
float shift_dist = params.cleareance_radius / 2 - 5;
params.bed_shrink_x -= shift_dist;
params.bed_shrink_y -= shift_dist;
}
}
//it will bed accurate after call update_params
Points get_shrink_bedpts(const Plater &plater, const arrangement::ArrangeParams &params)
{
Points bedpts = get_bed_shape(*plater.config());
// shrink bed by moving to center by dist
auto shrinkFun = [](Points &bedpts, double dist, int direction) {
#define SGN(x) ((x) >= 0 ? 1 : -1)
Point center = Polygon(bedpts).bounding_box().center();
for (auto &pt : bedpts) pt[direction] += dist * SGN(center[direction] - pt[direction]);
};
shrinkFun(bedpts, scaled(params.bed_shrink_x), 0);
shrinkFun(bedpts, scaled(params.bed_shrink_y), 1);
return bedpts;
}
void update_selected_items_inflation(arrangement::ArrangePolygons &selected, const Plater &p, const arrangement::ArrangeParams &params) {
// do not inflate brim_width. Objects are allowed to have overlapped brim.
Points bedpts = get_shrink_bedpts(p, params);
BoundingBox bedbb = Polygon(bedpts).bounding_box();
std::for_each(selected.begin(), selected.end(), [&](ArrangePolygon &ap) {
ap.inflation = std::max(scaled(ap.brim_width), params.min_obj_distance / 2);
BoundingBox apbb = ap.poly.contour.bounding_box();
auto diffx = bedbb.size().x() - apbb.size().x() - 5;
auto diffy = bedbb.size().y() - apbb.size().y() - 5;
if (diffx > 0 && diffy > 0) {
auto min_diff = std::min(diffx, diffy);
ap.inflation = std::min(min_diff / 2, ap.inflation);
}
});
}
void update_unselected_items_inflation(arrangement::ArrangePolygons &unselected, const Plater &p, const arrangement::ArrangeParams &params)
{
if (params.is_seq_print) {
float shift_dist = params.cleareance_radius / 2 - 5;
// dont forget to move the excluded region
for (auto &region : unselected) {
if (region.is_virt_object) region.poly.translate(-scaled(shift_dist), -scaled(shift_dist));
}
}
// For occulusion regions, inflation should be larger to prevent genrating brim on them.
// However, extrusion cali regions are exceptional, since we can allow brim overlaps them.
// 屏蔽区域只需要膨胀brim宽度防止brim长过去挤出标定区域不需要膨胀brim可以长过去。
// 以前我们认为还需要膨胀clearance_radius/2这其实是不需要的因为这些区域并不会真的摆放物体
// 其他物体的膨胀轮廓是可以跟它们重叠的。
double scaled_exclusion_gap = scale_(1);
std::for_each(unselected.begin(), unselected.end(),
[&](auto &ap) { ap.inflation = !ap.is_virt_object ? std::max(scaled(ap.brim_width), params.min_obj_distance / 2)
: (ap.is_extrusion_cali_object ? 0 : scaled_exclusion_gap); });
}
}} // namespace Slic3r::GUI

View file

@ -78,6 +78,16 @@ double bed_stride_y(const Plater* plater);
arrangement::ArrangeParams get_arrange_params(Plater *p);
arrangement::ArrangeParams init_arrange_params(Plater *p);
Points get_shrink_bedpts(const Plater& plater,const arrangement::ArrangeParams& params);
void update_arrange_params(arrangement::ArrangeParams &params, const Plater &p, const arrangement::ArrangePolygons &selected);
void update_selected_items_inflation(arrangement::ArrangePolygons &selected, const Plater &p, const arrangement::ArrangeParams &params);
void update_unselected_items_inflation(arrangement::ArrangePolygons &unselected, const Plater &p, const arrangement::ArrangeParams &params);
}} // namespace Slic3r::GUI
#endif // ARRANGEJOB_HPP

View file

@ -6,6 +6,7 @@
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "libnest2d/common.hpp"
#include <numeric>
@ -22,6 +23,8 @@ void FillBedJob::prepare()
m_unselected.clear();
m_bedpts.clear();
params = init_arrange_params(m_plater);
m_object_idx = m_plater->get_selected_object_idx();
if (m_object_idx == -1)
return;
@ -42,6 +45,7 @@ void FillBedJob::prepare()
ModelObject *model_object = m_plater->model().objects[m_object_idx];
if (model_object->instances.empty()) return;
const Slic3r::DynamicPrintConfig& global_config = wxGetApp().preset_bundle->full_config();
m_selected.reserve(model_object->instances.size());
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx)
{
@ -50,7 +54,7 @@ void FillBedJob::prepare()
{
bool selected = (oidx == m_object_idx);
ArrangePolygon ap = get_arrange_poly(mo->instances[inst_idx]);
ArrangePolygon ap = get_instance_arrange_poly(mo->instances[inst_idx], global_config);
BoundingBox ap_bb = ap.transformed_poly().contour.bounding_box();
ap.height = 1;
ap.name = mo->name;
@ -117,6 +121,8 @@ void FillBedJob::prepare()
if (m_selected.empty()) return;
//add the virtual object into unselect list if has
double scaled_exclusion_gap = scale_(1);
plate_list.preprocess_exclude_areas(params.excluded_regions, 1, scaled_exclusion_gap);
plate_list.preprocess_exclude_areas(m_unselected);
m_bedpts = get_bed_shape(*m_plater->config());
@ -134,14 +140,15 @@ void FillBedJob::prepare()
ap.bed_idx = arrangement::UNARRANGED;
m_unselected.emplace_back(ap);
}
}*/
if (auto wt = get_wipe_tower_arrangepoly(*m_plater))
m_unselected.emplace_back(std::move(*wt));*/
m_unselected.emplace_back(std::move(*wt));
double sc = scaled<double>(1.) * scaled(1.);
ExPolygon poly = m_selected.front().poly;
const GLCanvas3D::ArrangeSettings& settings = static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings();
auto polys = offset_ex(m_selected.front().poly, scaled(settings.distance) / 2);
ExPolygon poly = polys.empty() ? m_selected.front().poly : polys.front();
double poly_area = poly.area() / sc;
double unsel_area = std::accumulate(m_unselected.begin(),
m_unselected.end(), 0.,
@ -161,7 +168,7 @@ void FillBedJob::prepare()
// 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];
ArrangePolygon template_ap = get_arrange_poly(mi);
ArrangePolygon template_ap = get_instance_arrange_poly(mi, global_config);
for (int i = 0; i < needed_items; ++i) {
ArrangePolygon ap = template_ap;
@ -171,8 +178,10 @@ void FillBedJob::prepare()
ap.itemid = -1;
ap.setter = [this, mi](const ArrangePolygon &p) {
ModelObject *mo = m_plater->model().objects[m_object_idx];
ModelInstance *inst = mo->add_instance(*mi);
inst->apply_arrange_result(p.translation.cast<double>(), p.rotation);
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});
};
m_selected.emplace_back(ap);
}
@ -196,9 +205,19 @@ void FillBedJob::process()
const GLCanvas3D::ArrangeSettings &settings =
static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings();
arrangement::ArrangeParams params;
params.allow_rotations = settings.enable_rotation;
params.min_obj_distance = scaled(settings.distance);
update_arrange_params(params, *m_plater, m_selected);
m_bedpts = get_shrink_bedpts(*m_plater, params);
auto &partplate_list = m_plater->get_partplate_list();
auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
const Slic3r::DynamicPrintConfig& global_config = wxGetApp().preset_bundle->full_config();
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const bool has_lidar = preset_bundle->printers.get_edited_preset().has_lidar(preset_bundle);
if (has_lidar && params.avoid_extrusion_cali_region && global_config.opt_bool("scan_first_layer"))
partplate_list.preprocess_nonprefered_areas(m_unselected, MAX_NUM_PLATES);
update_selected_items_inflation(m_selected, *m_plater, params);
update_unselected_items_inflation(m_unselected, *m_plater, params);
bool do_stop = false;
params.stopcondition = [this, &do_stop]() {
@ -206,20 +225,22 @@ void FillBedJob::process()
};
params.progressind = [this](unsigned st,std::string str="") {
// if (st > 0)
// update_status(int(m_status_range - st), _L("Filling bed " + str));
if (st > 0)
update_status(st, _L("Filling bed " + str));
};
params.on_packed = [&do_stop] (const ArrangePolygon &ap) {
do_stop = ap.bed_idx > 0 && ap.priority == 0;
};
// final align用的是凸包在有fixed item的情况下可能找到的参考点位置是错的这里就不做了。见STUDIO-3265
params.do_final_align = !has_lidar;
arrangement::arrange(m_selected, m_unselected, m_bedpts, params);
// finalize just here.
// update_status(m_status_range, was_canceled() ?
// _(L("Bed filling canceled.")) :
// _(L("Bed filling done.")));
update_status(m_status_range, was_canceled() ?
_(L("Bed filling canceled.")) :
_(L("Bed filling done.")));
}
void FillBedJob::finalize()
@ -243,11 +264,14 @@ void FillBedJob::finalize()
return s + int(ap.priority == 0 && ap.bed_idx == 0);
});
int oldSize = m_plater->model().objects.size();
if (added_cnt > 0) {
//BBS: adjust the selected instances
for (ArrangePolygon& ap : m_selected) {
if (ap.bed_idx != 0) {
if (ap.itemid == -1)
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)*/
continue;
ap.bed_idx = plate_list.get_plate_count();
}
@ -263,33 +287,24 @@ void FillBedJob::finalize()
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));
}
for (size_t inst_idx = 0; inst_idx < model_object->instances.size(); ++inst_idx)
{
plate_list.notify_instance_update(m_object_idx, inst_idx);
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);
}
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": paste_objects_into_list";
/*for (ArrangePolygon& ap : m_selected) {
if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0))
ap.apply();
}*/
model_object->ensure_on_bed();
//model_object->ensure_on_bed();
//BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": model_object->ensure_on_bed()";
m_plater->update();
//BBS: add partplate related logic
int added_cnt = std::accumulate(m_selected.begin(), m_selected.end(), 0,
[cur_plate](int s, auto& ap) {
return s + int(ap.priority == 0 && ap.bed_idx == cur_plate);
//return s + int(ap.priority == 0 && ap.bed_idx == 0);
});
// FIXME: somebody explain why this is needed for increase_object_instances
if (inst_cnt == 1) added_cnt++;
if (added_cnt > 0)
m_plater->sidebar()
.obj_list()->increase_object_instances(m_object_idx, size_t(added_cnt));
}
Job::finalize();

View file

@ -21,6 +21,8 @@ class FillBedJob : public PlaterJob
Points m_bedpts;
arrangement::ArrangeParams params;
int m_status_range = 0;
protected:

View file

@ -549,7 +549,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
m_print_btn->Enable(m_print_enable);
if (m_print_enable) {
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle))
if (preset_bundle.printers.get_edited_preset().has_lidar(&preset_bundle))
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_PLATE));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_GCODE));
@ -907,7 +907,7 @@ void MainFrame::init_tabpanel() {
int old_sel = e.GetOldSelection();
int new_sel = e.GetSelection();
if (wxGetApp().preset_bundle &&
wxGetApp().preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(wxGetApp().preset_bundle) &&
wxGetApp().preset_bundle->printers.get_edited_preset().has_lidar(wxGetApp().preset_bundle) &&
new_sel == tpMonitor) {
if (!wxGetApp().getAgent()) {
e.Veto();
@ -1505,7 +1505,7 @@ wxBoxSizer* MainFrame::create_side_tools()
SidePopup* p = new SidePopup(this);
if (wxGetApp().preset_bundle
&& !wxGetApp().preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(wxGetApp().preset_bundle)) {
&& !wxGetApp().preset_bundle->printers.get_edited_preset().has_lidar(wxGetApp().preset_bundle)) {
// ThirdParty Buttons
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
export_gcode_btn->SetCornerRadius(0);
@ -3410,7 +3410,7 @@ void MainFrame::load_printer_url(wxString url)
void MainFrame::load_printer_url()
{
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle))
if (preset_bundle.printers.get_edited_preset().has_lidar(&preset_bundle))
return;
auto cfg = preset_bundle.printers.get_edited_preset().config;

View file

@ -23,7 +23,7 @@ PlateSettingsDialog::PlateSettingsDialog(wxWindow* parent, wxWindowID id, const
top_sizer->SetFlexibleDirection(wxBOTH);
top_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
bool is_bbl = wxGetApp().preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(wxGetApp().preset_bundle);
bool is_bbl = wxGetApp().preset_bundle->printers.get_edited_preset().has_lidar(wxGetApp().preset_bundle);
if (is_bbl) {
m_bed_type_choice = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(240), -1), 0,
NULL, wxCB_READONLY);

View file

@ -1024,7 +1024,7 @@ void Sidebar::update_all_preset_comboboxes()
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
bool is_bbl_preset = preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle);
bool is_bbl_preset = preset_bundle.printers.get_edited_preset().has_lidar(&preset_bundle);
auto p_mainframe = wxGetApp().mainframe;
@ -8183,7 +8183,7 @@ void Plater::_calib_pa_pattern(const Calib_Params& params)
const DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config();
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const bool is_bbl_machine = preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(preset_bundle);
const bool is_bbl_machine = preset_bundle->printers.get_edited_preset().has_lidar(preset_bundle);
const Vec3d plate_origin = get_partplate_list().get_current_plate_origin();
CalibPressureAdvancePattern pa_pattern(
params,
@ -10403,7 +10403,7 @@ void Plater::reslice()
model().calib_pa_pattern->generate_custom_gcodes(
wxGetApp().preset_bundle->full_config(),
preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(preset_bundle),
preset_bundle->printers.get_edited_preset().has_lidar(preset_bundle),
model(),
get_partplate_list().get_current_plate_origin()
);

View file

@ -1039,7 +1039,7 @@ void PlaterPresetComboBox::update()
//if (i + 1 == m_collection->num_default_presets())
// set_label_marker(Append(separator(L("System presets")), wxNullBitmap));
}
if (m_type == Preset::TYPE_FILAMENT && m_preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle))
if (m_type == Preset::TYPE_FILAMENT && m_preset_bundle->printers.get_edited_preset().has_lidar(m_preset_bundle))
add_ams_filaments(into_u8(selected_user_preset), true);
//BBS: add project embedded preset logic
@ -1267,7 +1267,7 @@ void TabPresetComboBox::update()
// set_label_marker(Append(separator(L("System presets")), wxNullBitmap));
}
if (m_type == Preset::TYPE_FILAMENT && m_preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle))
if (m_type == Preset::TYPE_FILAMENT && m_preset_bundle->printers.get_edited_preset().has_lidar(m_preset_bundle))
add_ams_filaments(into_u8(selected));
//BBS: add project embedded preset logic

View file

@ -1644,9 +1644,9 @@ void Tab::on_presets_changed()
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
wxGetApp().plater()->sidebar().update_presets(m_type);
bool is_bbl_vendor_preset = wxGetApp().preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(wxGetApp().preset_bundle);
bool has_lidar = wxGetApp().preset_bundle->printers.get_edited_preset().has_lidar(wxGetApp().preset_bundle);
auto& printer_cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (is_bbl_vendor_preset)
if (has_lidar)
wxGetApp().plater()->get_partplate_list().set_render_option(
!printer_cfg.option<ConfigOptionBool>("bbl_calib_mark_logo")->value, true);
else
@ -2873,7 +2873,7 @@ void TabFilament::toggle_options()
bool is_BBL_printer = false;
if (m_preset_bundle) {
is_BBL_printer =
m_preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(
m_preset_bundle->printers.get_edited_preset().has_lidar(
m_preset_bundle);
}
@ -3669,7 +3669,7 @@ void TabPrinter::toggle_options()
//BBS: whether the preset is Bambu Lab printer
bool is_BBL_printer = false;
if (m_preset_bundle) {
is_BBL_printer = m_preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle);
is_BBL_printer = m_preset_bundle->printers.get_edited_preset().has_lidar(m_preset_bundle);
}
bool have_multiple_extruders = true;