ENH: improve auto-arrange in several ways

1. reduce expansion of exclusion regions
2. expand extrusion calib regions to let them touch bed boundary, to
   greately simplify auto-arranging with avoidance option on
3. improve dist_for_BOTTOM_LEFT to allow objects be put left to
    exclusion regions temporarily
4. improve on_preload for better handling objects around exclusion regions.
5. improve debug tools
6. fix a bug with wipe tower estimation (don't estimate if wipe tower is
    explicitly disabled)
7. use larger y-overlap threshold to estimate rod height confliction
   better in per-object print ordering (now we use half the clearance
   radius)

Change-Id: Iab29d47a072d8515f28a09855432f92fcffa8c5f
(cherry picked from commit 3a4f242a3a6fd2f82dcc8306cde4d1cb107a5099)
This commit is contained in:
Arthur 2022-10-28 20:39:24 +08:00 committed by Lane.Wei
parent 17bc464bac
commit 5fa771c6cb
8 changed files with 59 additions and 35 deletions

View File

@ -481,7 +481,7 @@ public:
auto d = bbin.center() - bb.center(); auto d = bbin.center() - bb.center();
_Rectangle<RawShape> rect(bb.width(), bb.height()); _Rectangle<RawShape> rect(bb.width(), bb.height());
rect.translate(bb.minCorner() + d); rect.translate(bb.minCorner() + d);
return sl::isInside(rect.transformedShape(), bin) ? -1.0 : 1; return sl::isInside(rect.transformedShape(), bin) ? -1.5 : 1;
} }
static inline double overfit(const RawShape& chull, const RawShape& bin) { static inline double overfit(const RawShape& chull, const RawShape& bin) {
@ -907,7 +907,7 @@ private:
} }
} }
if( best_score < global_score && best_score< LARGE_COST_TO_REJECT) { if( best_score < global_score) {
auto d = (getNfpPoint(optimum) - iv) + startpos; auto d = (getNfpPoint(optimum) - iv) + startpos;
final_tr = d; final_tr = d;
final_rot = initial_rot + rot; final_rot = initial_rot + rot;
@ -922,7 +922,10 @@ private:
#ifdef SVGTOOLS_HPP #ifdef SVGTOOLS_HPP
svg::SVGWriter<RawShape> svgwriter; svg::SVGWriter<RawShape> svgwriter;
svgwriter.setSize(binbb); Box binbb2(binbb.width() * 2, binbb.height() * 2, binbb.center()); // expand bbox to allow object be drawed outside
svgwriter.setSize(binbb2);
svgwriter.conf_.x0 = binbb.width();
svgwriter.conf_.y0 = -binbb.height()/2; // origin is top left corner
svgwriter.writeShape(box2RawShape(binbb), "none", "black"); svgwriter.writeShape(box2RawShape(binbb), "none", "black");
for (int i = 0; i < nfps.size(); i++) for (int i = 0; i < nfps.size(); i++)
svgwriter.writeShape(nfps[i], "none", "blue"); svgwriter.writeShape(nfps[i], "none", "blue");

View File

@ -43,7 +43,7 @@ public:
public: public:
operator bool() { return item_ptr_ != nullptr; } operator bool() { return item_ptr_ != nullptr; }
double overfit() const { return overfit_; } double overfit() const { return overfit_; }
double score_ = -1; double score_ = -1.11;
double score() { return score_; } double score() { return score_; }
int plate_id = 0; // BBS int plate_id = 0; // BBS
}; };

View File

@ -35,8 +35,9 @@ public:
}; };
private:
Config conf_; Config conf_;
private:
std::vector<std::string> svg_layers_; std::vector<std::string> svg_layers_;
bool finished_ = false; bool finished_ = false;
public: public:

View File

