Add ModelArrange.hpp as extension to Model.hpp, use it for duplicating
Refactored Arrange interface: remove the union based BedShapeHint, replace it with proper function overloads WARN: this commit is only intermediate, it does not compile.
This commit is contained in:
parent
44ca0a6c3d
commit
1bffc2b99b
@ -34,6 +34,7 @@
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
@ -53,12 +54,6 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
PrinterTechnology get_printer_technology(const DynamicConfig &config)
|
||||
{
|
||||
const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
|
||||
return (opt == nullptr) ? ptUnknown : opt->value;
|
||||
}
|
||||
|
||||
int CLI::run(int argc, char **argv)
|
||||
{
|
||||
// Switch boost::filesystem to utf8.
|
||||
@ -87,12 +82,14 @@ int CLI::run(int argc, char **argv)
|
||||
m_extra_config.apply(m_config, true);
|
||||
m_extra_config.normalize();
|
||||
|
||||
PrinterTechnology printer_technology = Slic3r::printer_technology(m_config);
|
||||
|
||||
bool start_gui = m_actions.empty() &&
|
||||
// cutting transformations are setting an "export" action.
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
|
||||
PrinterTechnology printer_technology = get_printer_technology(m_extra_config);
|
||||
|
||||
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
|
||||
|
||||
// load config files supplied via --load
|
||||
@ -113,7 +110,7 @@ int CLI::run(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
config.normalize();
|
||||
PrinterTechnology other_printer_technology = get_printer_technology(config);
|
||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
} else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
|
||||
@ -134,7 +131,7 @@ int CLI::run(int argc, char **argv)
|
||||
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
||||
DynamicPrintConfig config;
|
||||
model = Model::read_from_file(file, &config, true);
|
||||
PrinterTechnology other_printer_technology = get_printer_technology(config);
|
||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
} else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
|
||||
@ -161,9 +158,6 @@ int CLI::run(int argc, char **argv)
|
||||
// Normalizing after importing the 3MFs / AMFs
|
||||
m_print_config.normalize();
|
||||
|
||||
if (printer_technology == ptUnknown)
|
||||
printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA;
|
||||
|
||||
// Initialize full print configs for both the FFF and SLA technologies.
|
||||
FullPrintConfig fff_print_config;
|
||||
SLAFullPrintConfig sla_print_config;
|
||||
@ -187,10 +181,18 @@ int CLI::run(int argc, char **argv)
|
||||
m_print_config.apply(sla_print_config, true);
|
||||
}
|
||||
|
||||
double min_obj_dist = min_object_distance(m_print_config);
|
||||
std::string validity = m_print_config.validate();
|
||||
if (!validity.empty()) {
|
||||
boost::nowide::cerr << "error: " << validity << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Loop through transform options.
|
||||
bool user_center_specified = false;
|
||||
Points bed = get_bed_shape(m_print_config);
|
||||
ArrangeParams arrange_cfg;
|
||||
arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config));
|
||||
|
||||
for (auto const &opt_key : m_transforms) {
|
||||
if (opt_key == "merge") {
|
||||
Model m;
|
||||
@ -200,29 +202,33 @@ int CLI::run(int argc, char **argv)
|
||||
// Rearrange instances unless --dont-arrange is supplied
|
||||
if (! m_config.opt_bool("dont_arrange")) {
|
||||
m.add_default_instances();
|
||||
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
|
||||
m.arrange_objects(
|
||||
min_obj_dist,
|
||||
// If we are going to use the merged model for printing, honor
|
||||
// the configured print bed for arranging, otherwise do it freely.
|
||||
this->has_print_action() ? &bb : nullptr
|
||||
);
|
||||
if (this->has_print_action())
|
||||
arrange_objects(m, bed, arrange_cfg);
|
||||
else
|
||||
arrange_objects(m, InfiniteBed{}, arrange_cfg);
|
||||
}
|
||||
m_models.clear();
|
||||
m_models.emplace_back(std::move(m));
|
||||
} else if (opt_key == "duplicate") {
|
||||
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
|
||||
for (auto &model : m_models) {
|
||||
const bool all_objects_have_instances = std::none_of(
|
||||
model.objects.begin(), model.objects.end(),
|
||||
[](ModelObject* o){ return o->instances.empty(); }
|
||||
);
|
||||
if (all_objects_have_instances) {
|
||||
// if all input objects have defined position(s) apply duplication to the whole model
|
||||
model.duplicate(m_config.opt_int("duplicate"), min_obj_dist, &bb);
|
||||
} else {
|
||||
model.add_default_instances();
|
||||
model.duplicate_objects(m_config.opt_int("duplicate"), min_obj_dist, &bb);
|
||||
|
||||
int dups = m_config.opt_int("duplicate");
|
||||
if (!all_objects_have_instances) model.add_default_instances();
|
||||
|
||||
try {
|
||||
if (dups > 1) {
|
||||
// if all input objects have defined position(s) apply duplication to the whole model
|
||||
duplicate(model, size_t(dups), bed, arrange_cfg);
|
||||
} else {
|
||||
arrange_objects(model, bed, arrange_cfg);
|
||||
}
|
||||
} catch (std::exception &ex) {
|
||||
boost::nowide::cerr << "error: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if (opt_key == "duplicate_grid") {
|
||||
@ -426,11 +432,11 @@ int CLI::run(int argc, char **argv)
|
||||
|
||||
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
|
||||
if (! m_config.opt_bool("dont_arrange")) {
|
||||
//FIXME make the min_object_distance configurable.
|
||||
model.arrange_objects(min_obj_dist);
|
||||
model.center_instances_around_point((! user_center_specified && m_print_config.has("bed_shape")) ?
|
||||
BoundingBoxf(m_print_config.opt<ConfigOptionPoints>("bed_shape")->values).center() :
|
||||
m_config.option<ConfigOptionPoint>("center")->value);
|
||||
if (user_center_specified) {
|
||||
Vec2d c = m_config.option<ConfigOptionPoint>("center")->value;
|
||||
arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg);
|
||||
} else
|
||||
arrange_objects(model, bed, arrange_cfg);
|
||||
}
|
||||
if (printer_technology == ptFFF) {
|
||||
for (auto* mo : model.objects)
|
||||
@ -613,6 +619,8 @@ bool CLI::setup(int argc, char **argv)
|
||||
set_logging_level(opt_loglevel->value);
|
||||
}
|
||||
|
||||
std::string validity = m_config.validate();
|
||||
|
||||
// Initialize with defaults.
|
||||
for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
|
||||
for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
|
||||
@ -620,6 +628,11 @@ bool CLI::setup(int argc, char **argv)
|
||||
|
||||
set_data_dir(m_config.opt_string("datadir"));
|
||||
|
||||
if (!validity.empty()) {
|
||||
boost::nowide::cerr << "error: " << validity << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "Arrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
//#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
#include <libnest2d/backends/clipper/geometries.hpp>
|
||||
#include <libnest2d/optimizers/nlopt/subplex.hpp>
|
||||
@ -83,7 +82,7 @@ const double BIG_ITEM_TRESHOLD = 0.02;
|
||||
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||
// Slic3r.
|
||||
template<class PConf>
|
||||
void fillConfig(PConf& pcfg) {
|
||||
void fill_config(PConf& pcfg) {
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
@ -105,7 +104,7 @@ void fillConfig(PConf& pcfg) {
|
||||
|
||||
// Apply penalty to object function result. This is used only when alignment
|
||||
// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
|
||||
double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
|
||||
static double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
|
||||
{
|
||||
double score = std::get<0>(result);
|
||||
Box pilebb = std::get<1>(result);
|
||||
@ -312,7 +311,7 @@ public:
|
||||
, m_bin_area(sl::area(bin))
|
||||
, m_norm(std::sqrt(m_bin_area))
|
||||
{
|
||||
fillConfig(m_pconf);
|
||||
fill_config(m_pconf);
|
||||
|
||||
// Set up a callback that is called just before arranging starts
|
||||
// This functionality is provided by the Nester class (m_pack).
|
||||
@ -363,6 +362,9 @@ public:
|
||||
m_item_count = 0;
|
||||
}
|
||||
|
||||
PConfig& config() { return m_pconf; }
|
||||
const PConfig& config() const { return m_pconf; }
|
||||
|
||||
inline void preload(std::vector<Item>& fixeditems) {
|
||||
m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
|
||||
auto bb = sl::boundingBox(m_bin);
|
||||
@ -438,127 +440,6 @@ std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn()
|
||||
};
|
||||
}
|
||||
|
||||
inline Circle to_lnCircle(const CircleBed& circ) {
|
||||
return Circle({circ.center()(0), circ.center()(1)}, circ.radius());
|
||||
}
|
||||
|
||||
// Get the type of bed geometry from a simple vector of points.
|
||||
void BedShapeHint::reset(BedShapes type)
|
||||
{
|
||||
if (m_type != type) {
|
||||
if (m_type == bsIrregular)
|
||||
m_bed.polygon.Slic3r::Polyline::~Polyline();
|
||||
else if (type == bsIrregular)
|
||||
::new (&m_bed.polygon) Polyline();
|
||||
}
|
||||
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
BedShapeHint::BedShapeHint(const Polyline &bed) {
|
||||
auto x = [](const Point& p) { return p(X); };
|
||||
auto y = [](const Point& p) { return p(Y); };
|
||||
|
||||
auto width = [x](const BoundingBox& box) {
|
||||
return x(box.max) - x(box.min);
|
||||
};
|
||||
|
||||
auto height = [y](const BoundingBox& box) {
|
||||
return y(box.max) - y(box.min);
|
||||
};
|
||||
|
||||
auto area = [&width, &height](const BoundingBox& box) {
|
||||
double w = width(box);
|
||||
double h = height(box);
|
||||
return w * h;
|
||||
};
|
||||
|
||||
auto poly_area = [](Polyline p) {
|
||||
Polygon pp; pp.points.reserve(p.points.size() + 1);
|
||||
pp.points = std::move(p.points);
|
||||
pp.points.emplace_back(pp.points.front());
|
||||
return std::abs(pp.area());
|
||||
};
|
||||
|
||||
auto distance_to = [x, y](const Point& p1, const Point& p2) {
|
||||
double dx = x(p2) - x(p1);
|
||||
double dy = y(p2) - y(p1);
|
||||
return std::sqrt(dx*dx + dy*dy);
|
||||
};
|
||||
|
||||
auto bb = bed.bounding_box();
|
||||
|
||||
auto isCircle = [bb, distance_to](const Polyline& polygon) {
|
||||
auto center = bb.center();
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt: polygon.points)
|
||||
{
|
||||
double distance = distance_to(center, pt);
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
CircleBed ret(center, avg_dist);
|
||||
for(auto el : vertex_distances)
|
||||
{
|
||||
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
|
||||
ret = CircleBed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if( (1.0 - parea/area(bb)) < 1e-3 ) {
|
||||
m_type = BedShapes::bsBox;
|
||||
m_bed.box = bb;
|
||||
}
|
||||
else if(auto c = isCircle(bed)) {
|
||||
m_type = BedShapes::bsCircle;
|
||||
m_bed.circ = c;
|
||||
} else {
|
||||
assert(m_type != BedShapes::bsIrregular);
|
||||
m_type = BedShapes::bsIrregular;
|
||||
::new (&m_bed.polygon) Polyline(bed);
|
||||
}
|
||||
}
|
||||
|
||||
BedShapeHint &BedShapeHint::operator=(BedShapeHint &&cpy)
|
||||
{
|
||||
reset(cpy.m_type);
|
||||
|
||||
switch(m_type) {
|
||||
case bsBox: m_bed.box = std::move(cpy.m_bed.box); break;
|
||||
case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break;
|
||||
case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break;
|
||||
case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break;
|
||||
case bsUnknown: break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BedShapeHint &BedShapeHint::operator=(const BedShapeHint &cpy)
|
||||
{
|
||||
reset(cpy.m_type);
|
||||
|
||||
switch(m_type) {
|
||||
case bsBox: m_bed.box = cpy.m_bed.box; break;
|
||||
case bsCircle: m_bed.circ = cpy.m_bed.circ; break;
|
||||
case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break;
|
||||
case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break;
|
||||
case bsUnknown: break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
|
||||
{
|
||||
auto it = items.begin();
|
||||
@ -572,12 +453,12 @@ void _arrange(
|
||||
std::vector<Item> & shapes,
|
||||
std::vector<Item> & excludes,
|
||||
const BinT & bin,
|
||||
coord_t minobjd,
|
||||
const ArrangeParams & params,
|
||||
std::function<void(unsigned)> progressfn,
|
||||
std::function<bool()> stopfn)
|
||||
{
|
||||
// Integer ceiling the min distance from the bed perimeters
|
||||
coord_t md = minobjd;
|
||||
coord_t md = params.min_obj_distance;
|
||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||
|
||||
auto corrected_bin = bin;
|
||||
@ -585,7 +466,10 @@ void _arrange(
|
||||
|
||||
AutoArranger<BinT> arranger{corrected_bin, progressfn, stopfn};
|
||||
|
||||
auto infl = coord_t(std::ceil(minobjd / 2.0));
|
||||
arranger.config().accuracy = params.accuracy;
|
||||
arranger.config().parallel = params.parallel;
|
||||
|
||||
auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0));
|
||||
for (Item& itm : shapes) itm.inflate(infl);
|
||||
for (Item& itm : excludes) itm.inflate(infl);
|
||||
|
||||
@ -603,44 +487,106 @@ void _arrange(
|
||||
for (Item &itm : inp) itm.inflate(-infl);
|
||||
}
|
||||
|
||||
// The final client function for arrangement. A progress indicator and
|
||||
// a stop predicate can be also be passed to control the process.
|
||||
void arrange(ArrangePolygons & arrangables,
|
||||
const ArrangePolygons & excludes,
|
||||
coord_t min_obj_dist,
|
||||
const BedShapeHint & bedhint,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool()> stopcondition)
|
||||
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
|
||||
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
|
||||
inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); }
|
||||
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
|
||||
|
||||
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
|
||||
inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); }
|
||||
inline double area(const BoundingBox& box) { return double(width(box)) * height(box); }
|
||||
inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); }
|
||||
inline double distance_to(const Point& p1, const Point& p2)
|
||||
{
|
||||
double dx = p2.x() - p1.x();
|
||||
double dy = p2.y() - p1.y();
|
||||
return std::sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
static CircleBed to_circle(const Point ¢er, const Points& points) {
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
|
||||
for (auto pt : points)
|
||||
{
|
||||
double distance = distance_to(center, pt);
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
CircleBed ret(center, avg_dist);
|
||||
for(auto el : vertex_distances)
|
||||
{
|
||||
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
|
||||
ret = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Create Item from Arrangeable
|
||||
static void process_arrangeable(const ArrangePolygon &arrpoly,
|
||||
std::vector<Item> & outp)
|
||||
{
|
||||
Polygon p = arrpoly.poly.contour;
|
||||
const Vec2crd &offs = arrpoly.translation;
|
||||
double rotation = arrpoly.rotation;
|
||||
|
||||
if (p.is_counter_clockwise()) p.reverse();
|
||||
|
||||
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
||||
|
||||
if (!clpath.Contour.empty()) {
|
||||
auto firstp = clpath.Contour.front();
|
||||
clpath.Contour.emplace_back(firstp);
|
||||
}
|
||||
|
||||
outp.emplace_back(std::move(clpath));
|
||||
outp.back().rotation(rotation);
|
||||
outp.back().translation({offs.x(), offs.y()});
|
||||
outp.back().binId(arrpoly.bed_idx);
|
||||
outp.back().priority(arrpoly.priority);
|
||||
}
|
||||
|
||||
template<>
|
||||
void arrange(ArrangePolygons & items,
|
||||
const ArrangePolygons &excludes,
|
||||
const Points & bed,
|
||||
const ArrangeParams & params)
|
||||
{
|
||||
if (bed.empty())
|
||||
arrange(items, excludes, InfiniteBed{}, params);
|
||||
else if (bed.size() == 1)
|
||||
arrange(items, excludes, InfiniteBed{bed.front()}, params);
|
||||
else {
|
||||
auto bb = BoundingBox(bed);
|
||||
CircleBed circ = to_circle(bb.center(), bed);
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3)
|
||||
arrange(items, excludes, bb, params);
|
||||
else if (!std::isnan(circ.radius()))
|
||||
arrange(items, excludes, circ, params);
|
||||
else
|
||||
arrange(items, excludes, Polygon(bed), params);
|
||||
}
|
||||
}
|
||||
|
||||
template<class BedT>
|
||||
void arrange(ArrangePolygons & arrangables,
|
||||
const ArrangePolygons &excludes,
|
||||
const BedT & bed,
|
||||
const ArrangeParams & params)
|
||||
{
|
||||
namespace clppr = ClipperLib;
|
||||
|
||||
std::vector<Item> items, fixeditems;
|
||||
items.reserve(arrangables.size());
|
||||
|
||||
// Create Item from Arrangeable
|
||||
auto process_arrangeable = [](const ArrangePolygon &arrpoly,
|
||||
std::vector<Item> & outp)
|
||||
{
|
||||
Polygon p = arrpoly.poly.contour;
|
||||
const Vec2crd &offs = arrpoly.translation;
|
||||
double rotation = arrpoly.rotation;
|
||||
|
||||
if (p.is_counter_clockwise()) p.reverse();
|
||||
|
||||
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
||||
|
||||
if (!clpath.Contour.empty()) {
|
||||
auto firstp = clpath.Contour.front();
|
||||
clpath.Contour.emplace_back(firstp);
|
||||
}
|
||||
|
||||
outp.emplace_back(std::move(clpath));
|
||||
outp.back().rotation(rotation);
|
||||
outp.back().translation({offs.x(), offs.y()});
|
||||
outp.back().binId(arrpoly.bed_idx);
|
||||
outp.back().priority(arrpoly.priority);
|
||||
};
|
||||
|
||||
for (ArrangePolygon &arrangeable : arrangables)
|
||||
process_arrangeable(arrangeable, items);
|
||||
|
||||
@ -649,45 +595,10 @@ void arrange(ArrangePolygons & arrangables,
|
||||
|
||||
for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON));
|
||||
|
||||
auto &cfn = stopcondition;
|
||||
auto &pri = progressind;
|
||||
auto &cfn = params.stopcondition;
|
||||
auto &pri = params.progressind;
|
||||
|
||||
switch (bedhint.get_type()) {
|
||||
case bsBox: {
|
||||
// Create the arranger for the box shaped bed
|
||||
BoundingBox bbb = bedhint.get_box();
|
||||
Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}};
|
||||
|
||||
_arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn);
|
||||
break;
|
||||
}
|
||||
case bsCircle: {
|
||||
auto cc = to_lnCircle(bedhint.get_circle());
|
||||
|
||||
_arrange(items, fixeditems, cc, min_obj_dist, pri, cfn);
|
||||
break;
|
||||
}
|
||||
case bsIrregular: {
|
||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.get_irregular());
|
||||
auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
|
||||
BoundingBox polybb(bedhint.get_irregular());
|
||||
|
||||
_arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn);
|
||||
break;
|
||||
}
|
||||
case bsInfinite: {
|
||||
const InfiniteBed& nobin = bedhint.get_infinite();
|
||||
auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()});
|
||||
|
||||
_arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn);
|
||||
break;
|
||||
}
|
||||
case bsUnknown: {
|
||||
// We know nothing about the bed, let it be infinite and zero centered
|
||||
_arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
|
||||
|
||||
for(size_t i = 0; i < items.size(); ++i) {
|
||||
clppr::IntPoint tr = items[i].translation();
|
||||
@ -697,15 +608,10 @@ void arrange(ArrangePolygons & arrangables,
|
||||
}
|
||||
}
|
||||
|
||||
// Arrange, without the fixed items (excludes)
|
||||
void arrange(ArrangePolygons & inp,
|
||||
coord_t min_d,
|
||||
const BedShapeHint & bedhint,
|
||||
std::function<void(unsigned)> prfn,
|
||||
std::function<bool()> stopfn)
|
||||
{
|
||||
arrange(inp, {}, min_d, bedhint, prfn, stopfn);
|
||||
}
|
||||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams ¶ms);
|
||||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams ¶ms);
|
||||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams ¶ms);
|
||||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams ¶ms);
|
||||
|
||||
} // namespace arr
|
||||
} // namespace Slic3r
|
||||
|
@ -1,12 +1,10 @@
|
||||
#ifndef MODELARRANGE_HPP
|
||||
#define MODELARRANGE_HPP
|
||||
#ifndef ARRANGE_HPP
|
||||
#define ARRANGE_HPP
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace arrangement {
|
||||
namespace Slic3r { namespace arrangement {
|
||||
|
||||
/// A geometry abstraction for a circular print bed. Similarly to BoundingBox.
|
||||
class CircleBed {
|
||||
@ -15,96 +13,16 @@ class CircleBed {
|
||||
public:
|
||||
|
||||
inline CircleBed(): center_(0, 0), radius_(std::nan("")) {}
|
||||
inline CircleBed(const Point& c, double r): center_(c), radius_(r) {}
|
||||
explicit inline CircleBed(const Point& c, double r): center_(c), radius_(r) {}
|
||||
|
||||
inline double radius() const { return radius_; }
|
||||
inline const Point& center() const { return center_; }
|
||||
inline operator bool() { return !std::isnan(radius_); }
|
||||
};
|
||||
|
||||
/// Representing an unbounded bed.
|
||||
struct InfiniteBed { Point center; };
|
||||
|
||||
/// Types of print bed shapes.
|
||||
enum BedShapes {
|
||||
bsBox,
|
||||
bsCircle,
|
||||
bsIrregular,
|
||||
bsInfinite,
|
||||
bsUnknown
|
||||
};
|
||||
|
||||
/// Info about the print bed for the arrange() function. This is a variant
|
||||
/// holding one of the four shapes a bed can be.
|
||||
class BedShapeHint {
|
||||
BedShapes m_type = BedShapes::bsInfinite;
|
||||
|
||||
// The union neither calls constructors nor destructors of its members.
|
||||
// The only member with non-trivial constructor / destructor is the polygon,
|
||||
// a placement new / delete needs to be called over it.
|
||||
union BedShape_u { // TODO: use variant from cpp17?
|
||||
CircleBed circ;
|
||||
BoundingBox box;
|
||||
Polyline polygon;
|
||||
InfiniteBed infbed{};
|
||||
~BedShape_u() {}
|
||||
BedShape_u() {}
|
||||
} m_bed;
|
||||
|
||||
// Reset the type, allocate m_bed properly
|
||||
void reset(BedShapes type);
|
||||
|
||||
public:
|
||||
|
||||
BedShapeHint(){}
|
||||
|
||||
/// Get a bed shape hint for arrange() from a naked Polyline.
|
||||
explicit BedShapeHint(const Polyline &polyl);
|
||||
explicit BedShapeHint(const BoundingBox &bb)
|
||||
{
|
||||
m_type = bsBox; m_bed.box = bb;
|
||||
}
|
||||
|
||||
explicit BedShapeHint(const CircleBed &c)
|
||||
{
|
||||
m_type = bsCircle; m_bed.circ = c;
|
||||
}
|
||||
|
||||
explicit BedShapeHint(const InfiniteBed &ibed)
|
||||
{
|
||||
m_type = bsInfinite; m_bed.infbed = ibed;
|
||||
}
|
||||
|
||||
~BedShapeHint()
|
||||
{
|
||||
if (m_type == BedShapes::bsIrregular)
|
||||
m_bed.polygon.Slic3r::Polyline::~Polyline();
|
||||
}
|
||||
|
||||
BedShapeHint(const BedShapeHint &cpy) { *this = cpy; }
|
||||
BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); }
|
||||
|
||||
BedShapeHint &operator=(const BedShapeHint &cpy);
|
||||
BedShapeHint& operator=(BedShapeHint &&cpy);
|
||||
|
||||
BedShapes get_type() const { return m_type; }
|
||||
|
||||
const BoundingBox &get_box() const
|
||||
{
|
||||
assert(m_type == bsBox); return m_bed.box;
|
||||
}
|
||||
const CircleBed &get_circle() const
|
||||
{
|
||||
assert(m_type == bsCircle); return m_bed.circ;
|
||||
}
|
||||
const Polyline &get_irregular() const
|
||||
{
|
||||
assert(m_type == bsIrregular); return m_bed.polygon;
|
||||
}
|
||||
const InfiniteBed &get_infinite() const
|
||||
{
|
||||
assert(m_type == bsInfinite); return m_bed.infbed;
|
||||
}
|
||||
struct InfiniteBed {
|
||||
Point center;
|
||||
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
|
||||
};
|
||||
|
||||
/// A logical bed representing an object not being arranged. Either the arrange
|
||||
@ -125,9 +43,14 @@ 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
|
||||
coord_t inflation = 0; /// Arrange with inflated polygon
|
||||
int bed_idx{UNARRANGED}; /// To which logical bed does poly belong...
|
||||
int priority{0};
|
||||
|
||||
// If empty, any rotation is allowed (currently unsupported)
|
||||
// If only a zero is there, no rotation is allowed
|
||||
std::vector<double> allowed_rotations = {0.};
|
||||
|
||||
/// Optional setter function which can store arbitrary data in its closure
|
||||
std::function<void(const ArrangePolygon&)> setter = nullptr;
|
||||
|
||||
@ -140,6 +63,30 @@ struct ArrangePolygon {
|
||||
|
||||
using ArrangePolygons = std::vector<ArrangePolygon>;
|
||||
|
||||
struct ArrangeParams {
|
||||
|
||||
/// The minimum distance which is allowed for any
|
||||
/// pair of items on the print bed in any direction.
|
||||
coord_t min_obj_distance = 0.;
|
||||
|
||||
/// The accuracy of optimization.
|
||||
/// Goes from 0.0 to 1.0 and scales performance as well
|
||||
float accuracy = 0.65f;
|
||||
|
||||
/// Allow parallel execution.
|
||||
bool parallel = true;
|
||||
|
||||
/// Progress indicator callback called when an object gets packed.
|
||||
/// The unsigned argument is the number of items remaining to pack.
|
||||
std::function<void(unsigned)> progressind;
|
||||
|
||||
/// A predicate returning true if abort is needed.
|
||||
std::function<bool(void)> stopcondition;
|
||||
|
||||
ArrangeParams() = default;
|
||||
explicit ArrangeParams(coord_t md) : min_obj_distance(md) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Arranges the input polygons.
|
||||
*
|
||||
@ -150,33 +97,23 @@ using ArrangePolygons = std::vector<ArrangePolygon>;
|
||||
* \param items Input vector of ArrangePolygons. The transformation, rotation
|
||||
* and bin_idx fields will be changed after the call finished and can be used
|
||||
* to apply the result on the input polygon.
|
||||
*
|
||||
* \param min_obj_distance The minimum distance which is allowed for any
|
||||
* pair of items on the print bed in any direction.
|
||||
*
|
||||
* \param bedhint Info about the shape and type of the bed.
|
||||
*
|
||||
* \param progressind Progress indicator callback called when
|
||||
* an object gets packed. The unsigned argument is the number of items
|
||||
* remaining to pack.
|
||||
*
|
||||
* \param stopcondition A predicate returning true if abort is needed.
|
||||
*/
|
||||
void arrange(ArrangePolygons & items,
|
||||
coord_t min_obj_distance,
|
||||
const BedShapeHint & bedhint,
|
||||
std::function<void(unsigned)> progressind = nullptr,
|
||||
std::function<bool(void)> stopcondition = nullptr);
|
||||
template<class TBed> void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const TBed &bed, const ArrangeParams ¶ms = {});
|
||||
|
||||
/// Same as the previous, only that it takes unmovable items as an
|
||||
/// additional argument. Those will be considered as already arranged objects.
|
||||
void arrange(ArrangePolygons & items,
|
||||
const ArrangePolygons & excludes,
|
||||
coord_t min_obj_distance,
|
||||
const BedShapeHint & bedhint,
|
||||
std::function<void(unsigned)> progressind = nullptr,
|
||||
std::function<bool(void)> stopcondition = nullptr);
|
||||
// A dispatch function that determines the bed shape from a set of points.
|
||||
template<> void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Points &bed, const ArrangeParams ¶ms);
|
||||
|
||||
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams ¶ms);
|
||||
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams ¶ms);
|
||||
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams ¶ms);
|
||||
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams ¶ms);
|
||||
|
||||
inline void arrange(ArrangePolygons &items, const Points &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); }
|
||||
inline void arrange(ArrangePolygons &items, const BoundingBox &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); }
|
||||
inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); }
|
||||
inline void arrange(ArrangePolygons &items, const Polygon &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); }
|
||||
inline void arrange(ArrangePolygons &items, const InfiniteBed &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); }
|
||||
|
||||
}} // namespace Slic3r::arrangement
|
||||
|
||||
} // arr
|
||||
} // Slic3r
|
||||
#endif // MODELARRANGE_HPP
|
||||
|
@ -120,6 +120,8 @@ add_library(libslic3r STATIC
|
||||
Line.hpp
|
||||
Model.cpp
|
||||
Model.hpp
|
||||
ModelArrange.hpp
|
||||
ModelArrange.cpp
|
||||
CustomGCode.cpp
|
||||
CustomGCode.hpp
|
||||
Arrange.hpp
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
@ -355,116 +356,6 @@ TriangleMesh Model::mesh() const
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out)
|
||||
{
|
||||
if (sizes.empty())
|
||||
// return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash
|
||||
return true;
|
||||
|
||||
// we supply unscaled data to arrange()
|
||||
bool result = Slic3r::Geometry::arrange(
|
||||
sizes.size(), // number of parts
|
||||
BoundingBoxf(sizes).max, // width and height of a single cell
|
||||
dist, // distance between cells
|
||||
bb, // bounding box of the area to fill
|
||||
out // output positions
|
||||
);
|
||||
|
||||
if (!result && bb != nullptr) {
|
||||
// Try to arrange again ignoring bb
|
||||
result = Slic3r::Geometry::arrange(
|
||||
sizes.size(), // number of parts
|
||||
BoundingBoxf(sizes).max, // width and height of a single cell
|
||||
dist, // distance between cells
|
||||
nullptr, // bounding box of the area to fill
|
||||
out // output positions
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* arrange objects preserving their instance count
|
||||
but altering their instance positions */
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto obj : objects) count += obj->instances.size();
|
||||
|
||||
arrangement::ArrangePolygons input;
|
||||
ModelInstancePtrs instances;
|
||||
input.reserve(count);
|
||||
instances.reserve(count);
|
||||
for (ModelObject *mo : objects)
|
||||
for (ModelInstance *minst : mo->instances) {
|
||||
input.emplace_back(minst->get_arrange_polygon());
|
||||
instances.emplace_back(minst);
|
||||
}
|
||||
|
||||
arrangement::BedShapeHint bedhint;
|
||||
coord_t bedwidth = 0;
|
||||
|
||||
if (bb) {
|
||||
bedwidth = scaled(bb->size().x());
|
||||
bedhint = arrangement::BedShapeHint(
|
||||
BoundingBox(scaled(bb->min), scaled(bb->max)));
|
||||
}
|
||||
|
||||
arrangement::arrange(input, scaled(dist), bedhint);
|
||||
|
||||
bool ret = true;
|
||||
coord_t stride = bedwidth + bedwidth / 5;
|
||||
|
||||
for(size_t i = 0; i < input.size(); ++i) {
|
||||
if (input[i].bed_idx != 0) ret = false;
|
||||
if (input[i].bed_idx >= 0) {
|
||||
input[i].translation += Vec2crd{input[i].bed_idx * stride, 0};
|
||||
instances[i]->apply_arrange_result(input[i].translation.cast<double>(),
|
||||
input[i].rotation);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Duplicate the entire model preserving instance relative positions.
|
||||
void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
|
||||
{
|
||||
Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size()));
|
||||
Pointfs positions;
|
||||
if (! _arrange(model_sizes, dist, bb, positions))
|
||||
throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
|
||||
|
||||
// note that this will leave the object count unaltered
|
||||
|
||||
for (ModelObject *o : this->objects) {
|
||||
// make a copy of the pointers in order to avoid recursion when appending their copies
|
||||
ModelInstancePtrs instances = o->instances;
|
||||
for (const ModelInstance *i : instances) {
|
||||
for (const Vec2d &pos : positions) {
|
||||
ModelInstance *instance = o->add_instance(*i);
|
||||
instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0));
|
||||
}
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
}
|
||||
|
||||
/* this will append more instances to each object
|
||||
and then automatically rearrange everything */
|
||||
void Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
|
||||
{
|
||||
for (ModelObject *o : this->objects) {
|
||||
// make a copy of the pointers in order to avoid recursion when appending their copies
|
||||
ModelInstancePtrs instances = o->instances;
|
||||
for (const ModelInstance *i : instances)
|
||||
for (size_t k = 2; k <= copies_num; ++ k)
|
||||
o->add_instance(*i);
|
||||
}
|
||||
|
||||
this->arrange_objects(dist, bb);
|
||||
}
|
||||
|
||||
void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
|
||||
{
|
||||
if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects";
|
||||
@ -1991,6 +1882,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
}
|
||||
|
@ -802,11 +802,9 @@ public:
|
||||
bool center_instances_around_point(const Vec2d &point);
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
|
||||
TriangleMesh mesh() const;
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
|
||||
// Croaks if the duplicated objects do not fit the print bed.
|
||||
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
|
||||
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
|
||||
|
||||
bool looks_like_multipart_object() const;
|
||||
void convert_multipart_object(unsigned int max_extruders);
|
||||
@ -822,6 +820,7 @@ public:
|
||||
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
|
||||
|
||||
private:
|
||||
|
||||
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
|
||||
void assign_new_unique_ids_recursive();
|
||||
void update_links_bottom_up_recursive();
|
||||
@ -831,7 +830,7 @@ private:
|
||||
template<class Archive> void serialize(Archive &ar) {
|
||||
Internal::StaticSerializationWrapper<ModelWipeTower> wipe_tower_wrapper(wipe_tower);
|
||||
ar(materials, objects, wipe_tower_wrapper);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
|
||||
|
83
src/libslic3r/ModelArrange.cpp
Normal file
83
src/libslic3r/ModelArrange.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "ModelArrange.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
arrangement::ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto obj : model.objects) count += obj->instances.size();
|
||||
|
||||
ArrangePolygons input;
|
||||
input.reserve(count);
|
||||
instances.clear(); instances.reserve(count);
|
||||
for (ModelObject *mo : model.objects)
|
||||
for (ModelInstance *minst : mo->instances) {
|
||||
input.emplace_back(minst->get_arrange_polygon());
|
||||
instances.emplace_back(minst);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
bool apply_arrange_polys(ArrangePolygons &input, ModelInstancePtrs &instances, VirtualBedFn vfn)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
for(size_t i = 0; i < input.size(); ++i) {
|
||||
if (input[i].bed_idx != 0) { ret = false; if (vfn) vfn(input[i]); }
|
||||
if (input[i].bed_idx >= 0)
|
||||
instances[i]->apply_arrange_result(input[i].translation,
|
||||
input[i].rotation);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model)
|
||||
{
|
||||
ArrangePolygon ap;
|
||||
Points &apts = ap.poly.contour.points;
|
||||
for (const ModelObject *mo : model.objects)
|
||||
for (const ModelInstance *minst : mo->instances) {
|
||||
ArrangePolygon obj_ap = minst->get_arrange_polygon();
|
||||
ap.poly.contour.rotate(obj_ap.rotation);
|
||||
ap.poly.contour.translate(obj_ap.translation.x(), obj_ap.translation.y());
|
||||
const Points &pts = obj_ap.poly.contour.points;
|
||||
std::copy(pts.begin(), pts.end(), std::back_inserter(apts));
|
||||
}
|
||||
|
||||
apts = Geometry::convex_hull(apts);
|
||||
return ap;
|
||||
}
|
||||
|
||||
void duplicate(Model &model, Slic3r::arrangement::ArrangePolygons &copies, VirtualBedFn vfn)
|
||||
{
|
||||
for (ModelObject *o : model.objects) {
|
||||
// make a copy of the pointers in order to avoid recursion when appending their copies
|
||||
ModelInstancePtrs instances = o->instances;
|
||||
o->instances.clear();
|
||||
for (const ModelInstance *i : instances) {
|
||||
for (arrangement::ArrangePolygon &ap : copies) {
|
||||
if (ap.bed_idx != 0) vfn(ap);
|
||||
ModelInstance *instance = o->add_instance(*i);
|
||||
Vec2d pos = unscale(ap.translation);
|
||||
instance->set_offset(instance->get_offset() + to_3d(pos, 0.));
|
||||
}
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
}
|
||||
|
||||
void duplicate_objects(Model &model, size_t copies_num)
|
||||
{
|
||||
for (ModelObject *o : model.objects) {
|
||||
// make a copy of the pointers in order to avoid recursion when appending their copies
|
||||
ModelInstancePtrs instances = o->instances;
|
||||
for (const ModelInstance *i : instances)
|
||||
for (size_t k = 2; k <= copies_num; ++ k)
|
||||
o->add_instance(*i);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
68
src/libslic3r/ModelArrange.hpp
Normal file
68
src/libslic3r/ModelArrange.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef MODELARRANGE_HPP
|
||||
#define MODELARRANGE_HPP
|
||||
|
||||
#include <libslic3r/Model.hpp>
|
||||
#include <libslic3r/Arrange.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using arrangement::ArrangePolygon;
|
||||
using arrangement::ArrangePolygons;
|
||||
using arrangement::ArrangeParams;
|
||||
using arrangement::InfiniteBed;
|
||||
using arrangement::CircleBed;
|
||||
|
||||
// Do something with ArrangePolygons in virtual beds
|
||||
using VirtualBedFn = std::function<void(arrangement::ArrangePolygon&)>;
|
||||
|
||||
[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&)
|
||||
{
|
||||
throw std::runtime_error("Objects could not fit on the bed");
|
||||
}
|
||||
|
||||
ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances);
|
||||
ArrangePolygon get_arrange_poly(const Model &model);
|
||||
bool apply_arrange_polys(ArrangePolygons &polys, ModelInstancePtrs &instances, VirtualBedFn);
|
||||
|
||||
void duplicate(Model &model, ArrangePolygons &copies, VirtualBedFn);
|
||||
void duplicate_objects(Model &model, size_t copies_num);
|
||||
|
||||
template<class TBed>
|
||||
bool arrange_objects(Model & model,
|
||||
const TBed & bed,
|
||||
const ArrangeParams ¶ms,
|
||||
VirtualBedFn vfn = throw_if_out_of_bed)
|
||||
{
|
||||
ModelInstancePtrs instances;
|
||||
auto&& input = get_arrange_polys(model, instances);
|
||||
arrangement::arrange(input, bed, params);
|
||||
|
||||
return apply_arrange_polys(input, instances, vfn);
|
||||
}
|
||||
|
||||
template<class TBed>
|
||||
void duplicate(Model & model,
|
||||
size_t copies_num,
|
||||
const TBed & bed,
|
||||
const ArrangeParams ¶ms,
|
||||
VirtualBedFn vfn = throw_if_out_of_bed)
|
||||
{
|
||||
ArrangePolygons copies(copies_num, get_arrange_poly(model));
|
||||
arrangement::arrange(copies, bed, params);
|
||||
duplicate(model, copies, vfn);
|
||||
}
|
||||
|
||||
template<class TBed>
|
||||
void duplicate_objects(Model & model,
|
||||
size_t copies_num,
|
||||
const TBed & bed,
|
||||
const ArrangeParams ¶ms,
|
||||
VirtualBedFn vfn = throw_if_out_of_bed)
|
||||
{
|
||||
duplicate_objects(model, copies_num);
|
||||
arrange_objects(model, bed, params, vfn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // MODELARRANGE_HPP
|
@ -48,7 +48,7 @@ int64_t Polygon::area2x() const
|
||||
}
|
||||
*/
|
||||
|
||||
double Polygon::area() const
|
||||
double Polygon::area(const Points &points)
|
||||
{
|
||||
size_t n = points.size();
|
||||
if (n < 3)
|
||||
@ -62,6 +62,11 @@ double Polygon::area() const
|
||||
return 0.5 * a;
|
||||
}
|
||||
|
||||
double Polygon::area() const
|
||||
{
|
||||
return Polygon::area(points);
|
||||
}
|
||||
|
||||
bool Polygon::is_counter_clockwise() const
|
||||
{
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
virtual ~Polygon() = default;
|
||||
explicit Polygon(const Points &points) : MultiPoint(points) {}
|
||||
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
@ -47,6 +48,7 @@ public:
|
||||
Polyline split_at_first_point() const { return this->split_at_index(0); }
|
||||
Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); }
|
||||
|
||||
static double area(const Points &pts);
|
||||
double area() const;
|
||||
bool is_counter_clockwise() const;
|
||||
bool is_clockwise() const;
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <libslic3r/ModelArrange.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -167,8 +168,7 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
|
||||
object->add_volume(std::move(t));
|
||||
object->add_instance();
|
||||
}
|
||||
model.arrange_objects(min_object_distance(config));
|
||||
model.center_instances_around_point(Slic3r::Vec2d(100, 100));
|
||||
arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))});
|
||||
for (ModelObject *mo : model.objects) {
|
||||
mo->ensure_on_bed();
|
||||
print.auto_assign_extruders(mo);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
@ -41,8 +42,7 @@ SCENARIO("Model construction", "[Model]") {
|
||||
}
|
||||
}
|
||||
model_object->add_instance();
|
||||
model.arrange_objects(min_object_distance(config));
|
||||
model.center_instances_around_point(Slic3r::Vec2d(100, 100));
|
||||
arrange_objects(model, InfiniteBed{scaled(Vec2d(100, 100))}, ArrangeParams{scaled(min_object_distance(config))});
|
||||
model_object->ensure_on_bed();
|
||||
print.auto_assign_extruders(model_object);
|
||||
THEN("Print works?") {
|
||||
|
@ -3,6 +3,7 @@
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
@ -80,9 +81,9 @@
|
||||
ModelObjectPtrs* objects()
|
||||
%code%{ RETVAL = &THIS->objects; %};
|
||||
|
||||
bool arrange_objects(double dist, BoundingBoxf* bb = NULL);
|
||||
void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||
bool arrange_objects(double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) arrange_objects(*THIS, scaled(*bb), ap); else arrange_objects(*THIS, InfiniteBed{}, ap); %};
|
||||
void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) duplicate(*THIS, copies_num, scaled(*bb), ap); else duplicate(*THIS, copies_num, InfiniteBed{}, ap); %};
|
||||
void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) duplicate_objects(*THIS, copies_num, scaled(*bb), ap); else duplicate_objects(*THIS, copies_num, InfiniteBed{}, ap); %};
|
||||
void duplicate_objects_grid(unsigned int x, unsigned int y, double dist);
|
||||
|
||||
bool looks_like_multipart_object() const;
|
||||
|
Loading…
Reference in New Issue
Block a user