From ad4d95f60c8a38630ec87889068d5404d2beae8d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 28 Jun 2018 18:47:18 +0200 Subject: [PATCH] AppController integration --- lib/Slic3r/GUI/MainFrame.pm | 13 + xs/CMakeLists.txt | 4 + .../clipper_backend/clipper_backend.hpp | 6 +- xs/src/libnest2d/libnest2d/common.hpp | 31 ++ xs/src/libnest2d/libnest2d/libnest2d.hpp | 2 + xs/src/libnest2d/tests/main.cpp | 377 +++++++++++++++++- xs/src/libslic3r/Model.cpp | 21 +- xs/src/libslic3r/Model.hpp | 3 +- xs/src/perlglue.cpp | 2 + xs/src/slic3r/AppController.cpp | 310 ++++++++++++++ xs/src/slic3r/AppController.hpp | 267 +++++++++++++ xs/src/slic3r/AppControllerWx.cpp | 295 ++++++++++++++ xs/src/slic3r/IProgressIndicator.hpp | 84 ++++ xs/src/xsinit.h | 1 + xs/xsp/AppController.xsp | 27 ++ xs/xsp/my.map | 2 + xs/xsp/typemap.xspt | 2 + 17 files changed, 1435 insertions(+), 12 deletions(-) create mode 100644 xs/src/slic3r/AppController.cpp create mode 100644 xs/src/slic3r/AppController.hpp create mode 100644 xs/src/slic3r/AppControllerWx.cpp create mode 100644 xs/src/slic3r/IProgressIndicator.hpp create mode 100644 xs/xsp/AppController.xsp diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 2b93351f8..e7af03b6a 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -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 diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 7e9c57a0a..c3821ffc9 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -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 diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp index ef9a2b622..1306525c2 100644 --- a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp +++ b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp @@ -142,6 +142,9 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord 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 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 diff --git a/xs/src/libnest2d/libnest2d/common.hpp b/xs/src/libnest2d/libnest2d/common.hpp index 9de75415b..da2e36fb1 100644 --- a/xs/src/libnest2d/libnest2d/common.hpp +++ b/xs/src/libnest2d/libnest2d/common.hpp @@ -205,5 +205,36 @@ inline Radians::Radians(const Degrees °s): 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(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 diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index aa36bd1bf..60d9c1ff4 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -648,6 +648,8 @@ public: using IndexedPackGroup = _IndexedPackGroup; using PackGroup = _PackGroup; + using ResultType = PackGroup; + using ResultTypeIndexed = IndexedPackGroup; private: BinType bin_; diff --git a/xs/src/libnest2d/tests/main.cpp b/xs/src/libnest2d/tests/main.cpp index 20dab3529..db890cdca 100644 --- a/xs/src/libnest2d/tests/main.cpp +++ b/xs/src/libnest2d/tests/main.cpp @@ -74,10 +74,370 @@ void arrangeRectangles() { // {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}} // }; + std::vector 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 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(); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 743b253e3..a46600350 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -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 progressind) { using ArrangeResult = _IndexedPackGroup; @@ -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 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 diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 42b0a9edb..e7d1318f9 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -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 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); diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 205eec218..c8aadc8c3 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -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) { diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp new file mode 100644 index 000000000..abc8c9736 --- /dev/null +++ b/xs/src/slic3r/AppController.cpp @@ -0,0 +1,310 @@ +#include "AppController.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace Slic3r { + +class AppControllerBoilerplate::PriMap { +public: + using M = std::unordered_map; + 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(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; + }); +} + +} diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp new file mode 100644 index 000000000..1dc825526 --- /dev/null +++ b/xs/src/slic3r/AppController.hpp @@ -0,0 +1,267 @@ +#ifndef APPCONTROLLER_HPP +#define APPCONTROLLER_HPP + +#include +#include +#include +#include +#include + +#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; + +private: + + // Pimpl data for thread safe progress indication features + std::unique_ptr progressind_; + +public: + + AppControllerBoilerplate(); + ~AppControllerBoilerplate(); + + using Path = std::string; + using PathList = std::vector; + + /// 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; + + 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 diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp new file mode 100644 index 000000000..c54c02d55 --- /dev/null +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -0,0 +1,295 @@ +#include "AppController.hpp" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +// 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 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( + title_, message_, range_, wxTheApp->GetTopWindow(), + wxPD_APP_MODAL | wxPD_AUTO_HIDE + ); + + if(!gauge_->IsShown()) gauge_->ShowModal(); + Base::state(st); + gauge_->Update(static_cast(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(range)); + Base::states(static_cast(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(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(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(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(gauge->GetRange())); + Base::states(static_cast(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(wxWindow::FindWindowById(gid)); + wxStatusBar* sb = dynamic_cast(wxWindow::FindWindowById(sid)); + + if(gauge && sb) { + global_progressind_ = std::make_shared(gauge, sb, *this); + } +} + +} diff --git a/xs/src/slic3r/IProgressIndicator.hpp b/xs/src/slic3r/IProgressIndicator.hpp new file mode 100644 index 000000000..73296697f --- /dev/null +++ b/xs/src/slic3r/IProgressIndicator.hpp @@ -0,0 +1,84 @@ +#ifndef IPROGRESSINDICATOR_HPP +#define IPROGRESSINDICATOR_HPP + +#include +#include + +namespace Slic3r { + +/** + * @brief Generic progress indication interface. + */ +class IProgressIndicator { +public: + using CancelFn = std::function; // 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 diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 01c1293ac..9365f1979 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -80,6 +80,7 @@ extern "C" { #include #include #include +#include namespace Slic3r { diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp new file mode 100644 index 000000000..1b653081d --- /dev/null +++ b/xs/xsp/AppController.xsp @@ -0,0 +1,27 @@ +%module{Slic3r::XS}; + +%{ +#include +#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(); +}; \ No newline at end of file diff --git a/xs/xsp/my.map b/xs/xsp/my.map index cc902acae..4a14f483f 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -216,6 +216,8 @@ Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T AppConfig* O_OBJECT_SLIC3R +AppController* O_OBJECT_SLIC3R +PrintController* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T GLShader* O_OBJECT_SLIC3R diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 57eae1598..b576b1373 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -268,3 +268,5 @@ $CVar = (PrintObjectStep)SvUV($PerlVar); %}; }; +%typemap{AppController*}; +%typemap{PrintController*};