@ -202,14 +202,20 @@ protected:
// 1) Y distance of item corner to bed corner. Must be put above bed corner. (high weight) // 1) Y distance of item corner to bed corner. Must be put above bed corner. (high weight)
// 2) X distance of item corner to bed corner (low weight) // 2) X distance of item corner to bed corner (low weight)
// 3) item row occupancy (useful when rotation is enabled) // 3) item row occupancy (useful when rotation is enabled)
// 4需要允许往屏蔽区域的左边或下边去一点不然很多物体可能认为摆不进去实际上我们最后是可以做平移的
double dist_for_BOTTOM_LEFT(Box ibb, const ClipperLib::IntPoint& origin_pack) double dist_for_BOTTOM_LEFT(Box ibb, const ClipperLib::IntPoint& origin_pack)
{ {
double dist_corner_y = ibb.minCorner().y() - origin_pack.y(); double dist_corner_y = ibb.minCorner().y() - origin_pack.y();
double dist_corner_x = ibb.minCorner().x() - origin_pack.x(); double dist_corner_x = ibb.minCorner().x() - origin_pack.x();
if (dist_corner_y < 0 || dist_corner_x<0) // occupy as few rows as possible if we have rotations
return LARGE_COST_TO_REJECT; double bindist = double(ibb.maxCorner().y() - ibb.minCorner().y());
double bindist = norm(dist_corner_y + 1 * dist_corner_x if (dist_corner_x >= 0 && dist_corner_y >= 0)
+ 1 * double(ibb.maxCorner().y() - ibb.minCorner().y())); // occupy as few rows as possible bindist += dist_corner_y + 1 * dist_corner_x;
else {
if (dist_corner_x < 0) bindist += 10 * (-dist_corner_x);
if (dist_corner_y < 0) bindist += 10 * (-dist_corner_y);
}
bindist = norm(bindist);
return bindist; return bindist;
} }
@ -513,13 +519,15 @@ public:
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) { m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return; if (items.empty()) return;
auto bb = sl::boundingBox(m_bin); auto binbb = sl::boundingBox(m_bin);
// BBS: excluded region (virtual object but not wipe tower) should not affect final alignment // 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; }); 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) if (!all_is_excluded_region)
cfg.alignment = PConfig::Alignment::DONT_ALIGN; cfg.alignment = PConfig::Alignment::DONT_ALIGN;
else
cfg.alignment = PConfig::Alignment::CENTER;
auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? bb.minCorner() : bb.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 // if we have wipe tower, items should be arranged around wipe tower
for (Item itm : items) { for (Item itm : items) {
if (itm.is_wipe_tower) { if (itm.is_wipe_tower) {
@ -528,12 +536,16 @@ public:
} }
} }
cfg.object_function = [this, bb, starting_point](const Item& item, const ItemGroup& packed_items) { cfg.object_function = [this, binbb, starting_point](const Item& item, const ItemGroup& packed_items) {
bool packed_are_excluded_region = std::all_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_virt_object && !itm.is_wipe_tower; }); // 在我们的摆盘中,没有天然的固定对象。固定对象只有:屏蔽区域、挤出补偿区域、料塔。
if(packed_are_excluded_region) // 对于屏蔽区域,摆入的对象仍然是可以向右上滑动的;
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb); // 对挤出料塔,摆入的对象不能滑动(必须围绕料塔)
else bool pack_around_wipe_tower = std::any_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_wipe_tower; });
return fixed_overfit(objfunc(item, starting_point), bb); if(pack_around_wipe_tower)
return fixed_overfit(objfunc(item, starting_point), binbb);
else {
return fixed_overfit_topright_sliding(objfunc(item, starting_point), binbb);
}
}; };
}; };
@ -611,16 +623,18 @@ template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box
double score = std::get<0>(result); double score = std::get<0>(result);
auto& fullbb = std::get<1>(result); auto& fullbb = std::get<1>(result);
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT) //if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
{ //{
if (!sl::isInside(fullbb, m_bin)) // if (!sl::isInside(fullbb, m_bin))
score += LARGE_COST_TO_REJECT; // score += LARGE_COST_TO_REJECT;
} //}
else //else
{ {
double miss = Placer::overfit(fullbb, m_bin); double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0 ? miss : 0; miss = miss > 0 ? miss : 0;
score += miss * miss; score += miss * miss;
if (score > LARGE_COST_TO_REJECT)
score = 1.5 * LARGE_COST_TO_REJECT;
} }
return score; return score;

