AppController integration

This commit is contained in:
tamasmeszaros 2018-06-28 18:47:18 +02:00
parent 5446b9f1e5
commit ad4d95f60c
17 changed files with 1435 additions and 12 deletions

View file

@ -19,6 +19,7 @@ use Wx::Locale gettext => 'L';
our $qs_last_input_file;
our $qs_last_output_file;
our $last_config;
our $appController;
# Events to be sent from a C++ Tab implementation:
# 1) To inform about a change of a configuration value.
@ -31,6 +32,9 @@ sub new {
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
Slic3r::GUI::set_main_frame($self);
$appController = Slic3r::AppController->new();
if ($^O eq 'MSWin32') {
# Load the icon either from the exe, or from the ico file.
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
@ -62,6 +66,15 @@ sub new {
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
$self->SetStatusBar($self->{statusbar});
# Make the global status bar and its progress indicator available in C++
$appController->set_global_progress_indicator(
$self->{statusbar}->{prog}->GetId(),
$self->{statusbar}->GetId(),
);
$appController->set_model($self->{plater}->{model});
$appController->set_print($self->{plater}->{print});
$self->{loaded} = 1;
# initialize layout

View file

@ -240,6 +240,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
${LIBDIR}/slic3r/Utils/Time.cpp
${LIBDIR}/slic3r/Utils/Time.hpp
${LIBDIR}/slic3r/IProgressIndicator.hpp
${LIBDIR}/slic3r/AppController.hpp
${LIBDIR}/slic3r/AppController.cpp
${LIBDIR}/slic3r/AppControllerWx.cpp
)
add_library(admesh STATIC

View file

@ -142,6 +142,9 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
// If the input is not at least a triangle, we can not do this algorithm
if(sh.Contour.size() <= 3) throw GeometryException(GeoErr::OFFSET);
ClipperOffset offs;
Paths result;
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
@ -151,7 +154,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
// it removes the last vertex as well so boost will not have a closed
// polygon
assert(result.size() == 1);
if(result.size() != 1) throw GeometryException(GeoErr::OFFSET);
sh.Contour = result.front();
// recreate closed polygon

View file

@ -205,5 +205,36 @@ inline Radians::Radians(const Degrees &degs): Double( degs * Pi/180) {}
inline double Radians::toDegrees() { return operator Degrees(); }
enum class GeoErr : std::size_t {
OFFSET,
MERGE,
NFP
};
static const std::string ERROR_STR[] = {
"Offsetting could not be done! An invalid geometry may have been added."
"Error while merging geometries!"
"No fit polygon cannaot be calculated."
};
class GeometryException: public std::exception {
virtual const char * errorstr(GeoErr errcode) const {
return ERROR_STR[static_cast<std::size_t>(errcode)].c_str();
}
GeoErr errcode_;
public:
GeometryException(GeoErr code): errcode_(code) {}
GeoErr errcode() const { return errcode_; }
virtual const char * what() const override {
return errorstr(errcode_);
}
};
}
#endif // LIBNEST2D_CONFIG_HPP

View file

@ -648,6 +648,8 @@ public:
using IndexedPackGroup = _IndexedPackGroup<typename Item::ShapeType>;
using PackGroup = _PackGroup<typename Item::ShapeType>;
using ResultType = PackGroup;
using ResultTypeIndexed = IndexedPackGroup;
private:
BinType bin_;

View file

@ -74,10 +74,370 @@ void arrangeRectangles() {
// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
// };
std::vector<Item> crasher = {
{
{-10836093, -2542602},
{-10834392, -1849197},
{-10826793, 1172203},
{-10824993, 1884506},
{-10823291, 2547500},
{-10822994, 2642700},
{-10807392, 2768295},
{-10414295, 3030403},
{-9677799, 3516204},
{-9555896, 3531204},
{-9445194, 3534698},
{9353008, 3487297},
{9463504, 3486999},
{9574008, 3482902},
{9695903, 3467399},
{10684803, 2805702},
{10814098, 2717803},
{10832805, 2599502},
{10836200, 2416801},
{10819705, -2650299},
{10800403, -2772300},
{10670505, -2859596},
{9800403, -3436599},
{9678203, -3516197},
{9556308, -3531196},
{9445903, -3534698},
{9084011, -3533798},
{-9234294, -3487701},
{-9462894, -3486999},
{-9573497, -3483001},
{-9695392, -3467300},
{-9816898, -3387199},
{-10429492, -2977798},
{-10821193, -2714096},
{-10836200, -2588195},
{-10836093, -2542602},
},
{
{-3533699, -9084051},
{-3487197, 9352449},
{-3486797, 9462949},
{-3482795, 9573547},
{-3467201, 9695350},
{-3386997, 9816949},
{-2977699, 10429548},
{-2713897, 10821249},
{-2587997, 10836149},
{-2542396, 10836149},
{-2054698, 10834949},
{2223503, 10824150},
{2459800, 10823547},
{2555002, 10823247},
{2642601, 10822948},
{2768301, 10807350},
{3030302, 10414247},
{3516099, 9677850},
{3531002, 9555850},
{3534502, 9445247},
{3534301, 9334949},
{3487300, -9353052},
{3486904, -9463548},
{3482801, -9574148},
{3467302, -9695848},
{2805601, -10684751},
{2717802, -10814149},
{2599403, -10832952},
{2416801, -10836149},
{-2650199, -10819749},
{-2772197, -10800451},
{-2859397, -10670450},
{-3436401, -9800451},
{-3515998, -9678350},
{-3530998, -9556451},
{-3534500, -9445848},
{-3533699, -9084051},
},
{
{-3533798, -9084051},
{-3487697, 9234249},
{-3486999, 9462949},
{-3482997, 9573547},
{-3467296, 9695350},
{-3387199, 9816949},
{-2977798, 10429548},
{-2714096, 10821249},
{-2588199, 10836149},
{-2542594, 10836149},
{-2054798, 10834949},
{2223701, 10824150},
{2459903, 10823547},
{2555202, 10823247},
{2642704, 10822948},
{2768302, 10807350},
{3030403, 10414247},
{3516204, 9677850},
{3531204, 9555850},
{3534702, 9445247},
{3487300, -9353052},
{3486999, -9463548},
{3482902, -9574148},
{3467403, -9695848},
{2805702, -10684751},
{2717803, -10814149},
{2599502, -10832952},
{2416801, -10836149},
{-2650299, -10819749},
{-2772296, -10800451},
{-2859596, -10670450},
{-3436595, -9800451},
{-3516197, -9678350},
{-3531196, -9556451},
{-3534698, -9445848},
{-3533798, -9084051},
},
{
{-49433151, 4289947},
{-49407352, 4421947},
{-49355751, 4534446},
{-29789449, 36223850},
{-29737350, 36307445},
{-29512149, 36401447},
{-29089149, 36511646},
{-28894351, 36550342},
{-18984951, 37803447},
{-18857151, 37815151},
{-18271148, 37768947},
{-18146148, 37755146},
{-17365447, 37643947},
{-17116649, 37601146},
{11243545, 29732448},
{29276451, 24568149},
{29497543, 24486148},
{29654548, 24410953},
{31574546, 23132640},
{33732849, 21695148},
{33881950, 21584445},
{34019454, 21471950},
{34623153, 20939151},
{34751945, 20816951},
{35249244, 20314647},
{36681549, 18775447},
{36766548, 18680446},
{36794250, 18647144},
{36893951, 18521953},
{37365951, 17881946},
{37440948, 17779247},
{37529243, 17642444},
{42233245, 10012245},
{42307250, 9884048},
{42452949, 9626548},
{44258949, 4766647},
{48122245, -5990951},
{48160751, -6117950},
{49381546, -17083953},
{49412246, -17420953},
{49429450, -18011253},
{49436141, -18702651},
{49438949, -20087953},
{49262947, -24000852},
{49172546, -24522455},
{48847549, -25859151},
{48623847, -26705650},
{48120246, -28514953},
{48067146, -28699455},
{48017845, -28862453},
{47941543, -29096954},
{47892547, -29246852},
{47813545, -29466651},
{47758453, -29612955},
{47307548, -30803253},
{46926544, -31807151},
{46891448, -31899551},
{46672546, -32475852},
{46502449, -32914852},
{46414451, -33140853},
{46294250, -33447650},
{46080146, -33980255},
{46039245, -34071853},
{45970542, -34186653},
{45904243, -34295955},
{45786247, -34475650},
{43063247, -37740955},
{42989547, -37815151},
{12128349, -36354953},
{12101844, -36343955},
{11806152, -36217453},
{7052848, -34171649},
{-3234352, -29743150},
{-15684650, -24381851},
{-16573852, -23998851},
{-20328948, -22381254},
{-20383052, -22357254},
{-20511447, -22280651},
{-42221252, -8719951},
{-42317150, -8653255},
{-42457851, -8528949},
{-42600151, -8399852},
{-47935852, -2722949},
{-48230651, -2316852},
{-48500850, -1713150},
{-48516853, -1676551},
{-48974651, -519649},
{-49003852, -412551},
{-49077850, -129650},
{-49239753, 735946},
{-49312652, 1188549},
{-49349349, 1467647},
{-49351650, 1513347},
{-49438949, 4165744},
{-49433151, 4289947},
},
// {
// {6000, 5851},
// {-6000, -5851},
// {6000, 5851},
// },
{
{-10836097, -2542396},
{-10834396, -1848999},
{-10826797, 1172000},
{-10825000, 1884403},
{-10823299, 2547401},
{-10822998, 2642604},
{-10807395, 2768299},
{-10414299, 3030300},
{-9677799, 3516101},
{-9555896, 3531002},
{-9445198, 3534500},
{-9334899, 3534297},
{9353000, 3487300},
{9463499, 3486904},
{9573999, 3482799},
{9695901, 3467300},
{10684803, 2805603},
{10814102, 2717800},
{10832803, 2599399},
{10836200, 2416797},
{10819700, -2650199},
{10800401, -2772201},
{10670499, -2859397},
{9800401, -3436397},
{9678201, -3515998},
{9556303, -3530998},
{9445901, -3534500},
{9083999, -3533699},
{-9352500, -3487201},
{-9462898, -3486797},
{-9573501, -3482799},
{-9695396, -3467201},
{-9816898, -3386997},
{-10429500, -2977699},
{-10821197, -2713897},
{-10836196, -2588001},
{-10836097, -2542396},
},
{
{-47073699, 26300853},
{-47009803, 27392650},
{-46855804, 28327953},
{-46829402, 28427051},
{-46764102, 28657550},
{-46200401, 30466648},
{-46066703, 30832347},
{-45887104, 31247554},
{-45663700, 31670848},
{-45414802, 32080554},
{-45273700, 32308956},
{-45179702, 32431850},
{-45057804, 32549350},
{-34670803, 40990154},
{-34539802, 41094348},
{-34393600, 41184452},
{-34284805, 41229953},
{-34080402, 41267154},
{17335296, 48077648},
{18460296, 48153553},
{18976600, 48182147},
{20403999, 48148555},
{20562301, 48131153},
{20706001, 48102855},
{20938796, 48053150},
{21134101, 48010051},
{21483192, 47920154},
{21904701, 47806346},
{22180099, 47670154},
{22357795, 47581645},
{22615295, 47423046},
{22782295, 47294651},
{24281791, 45908344},
{24405296, 45784854},
{41569297, 21952449},
{41784301, 21638050},
{41938491, 21393650},
{42030899, 21245052},
{42172996, 21015850},
{42415298, 20607151},
{42468299, 20504650},
{42553100, 20320850},
{42584594, 20250644},
{42684997, 20004344},
{42807098, 19672351},
{42939002, 19255153},
{43052299, 18693950},
{45094100, 7846851},
{45118400, 7684154},
{47079101, -16562252},
{47082000, -16705646},
{46916297, -22172447},
{46911598, -22294349},
{46874893, -22358146},
{44866996, -25470146},
{30996795, -46852050},
{30904998, -46933750},
{30864791, -46945850},
{9315696, -48169147},
{9086494, -48182147},
{8895500, -48160049},
{8513496, -48099548},
{8273696, -48057350},
{8180198, -48039051},
{7319801, -47854949},
{6288299, -47569950},
{6238498, -47554248},
{5936199, -47453250},
{-11930000, -41351551},
{-28986402, -33654148},
{-29111103, -33597148},
{-29201803, -33544147},
{-29324401, -33467845},
{-29467000, -33352848},
{-29606603, -33229351},
{-31140401, -31849147},
{-31264303, -31736450},
{-31385803, -31625452},
{-31829103, -31216148},
{-32127403, -30935951},
{-32253803, -30809648},
{-32364803, -30672147},
{-34225402, -28078847},
{-35819404, -25762451},
{-36304801, -25035346},
{-36506103, -24696445},
{-36574104, -24560146},
{-36926700, -23768646},
{-39767402, -17341148},
{-39904102, -16960147},
{-41008602, -11799850},
{-43227401, -704147},
{-43247303, -577148},
{-47057403, 24847454},
{-47077602, 25021648},
{-47080101, 25128650},
{-47082000, 25562953},
{-47073699, 26300853},
},
};
std::vector<Item> input;
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
// input.insert(input.end(), prusaParts().begin(), prusaParts().end());
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
// input.insert(input.end(), rects.begin(), rects.end());
input.insert(input.end(), crasher.begin(), crasher.end());
Box bin(250*SCALE, 210*SCALE);
@ -90,7 +450,7 @@ void arrangeRectangles() {
// pconf.rotations = {0.0, Pi/2.0, Pi, 3*Pi/2};
Packer::SelectionConfig sconf;
sconf.allow_parallel = true;
sconf.force_parallel = false;
sconf.force_parallel = true;
sconf.try_reverse_order = false;
Packer arrange(bin, min_obj_distance, pconf, sconf);
@ -107,8 +467,17 @@ void arrangeRectangles() {
Benchmark bench;
bench.start();
auto result = arrange.arrange(input.begin(),
input.end());
Packer::ResultType result;
try {
result = arrange.arrange(input.begin(), input.end());
} catch(GeometryException& ge) {
std::cerr << "Geometry error: " << ge.what() << std::endl;
return ;
} catch(std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return ;
}
bench.stop();

View file

@ -377,7 +377,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
* them).
*/
bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
bool first_bin_only)
bool first_bin_only,
std::function<void(unsigned)> progressind)
{
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
@ -435,7 +436,7 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
biggest = &item;
}
}
shapes.push_back(std::ref(it.second));
if(it.second.vertexCount() > 3) shapes.push_back(std::ref(it.second));
});
Box bin;
@ -471,12 +472,19 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
Arranger arranger(bin, min_obj_distance, pcfg, scfg);
arranger.useMinimumBoundigBoxRotation();
arranger.progressIndicator(progressind);
std::cout << "Arranging model..." << std::endl;
bench.start();
// Arrange and return the items with their respective indices within the
// input sequence.
ArrangeResult result =
arranger.arrangeIndexed(shapes.begin(), shapes.end());
ArrangeResult result;
try {
result = arranger.arrangeIndexed(shapes.begin(), shapes.end());
} catch(std::exception& e) {
std::cerr << "An exception occured: " << e.what() << std::endl;
}
bench.stop();
std::cout << "Model arranged in " << bench.getElapsedSec()
@ -543,12 +551,13 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
/* arrange objects preserving their instance count
but altering their instance positions */
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb,
std::function<void(unsigned)> progressind)
{
bool ret = false;
if(bb != nullptr && bb->defined) {
const bool FIRST_BIN_ONLY = false;
ret = arr::arrange(*this, dist, bb, FIRST_BIN_ONLY);
ret = arr::arrange(*this, dist, bb, FIRST_BIN_ONLY, progressind);
} else {
// get the (transformed) size of each instance so that we take
// into account their different transformations when packing

View file

@ -274,7 +274,8 @@ public:
void center_instances_around_point(const Pointf &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);
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL,
std::function<void(unsigned)> progressind = [](unsigned){});
// 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);

View file

@ -65,6 +65,8 @@ REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
REGISTER_CLASS(OctoPrint, "OctoPrint");
REGISTER_CLASS(AppController, "AppController");
REGISTER_CLASS(PrintController, "PrintController");
SV* ConfigBase__as_hash(ConfigBase* THIS)
{

View file

@ -0,0 +1,310 @@
#include "AppController.hpp"
#include <future>
#include <sstream>
#include <cstdarg>
#include <thread>
#include <unordered_map>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/PresetBundle.hpp>
#include <PrintConfig.hpp>
#include <Print.hpp>
#include <Model.hpp>
#include <Utils.hpp>
namespace Slic3r {
class AppControllerBoilerplate::PriMap {
public:
using M = std::unordered_map<std::thread::id, ProgresIndicatorPtr>;
std::mutex m;
M store;
std::thread::id ui_thread;
inline explicit PriMap(std::thread::id uit): ui_thread(uit) {}
};
AppControllerBoilerplate::AppControllerBoilerplate()
:progressind_(new PriMap(std::this_thread::get_id())) {}
AppControllerBoilerplate::~AppControllerBoilerplate() {
progressind_.reset();
}
bool AppControllerBoilerplate::is_main_thread() const
{
return progressind_->ui_thread == std::this_thread::get_id();
}
namespace GUI {
PresetBundle* get_preset_bundle();
}
static const PrintObjectStep STEP_SLICE = posSlice;
static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
static const PrintObjectStep STEP_INFILL = posInfill;
static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
static const PrintStep STEP_SKIRT = psSkirt;
static const PrintStep STEP_BRIM = psBrim;
static const PrintStep STEP_WIPE_TOWER = psWipeTower;
void AppControllerBoilerplate::progress_indicator(
AppControllerBoilerplate::ProgresIndicatorPtr progrind) {
progressind_->m.lock();
progressind_->store[std::this_thread::get_id()] = progrind;
progressind_->m.unlock();
}
void AppControllerBoilerplate::progress_indicator(unsigned statenum,
const std::string &title,
const std::string &firstmsg)
{
progressind_->m.lock();
progressind_->store[std::this_thread::get_id()] =
create_progress_indicator(statenum, title, firstmsg);;
progressind_->m.unlock();
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::progress_indicator() {
PriMap::M::iterator pret;
ProgresIndicatorPtr ret;
progressind_->m.lock();
if( (pret = progressind_->store.find(std::this_thread::get_id()))
== progressind_->store.end())
{
progressind_->store[std::this_thread::get_id()] = ret =
global_progressind_;
} else ret = pret->second;
progressind_->m.unlock();
return ret;
}
void PrintController::make_skirt()
{
assert(print_ != nullptr);
// prerequisites
for(auto obj : print_->objects) make_perimeters(obj);
for(auto obj : print_->objects) infill(obj);
for(auto obj : print_->objects) gen_support_material(obj);
if(!print_->state.is_done(STEP_SKIRT)) {
print_->state.set_started(STEP_SKIRT);
print_->skirt.clear();
if(print_->has_skirt()) print_->_make_skirt();
print_->state.set_done(STEP_SKIRT);
}
}
void PrintController::make_brim()
{
assert(print_ != nullptr);
// prerequisites
for(auto obj : print_->objects) make_perimeters(obj);
for(auto obj : print_->objects) infill(obj);
for(auto obj : print_->objects) gen_support_material(obj);
make_skirt();
if(!print_->state.is_done(STEP_BRIM)) {
print_->state.set_started(STEP_BRIM);
// since this method must be idempotent, we clear brim paths *before*
// checking whether we need to generate them
print_->brim.clear();
if(print_->config.brim_width > 0) print_->_make_brim();
print_->state.set_done(STEP_BRIM);
}
}
void PrintController::make_wipe_tower()
{
assert(print_ != nullptr);
// prerequisites
for(auto obj : print_->objects) make_perimeters(obj);
for(auto obj : print_->objects) infill(obj);
for(auto obj : print_->objects) gen_support_material(obj);
make_skirt();
make_brim();
if(!print_->state.is_done(STEP_WIPE_TOWER)) {
print_->state.set_started(STEP_WIPE_TOWER);
// since this method must be idempotent, we clear brim paths *before*
// checking whether we need to generate them
print_->brim.clear();
if(print_->has_wipe_tower()) print_->_make_wipe_tower();
print_->state.set_done(STEP_WIPE_TOWER);
}
}
void PrintController::slice(PrintObject *pobj)
{
assert(pobj != nullptr && print_ != nullptr);
if(pobj->state.is_done(STEP_SLICE)) return;
pobj->state.set_started(STEP_SLICE);
pobj->_slice();
auto msg = pobj->_fix_slicing_errors();
if(!msg.empty()) report_issue(IssueType::WARN, msg);
// simplify slices if required
if (print_->config.resolution)
pobj->_simplify_slices(scale_(print_->config.resolution));
if(pobj->layers.empty())
report_issue(IssueType::ERR,
"No layers were detected. You might want to repair your "
"STL file(s) or check their size or thickness and retry"
);
pobj->state.set_done(STEP_SLICE);
}
void PrintController::make_perimeters(PrintObject *pobj)
{
assert(pobj != nullptr);
slice(pobj);
auto&& prgind = progress_indicator();
if (!pobj->state.is_done(STEP_PERIMETERS)) {
pobj->_make_perimeters();
}
}
void PrintController::infill(PrintObject *pobj)
{
assert(pobj != nullptr);
make_perimeters(pobj);
if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
pobj->state.set_started(STEP_PREPARE_INFILL);
pobj->_prepare_infill();
pobj->state.set_done(STEP_PREPARE_INFILL);
}
pobj->_infill();
}
void PrintController::gen_support_material(PrintObject *pobj)
{
assert(pobj != nullptr);
// prerequisites
slice(pobj);
if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
pobj->state.set_started(STEP_SUPPORTMATERIAL);
pobj->clear_support_layers();
if((pobj->config.support_material || pobj->config.raft_layers > 0)
&& pobj->layers.size() > 1) {
pobj->_generate_support_material();
}
pobj->state.set_done(STEP_SUPPORTMATERIAL);
}
}
void PrintController::slice()
{
Slic3r::trace(3, "Starting the slicing process.");
progress_indicator()->update(20u, "Generating perimeters");
for(auto obj : print_->objects) make_perimeters(obj);
progress_indicator()->update(60u, "Infilling layers");
for(auto obj : print_->objects) infill(obj);
progress_indicator()->update(70u, "Generating support material");
for(auto obj : print_->objects) gen_support_material(obj);
progress_indicator()->message_fmt("Weight: %.1fg, Cost: %.1f",
print_->total_weight,
print_->total_cost);
progress_indicator()->state(85u);
progress_indicator()->update(88u, "Generating skirt");
make_skirt();
progress_indicator()->update(90u, "Generating brim");
make_brim();
progress_indicator()->update(95u, "Generating wipe tower");
make_wipe_tower();
progress_indicator()->update(100u, "Done");
// time to make some statistics..
Slic3r::trace(3, "Slicing process finished.");
}
void IProgressIndicator::message_fmt(
const std::string &fmtstr, ...) {
std::stringstream ss;
va_list args;
va_start(args, fmtstr);
auto fmt = fmtstr.begin();
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
ss << i << '\n';
} else if (*fmt == 'c') {
// note automatic conversion to integral type
int c = va_arg(args, int);
ss << static_cast<char>(c) << '\n';
} else if (*fmt == 'f') {
double d = va_arg(args, double);
ss << d << '\n';
}
++fmt;
}
va_end(args);
message(ss.str());
}
void AppController::arrange_model()
{
std::async(supports_asynch()? std::launch::async : std::launch::deferred,
[this](){
auto pind = progress_indicator();
// my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
// my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
double dist = GUI::get_preset_bundle()->full_config().option("min_object_distance")->getFloat();
std::cout << dist << std::endl;
});
}
}

