Merge branch 'tm_arrange_skirt_brim_SPE-1465'
This commit is contained in:
commit
048e05af7e
9 changed files with 228 additions and 15 deletions
|
@ -70,6 +70,7 @@ class _Item {
|
|||
|
||||
int binid_{BIN_ID_UNSET}, priority_{0};
|
||||
bool fixed_{false};
|
||||
std::function<void(_Item&)> on_packed_;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -205,6 +206,23 @@ public:
|
|||
sl::vertex(sh_, idx) = v;
|
||||
}
|
||||
|
||||
void setShape(RawShape rsh)
|
||||
{
|
||||
sh_ = std::move(rsh);
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
void setOnPackedFn(std::function<void(_Item&)> onpackedfn)
|
||||
{
|
||||
on_packed_ = onpackedfn;
|
||||
}
|
||||
|
||||
void onPacked()
|
||||
{
|
||||
if (on_packed_)
|
||||
on_packed_(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate the shape area.
|
||||
*
|
||||
|
|
|
@ -901,6 +901,7 @@ public:
|
|||
|
||||
if(can_pack) {
|
||||
ret = PackResult(item);
|
||||
item.onPacked();
|
||||
merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
|
||||
} else {
|
||||
ret = PackResult(best_overfit);
|
||||
|
|
|
@ -583,8 +583,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
|
|||
outp.emplace_back(std::move(p));
|
||||
outp.back().rotation(rotation);
|
||||
outp.back().translation({offs.x(), offs.y()});
|
||||
outp.back().inflate(arrpoly.inflation);
|
||||
outp.back().binId(arrpoly.bed_idx);
|
||||
outp.back().priority(arrpoly.priority);
|
||||
outp.back().setOnPackedFn([&arrpoly](Item &itm){
|
||||
itm.inflate(-arrpoly.inflation);
|
||||
});
|
||||
}
|
||||
|
||||
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
|
||||
|
|
|
@ -71,7 +71,7 @@ static const constexpr int UNARRANGED = -1;
|
|||
/// polygon belongs: UNARRANGED means no place for the polygon
|
||||
/// (also the initial state before arrange), 0..N means the index of the bed.
|
||||
/// Zero is the physical bed, larger than zero means a virtual bed.
|
||||
struct ArrangePolygon {
|
||||
struct ArrangePolygon {
|
||||
ExPolygon poly; /// The 2D silhouette to be arranged
|
||||
Vec2crd translation{0, 0}; /// The translation of the poly
|
||||
double rotation{0.0}; /// The rotation of the poly in radians
|
||||
|
|
|
@ -566,6 +566,11 @@ public:
|
|||
SpanOfConstPtrs<PrintObject> objects() const { return SpanOfConstPtrs<PrintObject>(const_cast<const PrintObject* const* const>(m_objects.data()), m_objects.size()); }
|
||||
PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
|
||||
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
|
||||
const PrintObject* get_print_object_by_model_object_id(ObjectID object_id) const {
|
||||
auto it = std::find_if(m_objects.begin(), m_objects.end(),
|
||||
[object_id](const PrintObject* obj) { return obj->model_object()->id() == object_id; });
|
||||
return (it == m_objects.end()) ? nullptr : *it;
|
||||
}
|
||||
// PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
|
||||
// in the notification center.
|
||||
const PrintObject* get_object(ObjectID object_id) const {
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
|
@ -11,6 +14,7 @@
|
|||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
|
||||
|
||||
#include "libnest2d/common.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
@ -93,28 +97,28 @@ void ArrangeJob::prepare_all() {
|
|||
|
||||
void ArrangeJob::prepare_selected() {
|
||||
clear_input();
|
||||
|
||||
|
||||
Model &model = m_plater->model();
|
||||
double stride = bed_stride(m_plater);
|
||||
|
||||
|
||||
std::vector<const Selection::InstanceIdxsList *>
|
||||
obj_sel(model.objects.size(), nullptr);
|
||||
|
||||
for (auto &s : m_plater->get_selection().get_content())
|
||||
if (s.first < int(obj_sel.size()))
|
||||
obj_sel[size_t(s.first)] = &s.second;
|
||||
|
||||
|
||||
// Go through the objects and check if inside the selection
|
||||
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
|
||||
const Selection::InstanceIdxsList * instlist = obj_sel[oidx];
|
||||
ModelObject *mo = model.objects[oidx];
|
||||
|
||||
|
||||
std::vector<bool> inst_sel(mo->instances.size(), false);
|
||||
|
||||
|
||||
if (instlist)
|
||||
for (auto inst_id : *instlist)
|
||||
inst_sel[size_t(inst_id)] = true;
|
||||
|
||||
|
||||
for (size_t i = 0; i < inst_sel.size(); ++i) {
|
||||
ModelInstance * mi = mo->instances[i];
|
||||
ArrangePolygon &&ap = get_arrange_poly_(mi);
|
||||
|
@ -123,11 +127,11 @@ void ArrangeJob::prepare_selected() {
|
|||
(inst_sel[i] ? m_selected :
|
||||
m_unselected) :
|
||||
m_unprintable;
|
||||
|
||||
|
||||
cont.emplace_back(std::move(ap));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (auto wti = get_wipe_tower(*m_plater)) {
|
||||
ArrangePolygon &&ap = get_arrange_poly(wti, m_plater);
|
||||
|
||||
|
@ -135,14 +139,120 @@ void ArrangeJob::prepare_selected() {
|
|||
m_unselected;
|
||||
cont.emplace_back(std::move(ap));
|
||||
}
|
||||
|
||||
|
||||
// If the selection was empty arrange everything
|
||||
if (m_selected.empty()) m_selected.swap(m_unselected);
|
||||
|
||||
if (m_selected.empty())
|
||||
m_selected.swap(m_unselected);
|
||||
|
||||
// The strides have to be removed from the fixed items. For the
|
||||
// arrangeable (selected) items bed_idx is ignored and the
|
||||
// translation is irrelevant.
|
||||
for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride;
|
||||
for (auto &p : m_unselected)
|
||||
p.translation(X) -= p.bed_idx * stride;
|
||||
}
|
||||
|
||||
static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret,
|
||||
const SLAPrintObject &po,
|
||||
const ModelInstance &inst)
|
||||
{
|
||||
// The 1.1 multiplier is a safety gap, as the offset might be bigger
|
||||
// in sharp edges of a polygon, depending on clipper's offset algorithm
|
||||
coord_t pad_infl = 0;
|
||||
{
|
||||
double infl = po.config().pad_enable.getBool() * (
|
||||
po.config().pad_brim_size.getFloat() +
|
||||
po.config().pad_around_object.getBool() *
|
||||
po.config().pad_object_gap.getFloat() );
|
||||
|
||||
pad_infl = scaled(1.1 * infl);
|
||||
}
|
||||
|
||||
auto laststep = po.last_completed_step();
|
||||
|
||||
if (laststep < slaposCount && laststep > slaposSupportTree) {
|
||||
auto omesh = po.get_mesh_to_print();
|
||||
auto &smesh = po.support_mesh();
|
||||
|
||||
Vec3d rotation = inst.get_rotation();
|
||||
rotation.z() = 0.;
|
||||
Transform3f trafo_instance =
|
||||
Geometry::assemble_transform(inst.get_offset().z() * Vec3d::UnitZ(),
|
||||
rotation,
|
||||
inst.get_scaling_factor(),
|
||||
inst.get_mirror()).cast<float>();
|
||||
|
||||
trafo_instance = trafo_instance * po.trafo().cast<float>().inverse();
|
||||
|
||||
auto polys = reserve_vector<Polygon>(3);
|
||||
auto zlvl = -po.get_elevation();
|
||||
|
||||
if (omesh) {
|
||||
polys.emplace_back(its_convex_hull_2d_above(*omesh, trafo_instance, zlvl));
|
||||
ret.poly.contour = polys.back();
|
||||
ret.poly.holes = {};
|
||||
}
|
||||
|
||||
polys.emplace_back(its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl));
|
||||
ret.poly.contour = Geometry::convex_hull(polys);
|
||||
ret.poly.holes = {};
|
||||
}
|
||||
|
||||
ret.inflation = pad_infl;
|
||||
}
|
||||
|
||||
static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst)
|
||||
{
|
||||
const BrimType brim_type = po.config().brim_type.value;
|
||||
const float brim_separation = po.config().brim_separation.getFloat();
|
||||
const float brim_width = po.config().brim_width.getFloat();
|
||||
const bool has_outer_brim = brim_type == BrimType::btOuterOnly ||
|
||||
brim_type == BrimType::btOuterAndInner;
|
||||
|
||||
// How wide is the brim? (in scaled units)
|
||||
return has_outer_brim ? scaled(brim_width + brim_separation) : 0;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
Polygon support_layers_chull (Points &pts, It from_lyr, It to_lyr) {
|
||||
|
||||
size_t cap = 0;
|
||||
for (auto it = from_lyr; it != to_lyr; ++it) {
|
||||
for (const ExPolygon &expoly : (*it)->support_islands)
|
||||
cap += expoly.contour.points.size();
|
||||
}
|
||||
|
||||
pts.reserve(pts.size() + cap);
|
||||
|
||||
for (auto it = from_lyr; it != to_lyr; ++it) {
|
||||
for (const ExPolygon &expoly : (*it)->support_islands)
|
||||
std::copy(expoly.contour.begin(), expoly.contour.end(),
|
||||
std::back_inserter(pts));
|
||||
}
|
||||
|
||||
Polygon ret = Geometry::convex_hull(pts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_arrangepoly_fffprint(arrangement::ArrangePolygon &ret,
|
||||
const PrintObject &po,
|
||||
const ModelInstance &inst)
|
||||
{
|
||||
auto laststep = po.last_completed_step();
|
||||
|
||||
coord_t infl = brim_offset(po, inst);
|
||||
|
||||
if (laststep < posCount && laststep > posSupportMaterial) {
|
||||
Points pts = std::move(ret.poly.contour.points);
|
||||
Polygon poly = support_layers_chull(pts,
|
||||
po.support_layers().begin(),
|
||||
po.support_layers().end());
|
||||
|
||||
ret.poly.contour = std::move(poly);
|
||||
ret.poly.holes = {};
|
||||
}
|
||||
|
||||
ret.inflation = infl;
|
||||
}
|
||||
|
||||
arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi)
|
||||
|
@ -159,9 +269,38 @@ arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi)
|
|||
return ap;
|
||||
}
|
||||
|
||||
coord_t get_skirt_offset(const Plater* plater) {
|
||||
float skirt_inset = 0.f;
|
||||
// Try to subtract the skirt from the bed shape so we don't arrange outside of it.
|
||||
if (plater->printer_technology() == ptFFF && plater->fff_print().has_skirt()) {
|
||||
const auto& print = plater->fff_print();
|
||||
skirt_inset = print.config().skirts.value * print.skirt_flow().width() +
|
||||
print.config().skirt_distance.value;
|
||||
}
|
||||
|
||||
return scaled(skirt_inset);
|
||||
}
|
||||
|
||||
void ArrangeJob::prepare()
|
||||
{
|
||||
wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all();
|
||||
|
||||
coord_t min_offset = 0;
|
||||
for (auto &ap : m_selected) {
|
||||
min_offset = std::max(ap.inflation, min_offset);
|
||||
}
|
||||
|
||||
if (m_plater->printer_technology() == ptSLA) {
|
||||
// Apply the max offset for all the objects
|
||||
for (auto &ap : m_selected) {
|
||||
ap.inflation = min_offset;
|
||||
}
|
||||
} else { // it's fff, brims only need to be minded from bed edges
|
||||
for (auto &ap : m_selected) {
|
||||
ap.inflation = 0;
|
||||
}
|
||||
m_min_bed_inset = min_offset;
|
||||
}
|
||||
}
|
||||
|
||||
void ArrangeJob::process(Ctl &ctl)
|
||||
|
@ -174,6 +313,8 @@ void ArrangeJob::process(Ctl &ctl)
|
|||
prepare();
|
||||
params = get_arrange_params(m_plater);
|
||||
get_bed_shape(*m_plater->config(), bed);
|
||||
coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset;
|
||||
params.min_bed_distance = std::max(params.min_bed_distance, min_inset);
|
||||
}).wait();
|
||||
|
||||
auto count = unsigned(m_selected.size() + m_unprintable.size());
|
||||
|
@ -286,7 +427,26 @@ template<>
|
|||
arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
|
||||
const Plater * plater)
|
||||
{
|
||||
return get_arrange_poly(PtrWrapper{inst}, plater);
|
||||
auto ap = get_arrange_poly(PtrWrapper{inst}, plater);
|
||||
|
||||
auto obj_id = inst->get_object()->id();
|
||||
if (plater->printer_technology() == ptSLA) {
|
||||
const SLAPrintObject *po =
|
||||
plater->sla_print().get_print_object_by_model_object_id(obj_id);
|
||||
|
||||
if (po) {
|
||||
update_arrangepoly_slaprint(ap, *po, *inst);
|
||||
}
|
||||
} else {
|
||||
const PrintObject *po =
|
||||
plater->fff_print().get_print_object_by_model_object_id(obj_id);
|
||||
|
||||
if (po) {
|
||||
update_arrangepoly_fffprint(ap, *po, *inst);
|
||||
}
|
||||
}
|
||||
|
||||
return ap;
|
||||
}
|
||||
|
||||
arrangement::ArrangeParams get_arrange_params(Plater *p)
|
||||
|
|
|
@ -21,6 +21,8 @@ class ArrangeJob : public Job
|
|||
|
||||
ArrangePolygons m_selected, m_unselected, m_unprintable;
|
||||
std::vector<ModelInstance*> m_unarranged;
|
||||
coord_t m_min_bed_inset = 0.;
|
||||
|
||||
Plater *m_plater;
|
||||
|
||||
// clear m_selected and m_unselected, reserve space for next usage
|
||||
|
@ -102,6 +104,8 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
|
|||
|
||||
arrangement::ArrangeParams get_arrange_params(Plater *p);
|
||||
|
||||
coord_t get_skirt_offset(const Plater* plater);
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // ARRANGEJOB_HPP
|
||||
|
|
|
@ -18,6 +18,7 @@ void FillBedJob::prepare()
|
|||
m_selected.clear();
|
||||
m_unselected.clear();
|
||||
m_bedpts.clear();
|
||||
m_min_bed_inset = 0.;
|
||||
|
||||
m_object_idx = m_plater->get_selected_object_idx();
|
||||
if (m_object_idx == -1)
|
||||
|
@ -29,7 +30,7 @@ void FillBedJob::prepare()
|
|||
m_selected.reserve(model_object->instances.size());
|
||||
for (ModelInstance *inst : model_object->instances)
|
||||
if (inst->printable) {
|
||||
ArrangePolygon ap = get_arrange_poly(PtrWrapper{inst}, m_plater);
|
||||
ArrangePolygon ap = get_arrange_poly(inst, m_plater);
|
||||
// Existing objects need to be included in the result. Only
|
||||
// the needed amount of object will be added, no more.
|
||||
++ap.priority;
|
||||
|
@ -101,6 +102,23 @@ void FillBedJob::prepare()
|
|||
for (auto &p : m_unselected)
|
||||
if (p.bed_idx > 0)
|
||||
p.translation(X) -= p.bed_idx * stride;
|
||||
|
||||
coord_t min_offset = 0;
|
||||
for (auto &ap : m_selected) {
|
||||
min_offset = std::max(ap.inflation, min_offset);
|
||||
}
|
||||
|
||||
if (m_plater->printer_technology() == ptSLA) {
|
||||
// Apply the max offset for all the objects
|
||||
for (auto &ap : m_selected) {
|
||||
ap.inflation = min_offset;
|
||||
}
|
||||
} else { // it's fff, brims only need to be minded from bed edges
|
||||
for (auto &ap : m_selected) {
|
||||
ap.inflation = 0;
|
||||
}
|
||||
m_min_bed_inset = min_offset;
|
||||
}
|
||||
}
|
||||
|
||||
void FillBedJob::process(Ctl &ctl)
|
||||
|
@ -110,6 +128,8 @@ void FillBedJob::process(Ctl &ctl)
|
|||
ctl.call_on_main_thread([this, ¶ms] {
|
||||
prepare();
|
||||
params = get_arrange_params(m_plater);
|
||||
coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset;
|
||||
params.min_bed_distance = std::max(params.min_bed_distance, min_inset);
|
||||
}).wait();
|
||||
ctl.update_status(0, statustxt);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ class FillBedJob : public Job
|
|||
|
||||
ArrangePolygons m_selected;
|
||||
ArrangePolygons m_unselected;
|
||||
coord_t m_min_bed_inset = 0.;
|
||||
|
||||
Points m_bedpts;
|
||||
|
||||
|
|
Loading…
Reference in a new issue