View File

@ -529,12 +529,9 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
auto inter_min = std::max(ly1, ry1); auto inter_min = std::max(ly1, ry1);
auto inter_max = std::min(ly2, ry2); auto inter_max = std::min(ly2, ry2);
auto inter_y = inter_max - inter_min; auto inter_y = inter_max - inter_min;
inter_min = std::max(lx1, rx1);
inter_max = std::min(lx2, rx2);
auto inter_x = inter_max - inter_min;
// 如果y方向的重合超过轮廓的膨胀量说明两个物体在一行应该先打左边的物体即先比较二者的x坐标。 // 如果y方向的重合超过轮廓的膨胀量说明两个物体在一行应该先打左边的物体即先比较二者的x坐标。
if (inter_y > scale_(1)) { if (inter_y > scale_(0.5 * print.config().extruder_clearance_radius.value)) {
if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) { if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) {
if (lx1 > rx1) { if (lx1 > rx1) {
left_right_pair.insert({j, i}); left_right_pair.insert({j, i});
@ -555,7 +552,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
} }
else if (l.height > hc2 && l.height > r.height && l.arrange_score<r.arrange_score) { else if (l.height > hc2 && l.height > r.height && l.arrange_score<r.arrange_score) {
// 如果当前物体的高度超过滑杆且比r高就给它加一点代价尽量让高的物体后打只有物体高度超过滑杆时才有必要按高度来 // 如果当前物体的高度超过滑杆且比r高就给它加一点代价尽量让高的物体后打只有物体高度超过滑杆时才有必要按高度来
l.arrange_score = std::max(l.arrange_score, r.arrange_score + bed_width/2); if (l.arrange_score < r.arrange_score)
l.arrange_score = r.arrange_score + 10;
BOOST_LOG_TRIVIAL(debug) << "height>hc2, print_instance " << inst.print_instance->model_instance->get_object()->name BOOST_LOG_TRIVIAL(debug) << "height>hc2, print_instance " << inst.print_instance->model_instance->get_object()->name
<< ", right=" << r.print_instance->model_instance->get_object()->name << ", l.score: " << l.arrange_score << ", right=" << r.print_instance->model_instance->get_object()->name << ", l.score: " << l.arrange_score
<< ", r.score: " << r.arrange_score; << ", r.score: " << r.arrange_score;
@ -563,7 +561,6 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
} }
} }
} }
BOOST_LOG_TRIVIAL(debug) << "bed width: " << bed_width << ", unsafe_dist:" << unsafe_dist;
// 多做几次代价传播,因为前一次有些值没有更新。 // 多做几次代价传播,因为前一次有些值没有更新。
// TODO 更好的办法是建立一颗树,一步到位。不过我暂时没精力搞,先就这样吧 // TODO 更好的办法是建立一颗树,一步到位。不过我暂时没精力搞,先就这样吧
for (int k=0;k<5;k++) for (int k=0;k<5;k++)
@ -571,14 +568,15 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
auto &l = print_instance_with_bounding_box[p(0)]; auto &l = print_instance_with_bounding_box[p(0)];
auto &r = print_instance_with_bounding_box[p(1)]; auto &r = print_instance_with_bounding_box[p(1)];
if(r.arrange_score<l.arrange_score) if(r.arrange_score<l.arrange_score)
r.arrange_score = l.arrange_score + bed_width/2; r.arrange_score = l.arrange_score + 10;
} }
BOOST_LOG_TRIVIAL(debug) << "bed width: " << unscale_(bed_width) << ", unsafe_dist:" << unscale_(unsafe_dist) << ", height_to_lid: " << unscale_(hc1) << ", height_to_rod:" << unscale_(hc2) << ", final dependency:";
for (auto p : left_right_pair) { for (auto p : left_right_pair) {
auto &l = print_instance_with_bounding_box[p(0)]; auto &l = print_instance_with_bounding_box[p(0)];
auto &r = print_instance_with_bounding_box[p(1)]; auto &r = print_instance_with_bounding_box[p(1)];
BOOST_LOG_TRIVIAL(debug) << "print_instance " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")" BOOST_LOG_TRIVIAL(debug) << "print_instance " << I18N::translate(l.print_instance->model_instance->get_object()->name) << "(" << l.arrange_score << ")"
<< " -> " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")"; << " -> " << I18N::translate(r.print_instance->model_instance->get_object()->name) << "(" << r.arrange_score << ")";
} }
// sort the print instance // sort the print instance
std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(), std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(),