View file

@ -0,0 +1,267 @@
#ifndef APPCONTROLLER_HPP
#define APPCONTROLLER_HPP
#include <string>
#include <vector>
#include <memory>
#include <atomic>
#include <iostream>
#include "IProgressIndicator.hpp"
namespace Slic3r {
class Model;
class Print;
class PrintObject;
/**
* @brief A boilerplate class for creating application logic. It should provide
* features as issue reporting and progress indication, etc...
*
* The lower lever UI independent classes can be manipulated with a subclass
* of this controller class. We can also catch any exceptions that lower level
* methods could throw and display appropriate errors and warnings.
*
* Note that the outer and the inner interface of this class is free from any
* UI toolkit dependencies. We can implement it with any UI framework or make it
* a cli client.
*/
class AppControllerBoilerplate {
class PriMap; // Some structure to store progress indication data
public:
/// A Progress indicator object smart pointer
using ProgresIndicatorPtr = std::shared_ptr<IProgressIndicator>;
private:
// Pimpl data for thread safe progress indication features
std::unique_ptr<PriMap> progressind_;
public:
AppControllerBoilerplate();
~AppControllerBoilerplate();
using Path = std::string;
using PathList = std::vector<Path>;
/// Common runtime issue types
enum class IssueType {
INFO,
WARN,
WARN_Q, // Warning with a question to continue
ERR,
FATAL
};
/**
* @brief Query some paths from the user.
*
* It should display a file chooser dialog in case of a UI application.
* @param title Title of a possible query dialog.
* @param extensions Recognized file extensions.
* @return Returns a list of paths choosed by the user.
*/
PathList query_destination_paths(
const std::string& title,
const std::string& extensions) const;
/**
* @brief Same as query_destination_paths but works for directories only.
*/
PathList query_destination_dirs(
const std::string& title) const;
/**
* @brief Same as query_destination_paths but returns only one path.
*/
Path query_destination_path(
const std::string& title,
const std::string& extensions,
const std::string& hint = "") const;
/**
* @brief Report an issue to the user be it fatal or recoverable.
*
* In a UI app this should display some message dialog.
*
* @param issuetype The type of the runtime issue.
* @param description A somewhat longer description of the issue.
* @param brief A very brief description. Can be used for message dialog
* title.
*/
bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief = "");
/**
* @brief Set up a progress indicator for the current thread.
* @param progrind An already created progress indicator object.
*/
void progress_indicator(ProgresIndicatorPtr progrind);
/**
* @brief Create and set up a new progress indicator for the current thread.
* @param statenum The number of states for the given procedure.
* @param title The title of the procedure.
* @param firstmsg The message for the first subtask to be displayed.
*/
void progress_indicator(unsigned statenum,
const std::string& title,
const std::string& firstmsg = "");
/**
* @brief Return the progress indicator set up for the current thread. This
* can be empty as well.
* @return A progress indicator object implementing IProgressIndicator. If
* a global progress indicator is available for the current implementation
* than this will be set up for the current thread and returned.
*/
ProgresIndicatorPtr progress_indicator();
/**
* @brief A predicate telling the caller whether it is the thread that
* created the AppConroller object itself. This probably means that the
* execution is in the UI thread. Otherwise it returns false meaning that
* some worker thread called this function.
* @return Return true for the same caller thread that created this
* object and false for every other.
*/
bool is_main_thread() const;
/**
* @brief The frontend supports asynch execution.
*
* A Graphic UI will support this, a CLI may not. This can be used in
* subclass methods to decide whether to start threads for block free UI.
*
* Note that even a progress indicator's update called regularly can solve
* the blocking UI problem in some cases even when an event loop is present.
* This is how wxWidgets gauge work but creating a separate thread will make
* the UI even more fluent.
*
* @return true if a job or method can be executed asynchronously, false
* otherwise.
*/
bool supports_asynch() const;
protected:
/**
* @brief Create a new progress indicator and return a smart pointer to it.
* @param statenum The number of states for the given procedure.
* @param title The title of the procedure.
* @param firstmsg The message for the first subtask to be displayed.
* @return Smart pointer to the created object.
*/
ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg = "") const;
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
ProgresIndicatorPtr global_progressind_;
};
/**
* @brief Implementation of the printing logic.
*/
class PrintController: public AppControllerBoilerplate {
Print *print_ = nullptr;
protected:
void make_skirt();
void make_brim();
void make_wipe_tower();
void make_perimeters(PrintObject *pobj);
void infill(PrintObject *pobj);
void gen_support_material(PrintObject *pobj);
public:
// Must be public for perl to use it
explicit inline PrintController(Print *print): print_(print) {}
PrintController(const PrintController&) = delete;
PrintController(PrintController&&) = delete;
using Ptr = std::unique_ptr<PrintController>;
inline static Ptr create(Print *print) {
return PrintController::Ptr( new PrintController(print) );
}
/**
* @brief Slice one pront object.
* @param pobj The print object.
*/
void slice(PrintObject *pobj);
/**
* @brief Slice the loaded print scene.
*/
void slice();
};
/**
* @brief Top level controller.
*/
class AppController: public AppControllerBoilerplate {
Model *model_ = nullptr;
PrintController::Ptr printctl;
public:
/**
* @brief Get the print controller object.
*
* @return Return a raw pointer instead of a smart one for perl to be able
* to use this function and access the print controller.
*/
PrintController * print_ctl() { return printctl.get(); }
/**
* @brief Set a model object.
*
* @param model A raw pointer to the model object. This can be used from
* perl.
*/
void set_model(Model *model) { model_ = model; }
/**
* @brief Set the print object from perl.
*
* This will create a print controller that will then be accessible from
* perl.
* @param print A print object which can be a perl-ish extension as well.
*/
void set_print(Print *print) {
printctl = PrintController::create(print);
printctl->progress_indicator(progress_indicator());
}
/**
* @brief Set up a global progress indicator.
*
* In perl we have a progress indicating status bar on the bottom of the
* window which is defined and created in perl. We can pass the ID-s of the
* gauge and the statusbar id and make a wrapper implementation of the
* IProgressIndicator interface so we can use this GUI widget from C++.
*
* This function should be called from perl.
*
* @param gauge_id The ID of the gague widget of the status bar.
* @param statusbar_id The ID of the status bar.
*/
void set_global_progress_indicator(unsigned gauge_id,
unsigned statusbar_id);
void arrange_model();
};
}
#endif // APPCONTROLLER_HPP

