mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-30 12:11:15 -06:00
Getting rid of AppController.
This commit is contained in:
parent
33eade5300
commit
98a640ea06
13 changed files with 857 additions and 1941 deletions
|
|
@ -1,364 +0,0 @@
|
|||
#include "AppController.hpp"
|
||||
|
||||
#include <slic3r/GUI/GUI.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <PrintConfig.hpp>
|
||||
#include <Print.hpp>
|
||||
#include <PrintExport.hpp>
|
||||
#include <Geometry.hpp>
|
||||
#include <Model.hpp>
|
||||
#include <ModelArrange.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppControllerGui::PriData {
|
||||
public:
|
||||
std::mutex m;
|
||||
std::thread::id ui_thread;
|
||||
|
||||
inline explicit PriData(std::thread::id uit): ui_thread(uit) {}
|
||||
};
|
||||
|
||||
AppControllerGui::AppControllerGui()
|
||||
:m_pri_data(new PriData(std::this_thread::get_id())) {}
|
||||
|
||||
AppControllerGui::~AppControllerGui() {
|
||||
m_pri_data.reset();
|
||||
}
|
||||
|
||||
bool AppControllerGui::is_main_thread() const
|
||||
{
|
||||
return m_pri_data->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;
|
||||
|
||||
ProgresIndicatorPtr AppControllerGui::global_progress_indicator() {
|
||||
ProgresIndicatorPtr ret;
|
||||
|
||||
m_pri_data->m.lock();
|
||||
ret = m_global_progressind;
|
||||
m_pri_data->m.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AppControllerGui::global_progress_indicator(ProgresIndicatorPtr gpri)
|
||||
{
|
||||
m_pri_data->m.lock();
|
||||
m_global_progressind = gpri;
|
||||
m_pri_data->m.unlock();
|
||||
}
|
||||
|
||||
PrintController::PngExportData
|
||||
PrintController::query_png_export_data(const DynamicPrintConfig& conf)
|
||||
{
|
||||
PngExportData ret;
|
||||
|
||||
auto c = GUI::get_appctl();
|
||||
auto zippath = c->query_destination_path("Output zip file", "*.zip",
|
||||
"export-png",
|
||||
"out");
|
||||
|
||||
ret.zippath = zippath;
|
||||
|
||||
ret.width_mm = conf.opt_float("display_width");
|
||||
ret.height_mm = conf.opt_float("display_height");
|
||||
|
||||
ret.width_px = conf.opt_int("display_pixels_x");
|
||||
ret.height_px = conf.opt_int("display_pixels_y");
|
||||
|
||||
auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
|
||||
|
||||
if(opt_corr) {
|
||||
ret.corr_x = opt_corr->values[0];
|
||||
ret.corr_y = opt_corr->values[1];
|
||||
ret.corr_z = opt_corr->values[2];
|
||||
}
|
||||
|
||||
ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
|
||||
ret.exp_time_s = conf.opt_float("exposure_time");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PrintController::slice(ProgresIndicatorPtr pri)
|
||||
{
|
||||
m_print->set_status_callback([pri](int st, const std::string& msg){
|
||||
pri->update(unsigned(st), msg);
|
||||
});
|
||||
|
||||
m_print->process();
|
||||
}
|
||||
|
||||
void PrintController::slice()
|
||||
{
|
||||
auto ctl = GUI::get_appctl();
|
||||
auto pri = ctl->global_progress_indicator();
|
||||
if(!pri) pri = ctl->create_progress_indicator(100, L("Slicing"));
|
||||
slice(pri);
|
||||
}
|
||||
|
||||
template<> class LayerWriter<Zipper> {
|
||||
Zipper m_zip;
|
||||
public:
|
||||
|
||||
inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
|
||||
|
||||
inline void next_entry(const std::string& fname) { m_zip.next_entry(fname); }
|
||||
|
||||
inline std::string get_name() const { return m_zip.get_name(); }
|
||||
|
||||
template<class T> inline LayerWriter& operator<<(const T& arg) {
|
||||
m_zip.stream() << arg; return *this;
|
||||
}
|
||||
|
||||
inline void close() { m_zip.close(); }
|
||||
};
|
||||
|
||||
void PrintController::slice_to_png()
|
||||
{
|
||||
// using Pointf3 = Vec3d;
|
||||
|
||||
// auto ctl = GUI::get_appctl();
|
||||
// auto presetbundle = GUI::wxGetApp().preset_bundle;
|
||||
|
||||
// assert(presetbundle);
|
||||
|
||||
// // FIXME: this crashes in command line mode
|
||||
// auto pt = presetbundle->printers.get_selected_preset().printer_technology();
|
||||
// if(pt != ptSLA) {
|
||||
// ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"),
|
||||
// L("Error"));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// auto conf = presetbundle->full_config();
|
||||
// conf.validate();
|
||||
|
||||
// auto exd = query_png_export_data(conf);
|
||||
// if(exd.zippath.empty()) return;
|
||||
|
||||
// Print *print = m_print;
|
||||
|
||||
// try {
|
||||
// print->apply_config(conf);
|
||||
// print->validate();
|
||||
// } catch(std::exception& e) {
|
||||
// ctl->report_issue(IssueType::ERR, e.what(), "Error");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // TODO: copy the model and work with the copy only
|
||||
// bool correction = false;
|
||||
// if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
|
||||
// correction = true;
|
||||
//// print->invalidate_all_steps();
|
||||
|
||||
//// for(auto po : print->objects) {
|
||||
//// po->model_object()->scale(
|
||||
//// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
|
||||
//// );
|
||||
//// po->model_object()->invalidate_bounding_box();
|
||||
//// po->reload_model_instances();
|
||||
//// po->invalidate_all_steps();
|
||||
//// }
|
||||
// }
|
||||
|
||||
// // Turn back the correction scaling on the model.
|
||||
// auto scale_back = [this, print, correction, exd]() {
|
||||
// if(correction) { // scale the model back
|
||||
//// print->invalidate_all_steps();
|
||||
//// for(auto po : print->objects) {
|
||||
//// po->model_object()->scale(
|
||||
//// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
|
||||
//// );
|
||||
//// po->model_object()->invalidate_bounding_box();
|
||||
//// po->reload_model_instances();
|
||||
//// po->invalidate_all_steps();
|
||||
//// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// auto print_bb = print->bounding_box();
|
||||
// Vec2d punsc = unscale(print_bb.size());
|
||||
|
||||
// // If the print does not fit into the print area we should cry about it.
|
||||
// if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
|
||||
// std::stringstream ss;
|
||||
|
||||
// ss << L("Print will not fit and will be truncated!") << "\n"
|
||||
// << L("Width needed: ") << px(punsc) << " mm\n"
|
||||
// << L("Height needed: ") << py(punsc) << " mm\n";
|
||||
|
||||
// if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) {
|
||||
// scale_back();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// auto pri = ctl->create_progress_indicator(
|
||||
// 200, L("Slicing to zipped png files..."));
|
||||
|
||||
// pri->on_cancel([&print](){ print->cancel(); });
|
||||
|
||||
// try {
|
||||
// pri->update(0, L("Slicing..."));
|
||||
// slice(pri);
|
||||
// } catch (std::exception& e) {
|
||||
// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
|
||||
// scale_back();
|
||||
// if(print->canceled()) print->restart();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// auto initstate = unsigned(pri->state());
|
||||
// print->set_status_callback([pri, initstate](int st, const std::string& msg)
|
||||
// {
|
||||
// pri->update(initstate + unsigned(st), msg);
|
||||
// });
|
||||
|
||||
// try {
|
||||
// print_to<FilePrinterFormat::PNG, Zipper>( *print, exd.zippath,
|
||||
// exd.width_mm, exd.height_mm,
|
||||
// exd.width_px, exd.height_px,
|
||||
// exd.exp_time_s, exd.exp_time_first_s);
|
||||
|
||||
// } catch (std::exception& e) {
|
||||
// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
|
||||
// }
|
||||
|
||||
// scale_back();
|
||||
// if(print->canceled()) print->restart();
|
||||
// print->set_status_default();
|
||||
}
|
||||
|
||||
const PrintConfig &PrintController::config() const
|
||||
{
|
||||
return m_print->config();
|
||||
}
|
||||
|
||||
void ProgressIndicator::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()
|
||||
{
|
||||
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
|
||||
|
||||
auto ctl = GUI::get_appctl();
|
||||
|
||||
if(m_arranging.load()) return;
|
||||
|
||||
// to prevent UI reentrancies
|
||||
m_arranging.store(true);
|
||||
|
||||
unsigned count = 0;
|
||||
for(auto obj : m_model->objects) count += obj->instances.size();
|
||||
|
||||
auto pind = ctl->global_progress_indicator();
|
||||
|
||||
float pmax = 1.0;
|
||||
|
||||
if(pind) {
|
||||
pmax = pind->max();
|
||||
|
||||
// Set the range of the progress to the object count
|
||||
pind->max(count);
|
||||
|
||||
pind->on_cancel([this](){
|
||||
m_arranging.store(false);
|
||||
});
|
||||
}
|
||||
|
||||
auto dist = print_ctl()->config().min_object_distance();
|
||||
|
||||
// Create the arranger config
|
||||
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
|
||||
|
||||
auto& bedpoints = print_ctl()->config().bed_shape.values;
|
||||
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||
for(auto& v : bedpoints)
|
||||
bed.append(Point::new_scale(v(0), v(1)));
|
||||
|
||||
if(pind) pind->update(0, L("Arranging objects..."));
|
||||
|
||||
try {
|
||||
arr::BedShapeHint hint;
|
||||
// TODO: from Sasha from GUI
|
||||
hint.type = arr::BedShapeType::WHO_KNOWS;
|
||||
|
||||
arr::arrange(*m_model,
|
||||
min_obj_distance,
|
||||
bed,
|
||||
hint,
|
||||
false, // create many piles not just one pile
|
||||
[this, pind, &ctl, count](unsigned rem) {
|
||||
if(pind)
|
||||
pind->update(count - rem, L("Arranging objects..."));
|
||||
|
||||
ctl->process_events();
|
||||
}, [this] () { return !m_arranging.load(); });
|
||||
} catch(std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
ctl->report_issue(IssueType::ERR,
|
||||
L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid."),
|
||||
L("Exception occurred"));
|
||||
}
|
||||
|
||||
// Restore previous max value
|
||||
if(pind) {
|
||||
pind->max(pmax);
|
||||
pind->update(0, m_arranging.load() ? L("Arranging done.") :
|
||||
L("Arranging canceled."));
|
||||
|
||||
pind->on_cancel(/*remove cancel function*/);
|
||||
}
|
||||
|
||||
m_arranging.store(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,414 +0,0 @@
|
|||
#ifndef APPCONTROLLER_HPP
|
||||
#define APPCONTROLLER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
#include "GUI/ProgressIndicator.hpp"
|
||||
|
||||
#include <PrintConfig.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class Print;
|
||||
class PrintObject;
|
||||
class PrintConfig;
|
||||
class ProgressStatusBar;
|
||||
class DynamicPrintConfig;
|
||||
|
||||
/// A Progress indicator object smart pointer
|
||||
using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
|
||||
|
||||
using FilePath = std::string;
|
||||
using FilePathList = std::vector<FilePath>;
|
||||
|
||||
/// Common runtime issue types
|
||||
enum class IssueType {
|
||||
INFO,
|
||||
WARN,
|
||||
WARN_Q, // Warning with a question to continue
|
||||
ERR,
|
||||
FATAL
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 AppControllerBase {
|
||||
public:
|
||||
|
||||
using Ptr = std::shared_ptr<AppControllerBase>;
|
||||
|
||||
inline virtual ~AppControllerBase() {}
|
||||
|
||||
/**
|
||||
* @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 chosen by the user.
|
||||
*/
|
||||
virtual FilePathList query_destination_paths(
|
||||
const std::string& title,
|
||||
const std::string& extensions,
|
||||
const std::string& functionid = "",
|
||||
const std::string& hint = "") const = 0;
|
||||
|
||||
/**
|
||||
* @brief Same as query_destination_paths but works for directories only.
|
||||
*/
|
||||
virtual FilePathList query_destination_dirs(
|
||||
const std::string& title,
|
||||
const std::string& functionid = "",
|
||||
const std::string& hint = "") const = 0;
|
||||
|
||||
/**
|
||||
* @brief Same as query_destination_paths but returns only one path.
|
||||
*/
|
||||
virtual FilePath query_destination_path(
|
||||
const std::string& title,
|
||||
const std::string& extensions,
|
||||
const std::string& functionid = "",
|
||||
const std::string& hint = "") const = 0;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
virtual bool report_issue(IssueType issuetype,
|
||||
const std::string& description,
|
||||
const std::string& brief) = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the global progress indicator for the current controller.
|
||||
* Can be empty as well.
|
||||
*
|
||||
* Only one thread should use the global indicator at a time.
|
||||
*/
|
||||
virtual ProgresIndicatorPtr global_progress_indicator() = 0;
|
||||
|
||||
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) = 0;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
virtual bool is_main_thread() const = 0;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
virtual bool supports_asynch() const = 0;
|
||||
|
||||
virtual void process_events() = 0;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
virtual ProgresIndicatorPtr create_progress_indicator(
|
||||
unsigned statenum,
|
||||
const std::string& title,
|
||||
const std::string& firstmsg = "") const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implementation of AppControllerBase for the GUI app
|
||||
*/
|
||||
class AppControllerGui: public AppControllerBase {
|
||||
private:
|
||||
class PriData; // Some structure to store progress indication data
|
||||
|
||||
// Pimpl data for thread safe progress indication features
|
||||
std::unique_ptr<PriData> m_pri_data;
|
||||
|
||||
public:
|
||||
|
||||
AppControllerGui();
|
||||
|
||||
virtual ~AppControllerGui();
|
||||
|
||||
virtual FilePathList query_destination_paths(
|
||||
const std::string& title,
|
||||
const std::string& extensions,
|
||||
const std::string& functionid,
|
||||
const std::string& hint) const override;
|
||||
|
||||
virtual FilePathList query_destination_dirs(
|
||||
const std::string& /*title*/,
|
||||
const std::string& /*functionid*/,
|
||||
const std::string& /*hint*/) const override { return {}; }
|
||||
|
||||
virtual FilePath query_destination_path(
|
||||
const std::string& title,
|
||||
const std::string& extensions,
|
||||
const std::string& functionid,
|
||||
const std::string& hint) const override;
|
||||
|
||||
virtual bool report_issue(IssueType issuetype,
|
||||
const std::string& description,
|
||||
const std::string& brief = std::string()) override;
|
||||
|
||||
virtual ProgresIndicatorPtr global_progress_indicator() override;
|
||||
|
||||
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) override;
|
||||
|
||||
virtual bool is_main_thread() const override;
|
||||
|
||||
virtual bool supports_asynch() const override;
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
virtual ProgresIndicatorPtr create_progress_indicator(
|
||||
unsigned statenum,
|
||||
const std::string& title,
|
||||
const std::string& firstmsg) const override;
|
||||
|
||||
protected:
|
||||
|
||||
// This is a global progress indicator placeholder. In the Slic3r UI it can
|
||||
// contain the progress indicator on the statusbar.
|
||||
ProgresIndicatorPtr m_global_progressind;
|
||||
};
|
||||
|
||||
class AppControllerCli: public AppControllerBase {
|
||||
|
||||
class CliProgress : public ProgressIndicator {
|
||||
std::string m_msg, m_title;
|
||||
public:
|
||||
virtual void message(const std::string& msg) override {
|
||||
m_msg = msg;
|
||||
}
|
||||
|
||||
virtual void title(const std::string& title) override {
|
||||
m_title = title;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
AppControllerCli() {
|
||||
std::cout << "Cli AppController ready..." << std::endl;
|
||||
m_global_progressind = std::make_shared<CliProgress>();
|
||||
}
|
||||
|
||||
virtual ~AppControllerCli() {}
|
||||
|
||||
virtual FilePathList query_destination_paths(
|
||||
const std::string& /*title*/,
|
||||
const std::string& /*extensions*/,
|
||||
const std::string& /*functionid*/,
|
||||
const std::string& /*hint*/) const override { return {}; }
|
||||
|
||||
virtual FilePathList query_destination_dirs(
|
||||
const std::string& /*title*/,
|
||||
const std::string& /*functionid*/,
|
||||
const std::string& /*hint*/) const override { return {}; }
|
||||
|
||||
virtual FilePath query_destination_path(
|
||||
const std::string& /*title*/,
|
||||
const std::string& /*extensions*/,
|
||||
const std::string& /*functionid*/,
|
||||
const std::string& /*hint*/) const override { return "out.zip"; }
|
||||
|
||||
virtual bool report_issue(IssueType /*issuetype*/,
|
||||
const std::string& description,
|
||||
const std::string& brief) override {
|
||||
std::cerr << brief << ": " << description << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual ProgresIndicatorPtr global_progress_indicator() override {
|
||||
return m_global_progressind;
|
||||
}
|
||||
|
||||
virtual void global_progress_indicator(ProgresIndicatorPtr) override {}
|
||||
|
||||
virtual bool is_main_thread() const override { return true; }
|
||||
|
||||
virtual bool supports_asynch() const override { return false; }
|
||||
|
||||
virtual void process_events() override {}
|
||||
|
||||
virtual ProgresIndicatorPtr create_progress_indicator(
|
||||
unsigned /*statenum*/,
|
||||
const std::string& /*title*/,
|
||||
const std::string& /*firstmsg*/) const override {
|
||||
return std::make_shared<CliProgress>();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// This is a global progress indicator placeholder. In the Slic3r UI it can
|
||||
// contain the progress indicator on the statusbar.
|
||||
ProgresIndicatorPtr m_global_progressind;
|
||||
};
|
||||
|
||||
class Zipper {
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
public:
|
||||
|
||||
Zipper(const std::string& zipfilepath);
|
||||
~Zipper();
|
||||
|
||||
void next_entry(const std::string& fname);
|
||||
|
||||
std::string get_name() const;
|
||||
|
||||
std::ostream& stream();
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implementation of the printing logic.
|
||||
*/
|
||||
class PrintController {
|
||||
Print *m_print = nullptr;
|
||||
std::function<void()> m_rempools;
|
||||
protected:
|
||||
|
||||
// Data structure with the png export input data
|
||||
struct PngExportData {
|
||||
std::string zippath; // output zip file
|
||||
unsigned long width_px = 1440; // resolution - rows
|
||||
unsigned long height_px = 2560; // resolution columns
|
||||
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
|
||||
double exp_time_first_s = 35.0; // first exposure time
|
||||
double exp_time_s = 8.0; // global exposure time
|
||||
double corr_x = 1.0; // offsetting in x
|
||||
double corr_y = 1.0; // offsetting in y
|
||||
double corr_z = 1.0; // offsetting in y
|
||||
};
|
||||
|
||||
// Should display a dialog with the input fields for printing to png
|
||||
PngExportData query_png_export_data(const DynamicPrintConfig&);
|
||||
|
||||
// The previous export data, to pre-populate the dialog
|
||||
PngExportData m_prev_expdata;
|
||||
|
||||
void slice(ProgresIndicatorPtr pri);
|
||||
|
||||
public:
|
||||
|
||||
// Must be public for perl to use it
|
||||
explicit inline PrintController(Print *print): m_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 the loaded print scene.
|
||||
*/
|
||||
void slice();
|
||||
|
||||
/**
|
||||
* @brief Slice the print into zipped png files.
|
||||
*/
|
||||
void slice_to_png();
|
||||
|
||||
const PrintConfig& config() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Top level controller.
|
||||
*/
|
||||
class AppController {
|
||||
Model *m_model = nullptr;
|
||||
PrintController::Ptr printctl;
|
||||
std::atomic<bool> m_arranging;
|
||||
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) { m_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* ProgressIndicator 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(ProgressStatusBar *prs);
|
||||
|
||||
void arrange_model();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // APPCONTROLLER_HPP
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
#include "AppController.hpp"
|
||||
|
||||
#include <wx/stdstream.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#include <thread>
|
||||
#include <future>
|
||||
|
||||
#include <slic3r/GUI/GUI.hpp>
|
||||
#include <slic3r/GUI/ProgressStatusBar.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 AppControllerGui::supports_asynch() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppControllerGui::process_events()
|
||||
{
|
||||
wxYieldIfNeeded();
|
||||
}
|
||||
|
||||
FilePathList AppControllerGui::query_destination_paths(
|
||||
const std::string &title,
|
||||
const std::string &extensions,
|
||||
const std::string &/*functionid*/,
|
||||
const std::string& hint) const
|
||||
{
|
||||
|
||||
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
|
||||
dlg.SetWildcard(extensions);
|
||||
|
||||
dlg.SetFilename(hint);
|
||||
|
||||
FilePathList ret;
|
||||
|
||||
if(dlg.ShowModal() == wxID_OK) {
|
||||
wxArrayString paths;
|
||||
dlg.GetPaths(paths);
|
||||
for(auto& p : paths) ret.push_back(p.ToStdString());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FilePath AppControllerGui::query_destination_path(
|
||||
const std::string &title,
|
||||
const std::string &extensions,
|
||||
const std::string &/*functionid*/,
|
||||
const std::string& hint) const
|
||||
{
|
||||
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
|
||||
dlg.SetWildcard(extensions);
|
||||
|
||||
dlg.SetFilename(hint);
|
||||
|
||||
FilePath ret;
|
||||
|
||||
if(dlg.ShowModal() == wxID_OK) {
|
||||
ret = FilePath(dlg.GetPath());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AppControllerGui::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);
|
||||
|
||||
struct Zipper::Impl {
|
||||
wxFileName fpath;
|
||||
wxFFileOutputStream zipfile;
|
||||
wxZipOutputStream zipstream;
|
||||
wxStdOutputStream pngstream;
|
||||
|
||||
Impl(const std::string& zipfile_path):
|
||||
fpath(zipfile_path),
|
||||
zipfile(zipfile_path),
|
||||
zipstream(zipfile),
|
||||
pngstream(zipstream)
|
||||
{
|
||||
if(!zipfile.IsOk())
|
||||
throw std::runtime_error(L("Cannot create zip file."));
|
||||
}
|
||||
};
|
||||
|
||||
Zipper::Zipper(const std::string &zipfilepath)
|
||||
{
|
||||
m_impl.reset(new Impl(zipfilepath));
|
||||
}
|
||||
|
||||
Zipper::~Zipper() {}
|
||||
|
||||
void Zipper::next_entry(const std::string &fname)
|
||||
{
|
||||
m_impl->zipstream.PutNextEntry(fname);
|
||||
}
|
||||
|
||||
std::string Zipper::get_name() const
|
||||
{
|
||||
return m_impl->fpath.GetName().ToStdString();
|
||||
}
|
||||
|
||||
std::ostream &Zipper::stream()
|
||||
{
|
||||
return m_impl->pngstream;
|
||||
}
|
||||
|
||||
void Zipper::close()
|
||||
{
|
||||
m_impl->zipstream.Close();
|
||||
m_impl->zipfile.Close();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* A simple thread safe progress dialog implementation that can be used from
|
||||
* the main thread as well.
|
||||
*/
|
||||
class GuiProgressIndicator:
|
||||
public ProgressIndicator, public wxEvtHandler {
|
||||
|
||||
wxProgressDialog m_gauge;
|
||||
using Base = ProgressIndicator;
|
||||
wxString m_message;
|
||||
int m_range; wxString m_title;
|
||||
bool m_is_asynch = false;
|
||||
|
||||
const int m_id = wxWindow::NewControlId();
|
||||
|
||||
// status update handler
|
||||
void _state( wxCommandEvent& evt) {
|
||||
unsigned st = evt.GetInt();
|
||||
m_message = evt.GetString();
|
||||
_state(st);
|
||||
}
|
||||
|
||||
// Status update implementation
|
||||
void _state( unsigned st) {
|
||||
if(!m_gauge.IsShown()) m_gauge.ShowModal();
|
||||
Base::state(st);
|
||||
if(!m_gauge.Update(static_cast<int>(st), m_message)) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Setting whether it will be used from the UI thread or some worker thread
|
||||
inline void asynch(bool is) { m_is_asynch = is; }
|
||||
|
||||
/// Get the mode of parallel operation.
|
||||
inline bool asynch() const { return m_is_asynch; }
|
||||
|
||||
inline GuiProgressIndicator(int range, const wxString& title,
|
||||
const wxString& firstmsg) :
|
||||
m_gauge(title, firstmsg, range, wxTheApp->GetTopWindow(),
|
||||
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT),
|
||||
|
||||
m_message(firstmsg),
|
||||
m_range(range), m_title(title)
|
||||
{
|
||||
Base::max(static_cast<float>(range));
|
||||
Base::states(static_cast<unsigned>(range));
|
||||
|
||||
Bind(PROGRESS_STATUS_UPDATE_EVENT,
|
||||
&GuiProgressIndicator::_state,
|
||||
this, m_id);
|
||||
}
|
||||
|
||||
virtual void state(float val) override {
|
||||
state(static_cast<unsigned>(val));
|
||||
}
|
||||
|
||||
void state(unsigned st) {
|
||||
// send status update event
|
||||
if(m_is_asynch) {
|
||||
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, m_id);
|
||||
evt->SetInt(st);
|
||||
evt->SetString(m_message);
|
||||
wxQueueEvent(this, evt);
|
||||
} else _state(st);
|
||||
}
|
||||
|
||||
virtual void message(const std::string & msg) override {
|
||||
m_message = _(msg);
|
||||
}
|
||||
|
||||
virtual void messageFmt(const std::string& fmt, ...) {
|
||||
va_list arglist;
|
||||
va_start(arglist, fmt);
|
||||
m_message = wxString::Format(_(fmt), arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
virtual void title(const std::string & title) override {
|
||||
m_title = _(title);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ProgresIndicatorPtr AppControllerGui::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 {
|
||||
|
||||
class Wrapper: public ProgressIndicator, public wxEvtHandler {
|
||||
ProgressStatusBar *m_sbar;
|
||||
using Base = ProgressIndicator;
|
||||
wxString m_message;
|
||||
AppControllerBase& m_ctl;
|
||||
|
||||
void showProgress(bool show = true) {
|
||||
m_sbar->show_progress(show);
|
||||
}
|
||||
|
||||
void _state(unsigned st) {
|
||||
if( st <= ProgressIndicator::max() ) {
|
||||
Base::state(st);
|
||||
m_sbar->set_status_text(m_message);
|
||||
m_sbar->set_progress(st);
|
||||
}
|
||||
}
|
||||
|
||||
// status update handler
|
||||
void _state( wxCommandEvent& evt) {
|
||||
unsigned st = evt.GetInt(); _state(st);
|
||||
}
|
||||
|
||||
const int id_ = wxWindow::NewControlId();
|
||||
|
||||
public:
|
||||
|
||||
inline Wrapper(ProgressStatusBar *sbar,
|
||||
AppControllerBase& ctl):
|
||||
m_sbar(sbar), m_ctl(ctl)
|
||||
{
|
||||
Base::max(static_cast<float>(m_sbar->get_range()));
|
||||
Base::states(static_cast<unsigned>(m_sbar->get_range()));
|
||||
|
||||
Bind(PROGRESS_STATUS_UPDATE_EVENT,
|
||||
&Wrapper::_state,
|
||||
this, id_);
|
||||
}
|
||||
|
||||
virtual void state(float val) override {
|
||||
state(unsigned(val));
|
||||
}
|
||||
|
||||
virtual void max(float val) override {
|
||||
if(val > 1.0) {
|
||||
m_sbar->set_range(static_cast<int>(val));
|
||||
ProgressIndicator::max(val);
|
||||
}
|
||||
}
|
||||
|
||||
void state(unsigned st) {
|
||||
if(!m_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 {
|
||||
m_message = _(msg);
|
||||
}
|
||||
|
||||
virtual void message_fmt(const std::string& fmt, ...) override {
|
||||
va_list arglist;
|
||||
va_start(arglist, fmt);
|
||||
m_message = wxString::Format(_(fmt), arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
virtual void title(const std::string & /*title*/) override {}
|
||||
|
||||
virtual void on_cancel(CancelFn fn) override {
|
||||
m_sbar->set_cancel_callback(fn);
|
||||
Base::on_cancel(fn);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
void AppController::set_global_progress_indicator(ProgressStatusBar *prsb)
|
||||
{
|
||||
if(prsb) {
|
||||
auto ctl = GUI::get_appctl();
|
||||
ctl->global_progress_indicator(std::make_shared<Wrapper>(prsb, *ctl));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -121,9 +121,6 @@ add_library(libslic3r_gui STATIC
|
|||
Utils/Time.hpp
|
||||
Utils/HexFile.cpp
|
||||
Utils/HexFile.hpp
|
||||
AppController.hpp
|
||||
AppController.cpp
|
||||
AppControllerWx.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "../AppController.hpp"
|
||||
#include "WipeTowerDialog.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
|
@ -453,23 +452,4 @@ void desktop_open_datadir_folder()
|
|||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
AppControllerPtr g_appctl;
|
||||
}
|
||||
|
||||
AppControllerPtr get_appctl()
|
||||
{
|
||||
return g_appctl;
|
||||
}
|
||||
|
||||
void set_cli_appctl()
|
||||
{
|
||||
g_appctl = std::make_shared<AppControllerCli>();
|
||||
}
|
||||
|
||||
void set_gui_appctl()
|
||||
{
|
||||
g_appctl = std::make_shared<AppControllerGui>();
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include "Tab.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "../AppController.hpp"
|
||||
#include "ProgressStatusBar.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "Print.hpp"
|
||||
|
|
@ -30,8 +29,6 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
m_no_plater(no_plater),
|
||||
m_loaded(loaded)
|
||||
{
|
||||
m_appController = new Slic3r::AppController();
|
||||
|
||||
// Load the icon either from the exe, or from the ico file.
|
||||
#if _WIN32
|
||||
{
|
||||
|
|
@ -59,14 +56,6 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
SLIC3R_VERSION +
|
||||
_(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
|
||||
|
||||
m_appController->set_model(&m_plater->model());
|
||||
m_appController->set_print(&m_plater->print());
|
||||
|
||||
GUI::set_gui_appctl();
|
||||
|
||||
// Make the global status bar and its progress indicator available in C++
|
||||
m_appController->set_global_progress_indicator(m_statusbar);
|
||||
|
||||
m_loaded = true;
|
||||
|
||||
// initialize layout
|
||||
|
|
@ -373,7 +362,7 @@ void MainFrame::slice_to_png()
|
|||
{
|
||||
// m_plater->stop_background_process();
|
||||
// m_plater->async_apply_config();
|
||||
m_appController->print_ctl()->slice_to_png();
|
||||
// m_appController->print_ctl()->slice_to_png();
|
||||
}
|
||||
|
||||
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ class wxProgressDialog;
|
|||
namespace Slic3r {
|
||||
|
||||
class ProgressStatusBar;
|
||||
class AppController;
|
||||
|
||||
// #define _(s) Slic3r::GUI::I18N::translate((s))
|
||||
|
||||
|
|
@ -54,7 +53,6 @@ class MainFrame : public wxFrame
|
|||
wxString m_qs_last_output_file = wxEmptyString;
|
||||
wxString m_last_config = wxEmptyString;
|
||||
|
||||
AppController* m_appController { nullptr };
|
||||
std::map<std::string, Tab*> m_options_tabs;
|
||||
|
||||
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
||||
|
|
@ -97,8 +95,6 @@ public:
|
|||
void select_tab(size_t tab) const;
|
||||
void select_view(const std::string& direction);
|
||||
|
||||
AppController* app_controller() { return m_appController; }
|
||||
|
||||
std::vector<PresetTab>& get_preset_tabs();
|
||||
|
||||
Plater* m_plater { nullptr };
|
||||
|
|
@ -110,4 +106,4 @@ public:
|
|||
} // GUI
|
||||
} //Slic3r
|
||||
|
||||
#endif // slic3r_MainFrame_hpp_
|
||||
#endif // slic3r_MainFrame_hpp_
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/GCode/PreviewData.hpp"
|
||||
|
|
@ -33,7 +34,7 @@
|
|||
#include "libslic3r/Format/STL.hpp"
|
||||
#include "libslic3r/Format/AMF.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
#include "slic3r/AppController.hpp"
|
||||
//#include "slic3r/AppController.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
|
|
@ -863,6 +864,7 @@ struct Plater::priv
|
|||
wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can
|
||||
Preview *preview;
|
||||
BackgroundSlicingProcess background_process;
|
||||
std::atomic<bool> arranging;
|
||||
|
||||
wxTimer background_process_timer;
|
||||
|
||||
|
|
@ -1446,13 +1448,86 @@ void Plater::priv::mirror(Axis axis)
|
|||
|
||||
void Plater::priv::arrange()
|
||||
{
|
||||
this->background_process.stop();
|
||||
main_frame->app_controller()->arrange_model();
|
||||
// don't do anything if currently arranging. Then this is a re-entrance
|
||||
if(arranging.load()) return;
|
||||
|
||||
// Guard the arrange process
|
||||
arranging.store(true);
|
||||
|
||||
_3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange());
|
||||
|
||||
this->background_process.stop();
|
||||
unsigned count = 0;
|
||||
for(auto obj : model.objects) count += obj->instances.size();
|
||||
|
||||
auto prev_range = statusbar()->get_range();
|
||||
statusbar()->set_range(count);
|
||||
|
||||
auto statusfn = [this, count] (unsigned st, const std::string& msg) {
|
||||
/* // In case we would run the arrange asynchronously
|
||||
wxCommandEvent event(EVT_PROGRESS_BAR);
|
||||
event.SetInt(st);
|
||||
event.SetString(msg);
|
||||
wxQueueEvent(this->q, event.Clone()); */
|
||||
statusbar()->set_progress(count - st);
|
||||
statusbar()->set_status_text(msg);
|
||||
|
||||
// ok, this is dangerous, but we are protected by the atomic flag
|
||||
// 'arranging'. This call is needed for the cancel button to work.
|
||||
wxYieldIfNeeded();
|
||||
};
|
||||
|
||||
statusbar()->set_cancel_callback([this, statusfn](){
|
||||
arranging.store(false);
|
||||
statusfn(0, L("Arranging canceled"));
|
||||
});
|
||||
|
||||
static const std::string arrangestr = L("Arranging");
|
||||
|
||||
// FIXME: I don't know how to obtain the minimum distance, it depends
|
||||
// on printer technology. I guess the following should work but it crashes.
|
||||
double dist = 6; //PrintConfig::min_object_distance(config);
|
||||
|
||||
auto min_obj_distance = static_cast<coord_t>(dist/SCALING_FACTOR);
|
||||
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
|
||||
assert(bed_shape_opt);
|
||||
auto& bedpoints = bed_shape_opt->values;
|
||||
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
|
||||
statusfn(0, arrangestr);
|
||||
|
||||
try {
|
||||
arr::BedShapeHint hint;
|
||||
|
||||
// TODO: from Sasha from GUI or
|
||||
hint.type = arr::BedShapeType::WHO_KNOWS;
|
||||
|
||||
arr::arrange(model,
|
||||
min_obj_distance,
|
||||
bed,
|
||||
hint,
|
||||
false, // create many piles not just one pile
|
||||
[statusfn](unsigned st) { statusfn(st, arrangestr); },
|
||||
[this] () { return !arranging.load(); });
|
||||
} catch(std::exception& /*e*/) {
|
||||
GUI::show_error(this->q, L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid."));
|
||||
}
|
||||
|
||||
statusfn(0, L("Arranging done."));
|
||||
statusbar()->set_range(prev_range);
|
||||
statusbar()->set_cancel_callback(); // remove cancel button
|
||||
arranging.store(false);
|
||||
|
||||
this->schedule_background_process();
|
||||
|
||||
// ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
|
||||
// when parts don't fit in print bed
|
||||
// ignore arrange failures on purpose: user has visual feedback and we
|
||||
// don't need to warn him when parts don't fit in print bed
|
||||
|
||||
_3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange());
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -1908,7 +1983,7 @@ bool Plater::priv::can_delete_all() const
|
|||
|
||||
bool Plater::priv::can_arrange() const
|
||||
{
|
||||
return !model.objects.empty();
|
||||
return !model.objects.empty() && !arranging.load();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_mirror() const
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue