WIP: Reconstruction of background processing.

This commit is contained in:
bubnikv 2018-10-23 15:27:31 +02:00
parent f33713e060
commit bded28f888
17 changed files with 273 additions and 229 deletions

View file

@ -242,6 +242,14 @@ BoundingBoxf3 Model::bounding_box() const
return bb;
}
unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
{
unsigned int num_printable = 0;
for (ModelObject *model_object : this->objects)
num_printable += model_object->check_instances_print_volume_state(print_volume);
return num_printable;
}
void Model::center_instances_around_point(const Vec2d &point)
{
BoundingBoxf3 bb;
@ -875,26 +883,32 @@ void ModelObject::repair()
v->mesh.repair();
}
// Called by Print::validate() from the UI thread.
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
for (const ModelVolume* vol : this->volumes)
{
if (vol->is_model_part())
{
for (ModelInstance* inst : this->instances)
{
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(inst->world_matrix());
unsigned int num_printable = 0;
enum {
INSIDE = 1,
OUTSIDE = 2
};
for (ModelInstance *model_instance : this->instances) {
unsigned int inside_outside = 0;
for (const ModelVolume *vol : this->volumes)
if (vol->is_model_part()) {
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->world_matrix());
if (print_volume.contains(bb))
inst->print_volume_state = ModelInstance::PVS_Inside;
inside_outside |= INSIDE;
else if (print_volume.intersects(bb))
inst->print_volume_state = ModelInstance::PVS_Partly_Outside;
inside_outside |= INSIDE | OUTSIDE;
else
inst->print_volume_state = ModelInstance::PVS_Fully_Outside;
inside_outside |= OUTSIDE;
}
}
model_instance->print_volume_state =
(inside_outside == (INSIDE | OUTSIDE)) ? ModelInstance::PVS_Partly_Outside :
(inside_outside == INSIDE) ? ModelInstance::PVS_Inside : ModelInstance::PVS_Fully_Outside;
if (inside_outside == INSIDE)
++ num_printable;
}
return num_printable;
}
void ModelObject::print_info() const

View file

@ -156,7 +156,7 @@ public:
void repair();
// Called by Print::validate() from the UI thread.
void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
// Print object statistics to console.
void print_info() const;
@ -411,6 +411,9 @@ public:
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
// return total number of printable objects.
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
void center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;

View file

@ -852,7 +852,11 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in)
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_BLOCKER);
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ) {
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
model_object.layer_height_profile != model_object_new.layer_height_profile ||
model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
@ -889,6 +893,9 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in)
it->print_object->config_apply_only(new_config, diff, true);
}
}
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.instances = model_object_new.instances;
}
}
@ -936,11 +943,10 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in)
print_objects_new.emplace_back(print_object);
print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
} else if ((*it_old)->print_object->copies() != new_instances.copies) {
// The PrintObject already exists and the copies differ. The only step currently sensitive to the order is the G-code generator.
// Stop it.
this->invalidate_step(psGCodeExport);
// The PrintObject already exists and the copies differ.
(*it_old)->print_object->set_copies(new_instances.copies);
}
print_objects_new.emplace_back((*it_old)->print_object);
}
}
}
if (m_objects != print_objects_new) {
@ -958,7 +964,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in)
int idx_region = 0;
for (const auto &volumes : print_object->region_volumes) {
if (! volumes.empty())
++ m_regions[idx_region];
++ m_regions[idx_region]->m_refcnt;
++ idx_region;
}
}
@ -1110,23 +1116,7 @@ bool Print::has_skirt() const
std::string Print::validate() const
{
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(m_config.bed_shape.values));
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(m_config.max_print_height)));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.min(2) = -1e10;
unsigned int printable_count = 0;
{
// Lock due to the po->reload_model_instances()
tbb::mutex::scoped_lock lock(m_mutex);
for (PrintObject *po : m_objects) {
po->model_object()->check_instances_print_volume_state(print_volume);
po->reload_model_instances();
if (po->is_printable())
++ printable_count;
}
}
if (printable_count == 0)
if (m_objects.empty())
return L("All objects are outside of the print volume.");
if (m_config.complete_objects) {

View file

@ -96,6 +96,7 @@ public:
#endif
mtx.unlock();
cancel();
m_state[step] = INVALID;
mtx.lock();
}
return invalidated;

View file

@ -87,23 +87,25 @@ bool PrintObject::delete_last_copy()
bool PrintObject::set_copies(const Points &points)
{
bool copies_num_changed = m_copies.size() != points.size();
// order copies with a nearest neighbor search and translate them by _copies_shift
m_copies.clear();
m_copies.reserve(points.size());
// order copies with a nearest-neighbor search
std::vector<Points::size_type> ordered_copies;
Slic3r::Geometry::chained_path(points, ordered_copies);
for (size_t point_idx : ordered_copies)
m_copies.push_back(points[point_idx] + m_copies_shift);
bool invalidated = m_print->invalidate_step(psSkirt);
invalidated |= m_print->invalidate_step(psBrim);
if (copies_num_changed)
invalidated |= m_print->invalidate_step(psWipeTower);
// Order copies with a nearest-neighbor search.
std::vector<Point> copies;
{
std::vector<Points::size_type> ordered_copies;
Slic3r::Geometry::chained_path(points, ordered_copies);
copies.reserve(ordered_copies.size());
for (size_t point_idx : ordered_copies)
copies.emplace_back(points[point_idx] + m_copies_shift);
}
// Invalidate and set copies.
bool invalidated = false;
if (copies != m_copies) {
invalidated = m_print->invalidate_step(psSkirt);
invalidated |= m_print->invalidate_step(psBrim);
if (copies.size() != m_copies.size())
invalidated |= m_print->invalidate_step(psWipeTower);
invalidated |= m_print->invalidate_step(psGCodeExport);
m_copies = copies;
}
return invalidated;
}

View file

@ -858,12 +858,12 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z); // first layer, whose slice_z is > max_z
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()) - 1);
#endif /* SLIC3R_TRIANGLEMESH_DEBUG */
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
for (std::vector<float>::const_iterator it = min_layer; it != max_layer; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
IntersectionLine il;
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {

View file

@ -57,12 +57,20 @@ void BackgroundSlicingProcess::thread_proc()
if (! m_print->canceled()) {
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_sliced_id));
m_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
if (! m_print->canceled() && ! m_output_path.empty()) {
if (copy_file(m_temp_output_path, m_output_path) != 0)
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
m_print->set_status(95, "Running post-processing scripts");
run_post_process_scripts(m_output_path, m_print->config());
}
if (! m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) {
this->set_step_started(bspsGCodeFinalize);
if (! m_export_path.empty()) {
//FIXME localize the messages
if (copy_file(m_temp_output_path, m_export_path) != 0)
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
m_print->set_status(95, "Running post-processing scripts");
run_post_process_scripts(m_export_path, m_print->config());
m_print->set_status(100, "G-code file exported to " + m_export_path);
} else {
m_print->set_status(100, "Slicing complete");
}
this->set_step_done(bspsGCodeFinalize);
}
}
} catch (CanceledException &ex) {
// Canceled, this is all right.
@ -133,7 +141,7 @@ bool BackgroundSlicingProcess::stop()
{
std::unique_lock<std::mutex> lck(m_mutex);
if (m_state == STATE_INITIAL) {
this->m_output_path.clear();
// this->m_export_path.clear();
return false;
}
// assert(this->running());
@ -147,7 +155,7 @@ bool BackgroundSlicingProcess::stop()
// In the "Finished" or "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE;
}
this->m_output_path.clear();
// this->m_export_path.clear();
return true;
}
@ -169,4 +177,53 @@ bool BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfi
return invalidated;
}
// Set the output path of the G-code.
void BackgroundSlicingProcess::schedule_export(const std::string &path)
{
assert(m_export_path.empty());
if (! m_export_path.empty())
return;
// Guard against entering the export step before changing the export path.
tbb::mutex::scoped_lock lock(m_step_state_mutex);
this->invalidate_step(bspsGCodeFinalize);
m_export_path = path;
}
void BackgroundSlicingProcess::reset_export()
{
assert(! this->running());
if (! this->running()) {
m_export_path.clear();
// invalidate_step expects the mutex to be locked.
tbb::mutex::scoped_lock lock(m_step_state_mutex);
this->invalidate_step(bspsGCodeFinalize);
}
}
void BackgroundSlicingProcess::set_step_started(BackgroundSlicingProcessStep step)
{
m_step_state.set_started(step, m_step_state_mutex);
if (m_print->canceled())
throw CanceledException();
}
void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step)
{
m_step_state.set_done(step, m_step_state_mutex);
if (m_print->canceled())
throw CanceledException();
}
bool BackgroundSlicingProcess::invalidate_step(BackgroundSlicingProcessStep step)
{
bool invalidated = m_step_state.invalidate(step, m_step_state_mutex, [this](){ this->stop(); });
return invalidated;
}
bool BackgroundSlicingProcess::invalidate_all_steps()
{
return m_step_state.invalidate_all(m_step_state_mutex, [this](){ this->stop(); });
}
}; // namespace Slic3r