View file

@ -0,0 +1,295 @@
#include "AppController.hpp"
#include <thread>
#include <future>
#include <slic3r/GUI/GUI.hpp>
#include <wx/app.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/gauge.h>
#include <wx/statusbr.h>
#include <wx/event.h>
// This source file implements the UI dependent methods of the AppControllers.
// It will be clear what is needed to be reimplemented in case of a UI framework
// change or a CLI client creation. In this particular case we use wxWidgets to
// implement everything.
namespace Slic3r {
bool AppControllerBoilerplate::supports_asynch() const
{
return true;
}
AppControllerBoilerplate::PathList
AppControllerBoilerplate::query_destination_paths(
const std::string &title,
const std::string &extensions) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), wxString(title) );
dlg.SetWildcard(extensions);
dlg.ShowModal();
wxArrayString paths;
dlg.GetPaths(paths);
PathList ret(paths.size(), "");
for(auto& p : paths) ret.push_back(p.ToStdString());
return ret;
}
AppControllerBoilerplate::Path
AppControllerBoilerplate::query_destination_path(
const std::string &title,
const std::string &extensions,
const std::string& hint) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
dlg.SetWildcard(extensions);
dlg.SetFilename(hint);
Path ret;
if(dlg.ShowModal() == wxID_OK) {
ret = Path(dlg.GetPath());
}
return ret;
}
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
const std::string &description,
const std::string &brief)
{
auto icon = wxICON_INFORMATION;
auto style = wxOK|wxCENTRE;
switch(issuetype) {
case IssueType::INFO: break;
case IssueType::WARN: icon = wxICON_WARNING; break;
case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break;
case IssueType::ERR:
case IssueType::FATAL: icon = wxICON_ERROR;
}
auto ret = wxMessageBox(description, brief, icon | style);
return ret != wxCANCEL;
}
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
namespace {
/*
* A simple thread safe progress dialog implementation that can be used from
* the main thread as well.
*/
class GuiProgressIndicator:
public IProgressIndicator, public wxEvtHandler {
std::shared_ptr<wxProgressDialog> gauge_;
using Base = IProgressIndicator;
wxString message_;
int range_; wxString title_;
unsigned prc_ = 0;
bool is_asynch_ = false;
const int id_ = wxWindow::NewControlId();
// status update handler
void _state( wxCommandEvent& evt) {
unsigned st = evt.GetInt();
_state(st);
}
// Status update implementation
void _state( unsigned st) {
if(st < max()) {
if(!gauge_) gauge_ = std::make_shared<wxProgressDialog>(
title_, message_, range_, wxTheApp->GetTopWindow(),
wxPD_APP_MODAL | wxPD_AUTO_HIDE
);
if(!gauge_->IsShown()) gauge_->ShowModal();
Base::state(st);
gauge_->Update(static_cast<int>(st), message_);
}
if(st == max()) {
prc_++;
if(prc_ == Base::procedure_count()) {
gauge_.reset();
prc_ = 0;
}
}
}
public:
/// Setting whether it will be used from the UI thread or some worker thread
inline void asynch(bool is) { is_asynch_ = is; }
/// Get the mode of parallel operation.
inline bool asynch() const { return is_asynch_; }
inline GuiProgressIndicator(int range, const std::string& title,
const std::string& firstmsg) :
range_(range), message_(_(firstmsg)), title_(_(title))
{
Base::max(static_cast<float>(range));
Base::states(static_cast<unsigned>(range));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&GuiProgressIndicator::_state,
this, id_);
}
virtual void cancel() override {
update(max(), "Abort");
IProgressIndicator::cancel();
}
virtual void state(float val) override {
if( val >= 1.0) state(static_cast<unsigned>(val));
}
void state(unsigned st) {
// send status update event
if(is_asynch_) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
evt->SetInt(st);
wxQueueEvent(this, evt);
} else _state(st);
}
virtual void message(const std::string & msg) override {
message_ = _(msg);
}
virtual void messageFmt(const std::string& fmt, ...) {
va_list arglist;
va_start(arglist, fmt);
message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & title) override {
title_ = _(title);
}
};
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::create_progress_indicator(
unsigned statenum, const std::string& title,
const std::string& firstmsg) const
{
auto pri =
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
// We set up the mode of operation depending of the creator thread's
// identity
pri->asynch(!is_main_thread());
return pri;
}
namespace {
// A wrapper progress indicator class around the statusbar created in perl.
class Wrapper: public IProgressIndicator, public wxEvtHandler {
wxGauge *gauge_;
wxStatusBar *stbar_;
using Base = IProgressIndicator;
std::string message_;
AppControllerBoilerplate& ctl_;
void showProgress(bool show = true) {
gauge_->Show(show);
}
void _state(unsigned st) {
if( st <= max() ) {
Base::state(st);
if(!gauge_->IsShown()) showProgress(true);
stbar_->SetStatusText(message_);
if(st == gauge_->GetRange()) {
gauge_->SetValue(0);
showProgress(false);
} else {
gauge_->SetValue(static_cast<int>(st));
}
}
}
// status update handler
void _state( wxCommandEvent& evt) {
unsigned st = evt.GetInt(); _state(st);
}
const int id_ = wxWindow::NewControlId();
public:
inline Wrapper(wxGauge *gauge, wxStatusBar *stbar,
AppControllerBoilerplate& ctl):
gauge_(gauge), stbar_(stbar), ctl_(ctl)
{
Base::max(static_cast<float>(gauge->GetRange()));
Base::states(static_cast<unsigned>(gauge->GetRange()));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&Wrapper::_state,
this, id_);
}
virtual void state(float val) override {
if(val >= 1.0) state(unsigned(val));
}
void state(unsigned st) {
if(!ctl_.is_main_thread()) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
evt->SetInt(st);
wxQueueEvent(this, evt);
} else _state(st);
}
virtual void message(const std::string & msg) override {
message_ = msg;
}
virtual void message_fmt(const std::string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & /*title*/) override {}
};
}
void AppController::set_global_progress_indicator(
unsigned gid,
unsigned sid)
{
wxGauge* gauge = dynamic_cast<wxGauge*>(wxWindow::FindWindowById(gid));
wxStatusBar* sb = dynamic_cast<wxStatusBar*>(wxWindow::FindWindowById(sid));
if(gauge && sb) {
global_progressind_ = std::make_shared<Wrapper>(gauge, sb, *this);
}
}
}

