Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortamasmeszaros <meszaros.q@gmail.com>2020-04-23 19:19:03 +0300
committertamasmeszaros <meszaros.q@gmail.com>2020-04-23 19:19:03 +0300
commit1bffc2b99bedb0d0d76baeec52523dc1fef737e1 (patch)
treeac55976385137cc0d2ec3914a9ef34d7b9d6531a /src
parent44ca0a6c3d8857428d0b59c5c35859442724928d (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/PrusaSlicer.cpp79
-rw-r--r--src/libslic3r/Arrange.cpp318
-rw-r--r--src/libslic3r/Arrange.hpp171
-rw-r--r--src/libslic3r/CMakeLists.txt2
-rw-r--r--src/libslic3r/Model.cpp112
-rw-r--r--src/libslic3r/Model.hpp9
-rw-r--r--src/libslic3r/ModelArrange.cpp83
-rw-r--r--src/libslic3r/ModelArrange.hpp68
-rw-r--r--src/libslic3r/Polygon.cpp9
-rw-r--r--src/libslic3r/Polygon.hpp4
10 files changed, 381 insertions, 474 deletions
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 2857b428e..0f9df1e41 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -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.
@@ -86,13 +81,15 @@ 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)
@@ -612,6 +618,8 @@ bool CLI::setup(int argc, char **argv)
if (opt_loglevel != 0)
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 })
@@ -619,6 +627,11 @@ bool CLI::setup(int argc, char **argv)
m_config.option(optdef.first, true);
set_data_dir(m_config.opt_string("datadir"));
+
+ if (!validity.empty()) {
+ boost::nowide::cerr << "error: " << validity << std::endl;
+ return false;
+ }
return true;
}
diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp
index b8ef0bcdc..5b048b0ff 100644
--- a/src/libslic3r/Arrange.cpp
+++ b/src/libslic3r/Arrange.cpp
@@ -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)
{
- namespace clppr = ClipperLib;
+ 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 &center, const Points& points) {
+ std::vector<double> vertex_distances;
+ double avg_dist = 0;
- std::vector<Item> items, fixeditems;
- items.reserve(arrangables.size());
+ for (auto pt : points)
+ {
+ double distance = distance_to(center, pt);
+ vertex_distances.push_back(distance);
+ avg_dist += distance;
+ }
- // Create Item from Arrangeable
- auto process_arrangeable = [](const ArrangePolygon &arrpoly,
- std::vector<Item> & outp)
+ avg_dist /= vertex_distances.size();
+
+ CircleBed ret(center, avg_dist);
+ for(auto el : vertex_distances)
{
- Polygon p = arrpoly.poly.contour;
- const Vec2crd &offs = arrpoly.translation;
- double rotation = arrpoly.rotation;
+ if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
+ ret = {};
+ break;
+ }
+ }
+
+ return ret;
+}
- if (p.is_counter_clockwise()) p.reverse();
+// 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;
- clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
-
- if (!clpath.Contour.empty()) {
- auto firstp = clpath.Contour.front();
- clpath.Contour.emplace_back(firstp);
- }
+ if (p.is_counter_clockwise()) p.reverse();
- 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);
- };
+ 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());
+
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 &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
} // namespace arr
} // namespace Slic3r
diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp
index 1cfe1c907..352e9e1cf 100644
--- a/src/libslic3r/Arrange.hpp
+++ b/src/libslic3r/Arrange.hpp
@@ -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);
-
-/// 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);
-
-} // arr
-} // Slic3r
+template<class TBed> void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const TBed &bed, const ArrangeParams &params = {});
+
+// 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 &params);
+
+extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams &params);
+extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams &params);
+extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
+extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
+
+inline void arrange(ArrangePolygons &items, const Points &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
+inline void arrange(ArrangePolygons &items, const BoundingBox &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
+inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
+inline void arrange(ArrangePolygons &items, const Polygon &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
+inline void arrange(ArrangePolygons &items, const InfiniteBed &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
+
+}} // namespace Slic3r::arrangement
+
#endif // MODELARRANGE_HPP
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index b37e09ad0..2dc18728c 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -120,6 +120,8 @@ add_library(libslic3r STATIC
Line.hpp
Model.cpp
Model.hpp
+ ModelArrange.hpp
+ ModelArrange.cpp
CustomGCode.cpp
CustomGCode.hpp
Arrange.hpp
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 59df8eb61..619d80c43 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -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 */
}
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 2ddad9e59..a0c5f4e8a 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -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
diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp
new file mode 100644
index 000000000..6ddbc5361
--- /dev/null
+++ b/src/libslic3r/ModelArrange.cpp
@@ -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
diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp
new file mode 100644
index 000000000..d65b0fd6d
--- /dev/null
+++ b/src/libslic3r/ModelArrange.hpp
@@ -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 &params,
+ 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 &params,
+ 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 &params,
+ VirtualBedFn vfn = throw_if_out_of_bed)
+{
+ duplicate_objects(model, copies_num);
+ arrange_objects(model, bed, params, vfn);
+}
+
+}
+
+#endif // MODELARRANGE_HPP
diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp
index e1e299144..48e63dab3 100644
--- a/src/libslic3r/Polygon.cpp
+++ b/src/libslic3r/Polygon.cpp
@@ -48,12 +48,12 @@ int64_t Polygon::area2x() const
}
*/
-double Polygon::area() const
+double Polygon::area(const Points &points)
{
size_t n = points.size();
if (n < 3)
return 0.;
-
+
double a = 0.;
for (size_t i = 0, j = n - 1; i < n; ++i) {
a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1));
@@ -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));
diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp
index 0f8457ebd..c6678e2d8 100644
--- a/src/libslic3r/Polygon.hpp
+++ b/src/libslic3r/Polygon.hpp
@@ -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) {}
@@ -46,7 +47,8 @@ public:
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
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;