View file

@ -6,6 +6,8 @@
#include <mutex>
#include <thread>
#include "Print.hpp"
namespace Slic3r {
class DynamicPrintConfig;
@ -13,6 +15,11 @@ class GCodePreviewData;
class Model;
class Print;
// Print step IDs for keeping track of the print state.
enum BackgroundSlicingProcessStep {
bspsGCodeFinalize, bspsCount,
};
// Support for the GUI background processing (Slicing and G-code generation).
// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits.
class BackgroundSlicingProcess
@ -32,8 +39,6 @@ public:
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_finished_event(int event_id) { m_event_finished_id = event_id; }
// Set the output path of the G-code.
void set_output_path(const std::string &path) { m_output_path = path; }
// Start the background processing. Returns false if the background processing was already running.
bool start();
// Cancel the background processing. Returns false if the background processing was not running.
@ -46,6 +51,13 @@ public:
// Apply config over the print. Returns false, if the new config values caused any of the already
// processed steps to be invalidated, therefore the task will need to be restarted.
bool apply(const Model &model, const DynamicPrintConfig &config);
// Set the export path of the G-code.
// Once the path is set, the G-code
void schedule_export(const std::string &path);
// Clear m_export_path.
void reset_export();
// Once the G-code export is scheduled, the apply() methods will do nothing.
bool is_export_scheduled() const { return ! m_export_path.empty(); }
enum State {
// m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet).
@ -74,8 +86,11 @@ private:
Print *m_print = nullptr;
// Data structure, to which the G-code export writes its annotations.
GCodePreviewData *m_gcode_preview_data = nullptr;
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
std::string m_temp_output_path;
std::string m_output_path;
// Output path provided by the user. The output path may be set even if the slicing is running,
// but once set, it cannot be re-set.
std::string m_export_path;
// Thread, on which the background processing is executed. The thread will always be present
// and ready to execute the slicing process.
std::thread m_thread;
@ -84,6 +99,14 @@ private:
std::condition_variable m_condition;
State m_state = STATE_INITIAL;
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
mutable tbb::mutex m_step_state_mutex;
void set_step_started(BackgroundSlicingProcessStep step);
void set_step_done(BackgroundSlicingProcessStep step);
bool is_step_done(BackgroundSlicingProcessStep step) const { return m_step_state.is_done(step); }
bool invalidate_step(BackgroundSlicingProcessStep step);
bool invalidate_all_steps();
// wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue.
int m_event_sliced_id = 0;
// wxWidgets command ID to be sent to the platter to inform that the task finished.

View file

@ -5145,7 +5145,7 @@ void GLCanvas3D::_render_layer_editing_overlay() const
#else
int object_idx = int(volume->select_group_id / 1000000);
#endif // ENABLE_EXTENDED_SELECTION
if ((int)m_print->objects().size() < object_idx)
if ((int)m_print->objects().size() <= object_idx)
return;
const PrintObject* print_object = m_print->get_object(object_idx);

View file

@ -261,18 +261,6 @@ void warning_catcher(wxWindow* parent, const wxString& message){
msg.ShowModal();
}
// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
// to deliver a progress status message.
void set_print_callback_event(Print *print, int id)
{
print->set_status_callback([id](int percent, const std::string &message){
wxCommandEvent event(id);
event.SetInt(percent);
event.SetString(message);
wxQueueEvent(wxGetApp().mainframe, event.Clone());
});
}
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
{
if (comboCtrl == nullptr)

View file

@ -80,10 +80,6 @@ void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title);
void warning_catcher(wxWindow* parent, const wxString& message);
// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
// to deliver a progress status message.
void set_print_callback_event(Print *print, int id);
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
// Items are all initialized to the given value.
// Items must be separated by '|', for example "Item1|Item2|Item3", and so on.

View file

@ -63,10 +63,10 @@ namespace Slic3r {
namespace GUI {
wxDEFINE_EVENT(EVT_PROGRESS_BAR, wxCommandEvent);
wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent);
// Sidebar widgets
// struct InfoBox : public wxStaticBox
@ -741,9 +741,6 @@ struct Plater::priv
std::vector<PlaterObject> objects;
#endif // !ENABLE_EXTENDED_SELECTION
fs::path export_gcode_output_file;
fs::path send_gcode_file;
// GUI elements
wxNotebook *notebook;
Sidebar *sidebar;
@ -809,7 +806,7 @@ struct Plater::priv
void on_notebook_changed(wxBookCtrlEvent&);
void on_select_preset(wxCommandEvent&);
void on_progress_event();
void on_progress_event(wxCommandEvent&);
void on_update_print_preview(wxCommandEvent&);
void on_process_completed(wxCommandEvent&);
void on_layer_editing_toggled(bool enable);
@ -880,6 +877,14 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
background_process.set_gcode_preview_data(&gcode_preview_data);
background_process.set_sliced_event(EVT_SLICING_COMPLETED);
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
// Register progress callback from the Print class to the Platter.
print.set_status_callback([this](int percent, const std::string &message){
wxCommandEvent event(EVT_PROGRESS_BAR);
event.SetInt(percent);
event.SetString(message);
wxQueueEvent(this->q, event.Clone());
});
this->q->Bind(EVT_PROGRESS_BAR, &priv::on_progress_event, this);
_3DScene::add_canvas(canvas3D);
_3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample());
@ -902,7 +907,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
_3DScene::enable_shader(canvas3D, true);
_3DScene::enable_force_zoom_to_bed(canvas3D, true);
background_process_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent &evt){ this->async_apply_config(); }, 0);
this->background_process_timer.SetOwner(this->q, 0);
this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt){ this->async_apply_config(); });
auto *bed_shape = config->opt<ConfigOptionPoints>("bed_shape");
_3DScene::set_bed_shape(canvas3D, bed_shape->values);
@ -938,7 +944,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
canvas3D->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
#if !ENABLE_EXTENDED_SELECTION
canvas3D->Bind(EVT_GLCANVAS_ROTATE_OBJECT, [this](Event<int> &evt) { /*TODO: call rotate */ });
canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, [this](SimpleEvent&) { scale(); });
canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, [this](SimpleEvent&) { this->scale(); });
#endif // !ENABLE_EXTENDED_SELECTION
canvas3D->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event<int> &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); });
canvas3D->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
@ -998,7 +1004,7 @@ void Plater::priv::update(bool force_autocenter)
}
// stop_background_process(); // TODO
print.reload_model_instances();
// print.reload_model_instances();
#if !ENABLE_EXTENDED_SELECTION
const auto selections = collect_selections();
@ -1008,7 +1014,7 @@ void Plater::priv::update(bool force_autocenter)
preview->reset_gcode_preview_data();
preview->reload_print();
schedule_background_process();
this->schedule_background_process();
}
void Plater::priv::select_view(const std::string& direction)
@ -1034,7 +1040,7 @@ void Plater::priv::update_ui_from_settings()
// }
}
ProgressStatusBar* Plater::priv::statusbar()
ProgressStatusBar* Plater::priv::statusbar()
{
return main_frame->m_statusbar;
}
@ -1257,7 +1263,7 @@ std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType
case FT_GCODE:
wildcard = file_wildcards[file_type];
break;
// FT_GCODE
default:
wildcard = file_wildcards[FT_MODEL];
break;
@ -1290,7 +1296,6 @@ std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType
fs::path path(dlg->GetPath());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
export_gcode_output_file = path;
return dlg;
}
@ -1446,7 +1451,7 @@ void Plater::priv::object_list_changed()
_3DScene::enable_toolbar_item(canvas3D, "arrange", have_objects);
#endif // ENABLE_EXTENDED_SELECTION
const bool export_in_progress = !(export_gcode_output_file.empty() && send_gcode_file.empty());
const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty());
// XXX: is this right?
const bool model_fits = _3DScene::check_volumes_outside_state(canvas3D, config) == ModelInstance::PVS_Inside;
@ -1478,7 +1483,7 @@ void Plater::priv::remove(size_t obj_idx)
objects.erase(objects.begin() + obj_idx);
#endif // !ENABLE_EXTENDED_SELECTION
model.delete_object(obj_idx);
print.delete_object(obj_idx);
// print.delete_object(obj_idx);
// Delete object from Sidebar list
sidebar->obj_list()->delete_object_from_list(obj_idx);
@ -1504,7 +1509,7 @@ void Plater::priv::reset()
objects.clear();
#endif // !ENABLE_EXTENDED_SELECTION
model.clear_objects();
print.clear_objects();
// print.clear_objects();
// Delete all objects from list on c++ side
sidebar->obj_list()->delete_all_objects_from_list();
@ -1609,7 +1614,6 @@ void Plater::priv::split_object()
if (new_objects.size() == 1)
{
Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part.")));
// $self->schedule_background_process;
}
else
{
@ -1635,9 +1639,16 @@ void Plater::priv::schedule_background_process()
void Plater::priv::async_apply_config()
{
DynamicPrintConfig config = wxGetApp().preset_bundle->full_config();
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.opt<ConfigOptionPoints>("bed_shape")->values));
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(config.opt_float("max_print_height"))));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.min(2) = -1e10;
this->q->model().update_print_volume_state(print_volume);
// Apply new config to the possibly running background task.
bool was_running = this->background_process.running();
bool invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
bool invalidated = this->background_process.apply(this->q->model(), std::move(config));
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
if (Slic3r::_3DScene::is_layers_editing_enabled(this->canvas3D))
this->canvas3D->Refresh();
@ -1669,19 +1680,19 @@ void Plater::priv::async_apply_config()
void Plater::priv::start_background_process()
{
if (this->background_process.running())
return;
// return if ! @{$self->{objects}} || $self->{background_slicing_process}->running;
// Don't start process thread if config is not valid.
std::string err = wxGetApp().preset_bundle->full_config().validate();
if (err.empty())
err = this->q->print().validate();
// Don't start process thread if Print is not valid.
std::string err = this->q->print().validate();
if (! err.empty()) {
// $self->statusbar->SetStatusText(err);
return;
}
// Copy the names of active presets into the placeholder parser.
wxGetApp().preset_bundle->export_selections(this->q->print().placeholder_parser());
// Start the background process.
this->background_process.start();
this->statusbar()->set_status_text(err);
} else {
// Copy the names of active presets into the placeholder parser.
wxGetApp().preset_bundle->export_selections(this->q->print().placeholder_parser());
// Start the background process.
this->background_process.start();
}
}
void Plater::priv::stop_background_process()
@ -1795,9 +1806,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
}
void Plater::priv::on_progress_event()
void Plater::priv::on_progress_event(wxCommandEvent &evt)
{
// TODO
this->statusbar()->set_progress(evt.GetInt());
this->statusbar()->set_status_text(evt.GetString() + wxString::FromUTF8(""));
}
void Plater::priv::on_update_print_preview(wxCommandEvent &)
@ -1813,62 +1825,25 @@ void Plater::priv::on_update_print_preview(wxCommandEvent &)
#endif // !ENABLE_EXTENDED_SELECTION
}
void Plater::priv::on_process_completed(wxCommandEvent &)
void Plater::priv::on_process_completed(wxCommandEvent &evt)
{
// Stop the background task, wait until the thread goes into the "Idle" state.
// At this point of time the thread should be either finished or canceled,
// so the following call just confirms, that the produced data were consumed.
this->background_process.stop();
//$self->statusbar->ResetCancelCallback();
//$self->statusbar->StopBusy;
//$self->statusbar->SetStatusText("");
/*
my $message;
my $send_gcode = 0;
my $do_print = 0;
# print "Process completed, message: ", $message, "\n";
if (defined($result)) {
$message = L("Export failed");
} else {
# G-code file exported successfully.
if ($self->{print_file}) {
$message = L("File added to print queue");
$do_print = 1;
} elsif ($self->{send_gcode_file}) {
$message = L("Sending G-code file to the Printer Host ...");
$send_gcode = 1;
} elsif (defined $self->{export_gcode_output_file}) {
$message = L("G-code file exported to ") . $self->{export_gcode_output_file};
} else {
$message = L("Slicing complete");
}
this->statusbar()->reset_cancel_callback();
this->statusbar()->stop_busy();
bool success = evt.GetInt();
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this->background_process.reset_export();
if (! success) {
wxString message = evt.GetString();
if (message.IsEmpty())
message = _(L("Export failed"));
this->statusbar()->set_status_text(message);
}
$self->{export_gcode_output_file} = undef;
wxTheApp->notify($message);
$self->do_print if $do_print;
# Send $self->{send_gcode_file} to OctoPrint.
if ($send_gcode) {
my $host = Slic3r::PrintHost::get_print_host($self->{config});
if ($host->send_gcode($self->{send_gcode_file})) {
$message = L("Upload to host finished.");
} else {
$message = "";
}
}
*/
// As of now, the BackgroundProcessing thread posts status bar update messages to a queue on the MainFrame.pm,
// but the "Processing finished" message is posted to this window.
// Delay the following status bar update, so it will be called later than what is received by MainFrame.pm.
//wxTheApp->CallAfter(sub {
// $self->statusbar->SetStatusText($message);
// });
//$self->{print_file} = undef;
//$self->{send_gcode_file} = undef;
//$self->print_info_box_show(1);
// this updates buttons status
@ -2209,9 +2184,9 @@ void Plater::increase_instances(size_t num)
Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0);
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation());
#if ENABLE_EXTENDED_SELECTION
p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
#else
p->print.get_object(*obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
// p->print.get_object(*obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
#endif // ENABLE_EXTENDED_SELECTION
}
@ -2254,9 +2229,9 @@ void Plater::decrease_instances(size_t num)
for (size_t i = 0; i < num; i++) {
model_object->delete_last_instance();
#if ENABLE_EXTENDED_SELECTION
p->print.get_object(obj_idx)->delete_last_copy();
// p->print.get_object(obj_idx)->delete_last_copy();
#else
p->print.get_object(*obj_idx)->delete_last_copy();
// p->print.get_object(*obj_idx)->delete_last_copy();
#endif // ENABLE_EXTENDED_SELECTION
}
#if ENABLE_EXTENDED_SELECTION
@ -2281,8 +2256,7 @@ void Plater::decrease_instances(size_t num)
#endif // ENABLE_EXTENDED_SELECTION
p->selection_changed();
// $self->schedule_background_process;
this->p->schedule_background_process();
}
void Plater::set_number_of_copies(size_t num)
@ -2307,38 +2281,35 @@ void Plater::set_number_of_copies(size_t num)
decrease_instances(-diff);
}
fs::path Plater::export_gcode(const fs::path &output_path)
void Plater::export_gcode(fs::path output_path)
{
#if ENABLE_EXTENDED_SELECTION
if (p->model.objects.empty()) { return ""; }
if (p->model.objects.empty())
return;
#else
if (p->objects.empty()) { return ""; }
if (p->objects.empty())
return;
#endif // ENABLE_EXTENDED_SELECTION
if (! p->export_gcode_output_file.empty()) {
if (this->p->background_process.is_export_scheduled()) {
GUI::show_error(this, _(L("Another export job is currently running.")));
return "";
return;
}
std::string err = wxGetApp().preset_bundle->full_config().validate();
if (err.empty()) {
if (err.empty())
err = p->print.validate();
}
if (! err.empty()) {
// The config is not valid
GUI::show_error(this, _(err));
return fs::path();
return;
}
// Copy the names of active presets into the placeholder parser.
wxGetApp().preset_bundle->export_selections(p->print.placeholder_parser());
// select output file
if (! output_path.empty()) {
p->export_gcode_output_file = fs::path(p->print.output_filepath(output_path.string()));
// FIXME: ^ errors to handle?
} else {
if (output_path.empty()) {
// XXX: take output path from CLI opts? Ancient Slic3r versions used to do that...
// If possible, remove accents from accented latin characters.
@ -2355,13 +2326,17 @@ fs::path Plater::export_gcode(const fs::path &output_path)
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if (dlg.ShowModal() != wxID_OK) { return ""; }
fs::path path(dlg.GetPath());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
p->export_gcode_output_file = path;
if (dlg.ShowModal() == wxID_OK) {
fs::path path(dlg.GetPath());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
output_path = path;
}
}
return p->export_gcode_output_file;
if (! output_path.empty()) {
this->p->background_process.schedule_export(p->print.output_filepath(output_path.string()));
this->p->background_process.start();
}
}
void Plater::export_stl()
@ -2452,7 +2427,7 @@ void Plater::reslice()
void Plater::send_gcode()
{
p->send_gcode_file = export_gcode();
// p->send_gcode_file = export_gcode();
}
void Plater::on_extruders_change(int num_extruders)

View file

@ -118,7 +118,7 @@ public:
void set_number_of_copies(size_t num);
// Note: empty path means "use the default"
boost::filesystem::path export_gcode(const boost::filesystem::path &output_path = boost::filesystem::path());
void export_gcode(boost::filesystem::path output_path = boost::filesystem::path());
void export_stl();
void export_amf();
void export_3mf();

View file

@ -884,7 +884,8 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
std::string old_label = ui->GetString(ui_id).utf8_str().data();
std::string preset_name = Preset::remove_suffix_modified(old_label);
const Preset *preset = this->find_preset(preset_name, false);
assert(preset != nullptr);
// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator.
// assert(preset != nullptr);
if (preset != nullptr) {
std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name;
if (old_label != new_label)

View file

@ -53,8 +53,8 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
});
m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) {
if(m_cancel_cb) m_cancel_cb();
m_perl_cancel_callback.call();
if (m_cancel_cb)
m_cancel_cb();
m_cancelbutton->Hide();
});
}
@ -136,7 +136,17 @@ void ProgressStatusBar::embed(wxFrame *frame)
void ProgressStatusBar::set_status_text(const wxString& txt)
{
self->SetStatusText(wxString::FromUTF8(txt.c_str()));
self->SetStatusText(txt);
}
void ProgressStatusBar::set_status_text(const std::string& txt)
{
this->set_status_text(txt.c_str());
}
void ProgressStatusBar::set_status_text(const char *txt)
{
this->set_status_text(wxString::FromUTF8(txt));
}
void ProgressStatusBar::show_cancel_button()

