299 lines
11 KiB
C++
299 lines
11 KiB
C++
|
#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"
|
||
|
|
||
|
#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();
|
||
|
|
||
|
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;
|
||
|
|
||
|
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);
|
||
|
|
||
|
ArrangePolygon ap = get_arrange_poly(mo->instances[inst_idx]);
|
||
|
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
|
||
|
plate_list.preprocess_exclude_areas(m_unselected);
|
||
|
|
||
|
m_bedpts = get_bed_shape(*m_plater->config());
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
if (auto wt = get_wipe_tower_arrangepoly(*m_plater))
|
||
|
m_unselected.emplace_back(std::move(*wt));*/
|
||
|
|
||
|
double sc = scaled<double>(1.) * scaled(1.);
|
||
|
|
||
|
ExPolygon poly = m_selected.front().poly;
|
||
|
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];
|
||
|
ArrangePolygon template_ap = get_arrange_poly(mi);
|
||
|
|
||
|
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];
|
||
|
ModelInstance *inst = mo->add_instance(*mi);
|
||
|
inst->apply_arrange_result(p.translation.cast<double>(), p.rotation);
|
||
|
};
|
||
|
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;
|
||
|
|
||
|
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);
|
||
|
|
||
|
bool do_stop = false;
|
||
|
params.stopcondition = [this, &do_stop]() {
|
||
|
return was_canceled() || do_stop;
|
||
|
};
|
||
|
|
||
|
params.progressind = [this](unsigned st,std::string str="") {
|
||
|
// if (st > 0)
|
||
|
// update_status(int(m_status_range - st), _L("Filling bed " + str));
|
||
|
};
|
||
|
|
||
|
params.on_packed = [&do_stop] (const ArrangePolygon &ap) {
|
||
|
do_stop = ap.bed_idx > 0 && ap.priority == 0;
|
||
|
};
|
||
|
|
||
|
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.")));
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
});
|
||
|
|
||
|
if (added_cnt > 0) {
|
||
|
//BBS: adjust the selected instances
|
||
|
for (ArrangePolygon& ap : m_selected) {
|
||
|
if (ap.bed_idx != 0) {
|
||
|
if (ap.itemid == -1)
|
||
|
continue;
|
||
|
ap.bed_idx = plate_list.get_plate_count();
|
||
|
}
|
||
|
else
|
||
|
ap.bed_idx = cur_plate;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
for (size_t inst_idx = 0; inst_idx < model_object->instances.size(); ++inst_idx)
|
||
|
{
|
||
|
plate_list.notify_instance_update(m_object_idx, inst_idx);
|
||
|
}
|
||
|
|
||
|
/*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();
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
}} // namespace Slic3r::GUI
|