View File

@ -75,6 +75,7 @@ static constexpr double BRIDGE_INFILL_MARGIN = 1;
//FIXME Better to use an inline function with an explicit return type. //FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR) #define scale_(val) ((val) / SCALING_FACTOR)
#define unscale_(val) ((val) * SCALING_FACTOR)
//BBS: BBS only support relative E and can't been changed by user at the moment. because //BBS: BBS only support relative E and can't been changed by user at the moment. because
//BBS need to support skip object when printing. //BBS need to support skip object when printing.

View File

@ -241,6 +241,10 @@ void ArrangeJob::prepare_wipe_tower()
{ {
bool need_wipe_tower = false; bool need_wipe_tower = false;
// if wipe tower is explicitly disabled, no need to estimate
auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
if (!print.config().enable_prime_tower) return;
// estimate if we need wipe tower for all plates: // estimate if we need wipe tower for all plates:
// if multile extruders have same bed temp, we need wipe tower // if multile extruders have same bed temp, we need wipe tower
if (!params.is_seq_print) { if (!params.is_seq_print) {
@ -531,10 +535,13 @@ void ArrangeJob::process()
std::for_each(m_selected.begin(), m_selected.end(), [&](auto& ap) {ap.inflation = params.min_obj_distance / 2; }); std::for_each(m_selected.begin(), m_selected.end(), [&](auto& ap) {ap.inflation = params.min_obj_distance / 2; });
// For occulusion regions, inflation should be larger to prevent genrating brim on them. // 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. // However, extrusion cali regions are exceptional, since we can allow brim overlaps them.
// 屏蔽区域只需要膨胀brim宽度防止brim长过去挤出标定区域不需要膨胀brim可以长过去。
// 以前我们认为还需要膨胀clearance_radius/2这其实是不需要的因为这些区域并不会真的摆放物体
// 其他物体的膨胀轮廓是可以跟它们重叠的。
std::for_each(m_unselected.begin(), m_unselected.end(), [&](auto &ap) { std::for_each(m_unselected.begin(), m_unselected.end(), [&](auto &ap) {
ap.inflation = !ap.is_virt_object ? ap.inflation = !ap.is_virt_object ?
params.min_obj_distance / 2 : params.min_obj_distance / 2 :
(ap.is_extrusion_cali_object ? scaled(params.cleareance_radius / 2) : scaled(params.brim_skirt_distance + params.cleareance_radius / 2)); (ap.is_extrusion_cali_object ? 0 : scaled(params.brim_skirt_distance));
}); });

View File

@ -3473,8 +3473,8 @@ bool PartPlateList::preprocess_nonprefered_areas(arrangement::ArrangePolygons& r
bool added = false; bool added = false;
std::vector<BoundingBoxf> nonprefered_regions; std::vector<BoundingBoxf> nonprefered_regions;
nonprefered_regions.emplace_back(Vec2d{ 45,15 }, Vec2d{ 225,25 }); // extrusion calibration region nonprefered_regions.emplace_back(Vec2d{ 45,0 }, Vec2d{ 225,25 }); // extrusion calibration region
nonprefered_regions.emplace_back(Vec2d{ 25,10 }, Vec2d{ 50,60 }); // hand-eye calibration region nonprefered_regions.emplace_back(Vec2d{ 25,0 }, Vec2d{ 50,60 }); // hand-eye calibration region
//has exclude areas //has exclude areas
PartPlate* plate = m_plate_list[0]; PartPlate* plate = m_plate_list[0];