View file

@ -4,8 +4,6 @@
#include <memory>
#include <functional>
#include "callback.hpp"
class wxTimer;
class wxGauge;
class wxButton;
@ -22,7 +20,8 @@ namespace Slic3r {
* of the Slicer main window. It consists of a message area to the left and a
* progress indication area to the right with an optional cancel button.
*/
class ProgressStatusBar {
class ProgressStatusBar
{
wxStatusBar *self; // we cheat! It should be the base class but: perl!
wxTimer *m_timer;
wxGauge *m_prog;
@ -35,25 +34,26 @@ public:
ProgressStatusBar(wxWindow *parent = nullptr, int id = -1);
~ProgressStatusBar();
int get_progress() const;
void set_progress(int);
int get_range() const;
void set_range(int = 100);
void show_progress(bool);
void start_busy(int = 100);
void stop_busy();
int get_progress() const;
void set_progress(int);
int get_range() const;
void set_range(int = 100);
void show_progress(bool);
void start_busy(int = 100);
void stop_busy();
inline bool is_busy() const { return m_busy; }
void set_cancel_callback(CancelFn = CancelFn());
inline void remove_cancel_callback() { set_cancel_callback(); }
void run(int rate);
void embed(wxFrame *frame = nullptr);
void set_status_text(const wxString& txt);
void set_cancel_callback(CancelFn = CancelFn());
inline void reset_cancel_callback() { set_cancel_callback(); }
void run(int rate);
void embed(wxFrame *frame = nullptr);
void set_status_text(const wxString& txt);
void set_status_text(const std::string& txt);
void set_status_text(const char *txt);
// Temporary methods to satisfy Perl side
void show_cancel_button();
void hide_cancel_button();
void show_cancel_button();
void hide_cancel_button();
PerlCallback m_perl_cancel_callback;
private:
bool m_busy = false;
CancelFn m_cancel_cb;

View file

@ -185,22 +185,6 @@ _constant()
}
%};
void export_gcode_with_preview_data(char *path_template, GCodePreviewData *preview_data) %code%{
try {
THIS->export_gcode(path_template, preview_data);
} catch (std::exception& e) {
croak(e.what());
}
%};
void export_gcode(char *path_template) %code%{
try {
THIS->export_gcode(path_template, nullptr);
} catch (std::exception& e) {
croak(e.what());
}
%};
void export_png(char *path) %code%{
try {
THIS->export_png(path);