View file

@ -0,0 +1,84 @@
#ifndef IPROGRESSINDICATOR_HPP
#define IPROGRESSINDICATOR_HPP
#include <string>
#include <functional>
namespace Slic3r {
/**
* @brief Generic progress indication interface.
*/
class IProgressIndicator {
public:
using CancelFn = std::function<void(void)>; // Cancel functio signature.
private:
float state_ = .0f, max_ = 1.f, step_;
CancelFn cancelfunc_ = [](){};
unsigned proc_count_ = 1;
public:
inline virtual ~IProgressIndicator() {}
/// Get the maximum of the progress range.
float max() const { return max_; }
/// Get the current progress state
float state() const { return state_; }
/// Set the maximum of hte progress range
virtual void max(float maxval) { max_ = maxval; }
/// Set the current state of the progress.
virtual void state(float val) { state_ = val; }
/**
* @brief Number of states int the progress. Can be used insted of giving a
* maximum value.
*/
virtual void states(unsigned statenum) {
step_ = max_ / statenum;
}
/// Message shown on the next status update.
virtual void message(const std::string&) = 0;
/// Title of the operaton.
virtual void title(const std::string&) = 0;
/// Formatted message for the next status update. Works just like sprinf.
virtual void message_fmt(const std::string& fmt, ...);
/// Set up a cancel callback for the operation if feasible.
inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
/**
* Explicitly shut down the progress indicator and call the associated
* callback.
*/
virtual void cancel() { cancelfunc_(); }
/**
* \brief Set up how many subprocedures does the whole operation contain.
*
* This was neccesary from practical reasons. If the progress indicator is
* a dialog and we want to show the progress of a few sub operations than
* the dialog wont be closed and reopened each time a new sub operation is
* started. This is not a mandatory feature and can be ignored completely.
*/
inline void procedure_count(unsigned pc) { proc_count_ = pc; }
/// Get the current procedure count
inline unsigned procedure_count() const { return proc_count_; }
/// Convinience function to call message and status update in one function.
void update(float st, const std::string& msg) {
message(msg); state(st);
}
};
}
#endif // IPROGRESSINDICATOR_HPP

View file

@ -80,6 +80,7 @@ extern "C" {
#include <Polygon.hpp>
#include <Polyline.hpp>
#include <TriangleMesh.hpp>
#include <slic3r/AppController.hpp>
namespace Slic3r {

27
xs/xsp/AppController.xsp Normal file
View file

@ -0,0 +1,27 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "slic3r/AppController.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
%}
%name{Slic3r::PrintController} class PrintController {
PrintController(Print *print);
void slice();
};
%name{Slic3r::AppController} class AppController {
AppController();
PrintController *print_ctl();
void set_model(Model *model);
void set_print(Print *print);
void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id);
void arrange_model();
};

View file

@ -216,6 +216,8 @@ Ref<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
Clone<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
AppConfig* O_OBJECT_SLIC3R
AppController* O_OBJECT_SLIC3R
PrintController* O_OBJECT_SLIC3R
Ref<AppConfig> O_OBJECT_SLIC3R_T
GLShader* O_OBJECT_SLIC3R

View file

@ -268,3 +268,5 @@
$CVar = (PrintObjectStep)SvUV($PerlVar);
%};
};
%typemap{AppController*};
%typemap{PrintController*};